In Swagger CodeGen, how do you specify a field maximum with decimal places? - openapi

We're using Java 11 with the following version of Swagger Codegen
<plugin>
<groupId>io.swagger.codegen.v3</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<version>3.0.35</version>
I have this in my OpenAPI 3 spec for a particular DTO field,
amount:
type: number
format: double
maximum: 99999999.99
multipleOf: 0.01
Upon running the plugin, the DTO is generated without the decimal places
/**
* Get amount
* maximum: 99999999
* #return amount
**/
#Schema(required = true, description = "")
#NotNull
#DecimalMax("99999999") public Double getAmount() {
return amount;
}
As a result, when I submit a value for my listed maximum, 99999999.99, I get this error
"errorMessage": "must be less than or equal to 99999999"
What's the proper way in the openAPI spec and Swagger code gen to have a maximum field with decimal places included afterwards?

Can you try this?
amount:
type: number
format: double
exclusiveMaximum: 100000000
multipleOf: 0.01

Try this based on format double (am aware that DTO is not generating decimal), add it within the swagger-codegen-maven-plugin configuration in your pom.xml:
<configuration>
<typeMappings>
<typeMapping>Double=java.math.BigDecimal</typeMapping>
</typeMappings>
</configuration>

Related

Handling of Incorrect Value for type Integer as RequestParam in REST API in Spring Boot Application

I have a problem with one of the negative scenarios of Testing an API.
I have a spring boot application with one RestController which takes in a RequestParam("id") and i return back the same id along with HttpStatus.OK.
When i hit GET API like : http://localhost:8080/Temp/getInteger?id=%23abcd
the value that gets assigned to id is 43981 which i don't understand how it got assigned.
Ideally i don't expect this String to be converted into an Integer, Can someone help me with this?
Code:
#RestController
#RequestMapping("/Temp")
public class TempController{
#RequestMapping(method=RequestMethod.GET, path= "/getInteger")
public ResponseEntity<Object> getInteger(#RequestParam("id") Integer id){
return new ResponseEntity(id,HttpStatus.OK);
}
}
Request:
http://localhost:8080/Temp/getInteger?id=%23abcd
Output :
Http Status : 200 OK
Response Body : 43981
From UTF-8 URL Encoded Characters: %23 is # and
from hexadecimal number system:
abcd is 43981
So it can be cast to INT. You can add a letter "e" and get a new number 703710. If you want to avoid it you need to add validation.

Date format changed in Payara 5 (Long does not work anymore ) -- org.eclipse.yasson.YassonProperties#ZERO_TIME_PARSE_DEFAULTING

Using Payara 4.174 and receive in backend the date as a long, for example 1540545780000. Now, I upgraded to Payara version 5.
public User {
String name;
Date birthdate;
}
#POST
#Path("/user/)
public void createUser(User user) {
...
}
json call
{
name: "name",
birthdate: 1540545780000
}
Now The call brakes with the following error:
Caused by: javax.json.bind.JsonbException: Error parsing class java.util.Date from value: 1540545780000. Check your #JsonbDateFormat has all time units for class java.util.Date type, or consider using org.eclipse.yasson.YassonProperties#ZERO_TIME_PARSE_DEFAULTING.
at org.eclipse.yasson.internal.serializer.AbstractDateTimeDeserializer.deserialize(AbstractDateTimeDeserializer.java:70)
at org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserializeInternal(AbstractContainerDeserializer.java:85)
at org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserialize(AbstractContainerDeserializer.java:61)
at org.eclipse.yasson.internal.Unmarshaller.deserializeItem(Unmarshaller.java:62)
at org.eclipse.yasson.internal.Unmarshaller.deserialize(Unmarshaller.java:52)
at org.eclipse.yasson.internal.JsonBinding.deserialize(JsonBinding.java:45)
at org.eclipse.yasson.internal.JsonBinding.fromJson(JsonBinding.java:85)
at org.glassfish.jersey.jsonb.internal.JsonBindingProvider.readFrom(JsonBindingProvider.java:99)
... 62 common frames omitted
Caused by: java.time.format.DateTimeParseException: Text '1540545780000' could not be parsed at index 0
Jonathan is partly right. You don't have to write a custom DateTypeDeserializer to solve your issue. However, as the error message describes, you can annotate your Field with #JsonbDateFormat to resolve your error, you have to make sure it has all time units.
Here is an example:
public User {
String name;
#JsonbDateFormat(value = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
Date birthdate;
}
This is because Payara 5 switched to using Yasson as its JsonB provider, which does not contain a mapper for long to java.util.Date (see here). You have to write your own version to map a number to Date format.

kotlin data class + bean validation jsr 303

I'm trying to get Kotlin working with jsr 303 validation on a spring-data-rest project.
Given the following data class declarartion :
#Entity data class User(
#Id
#GeneratedValue(strategy = javax.persistence.GenerationType.AUTO)
var id: Long? = null,
#Size(min=5, max=15)
val name: String
)
The #Size annotation has no effect here, making me able to save a user with a name of 1 character.
It works well when executing the very same example but in a Java class instead of Kotlin.
This makes me think of a Kotlin problem.
Thanks in advance for you help !
You need to use Annotation use-site targets since the default for a property declared in the constructor is to target the annotation on the constructor parameter instead of the getter (which will be seen by JavaBeans compliant hosts) when there are multiple options available. Also using a data class might be inappropriate here (see note at end).
#Entity data class User(
#Id
#GeneratedValue(strategy = javax.persistence.GenerationType.AUTO)
var id: Long? = null,
#get:Size(min=5, max=15) // added annotation use-site target here
val name: String
)
The property target from the Kotlin docs may look tempting, but it can only be seen from Kotlin and not Java. Usually get does the trick, and it is not needed on the bean set.
The docs describe the process as:
If you don’t specify a use-site target, the target is chosen according to the #Target annotation of the annotation being used. If there are multiple applicable targets, the first applicable target from the following list is used:
param
property
field
And the #Size annotation is:
#Target(value={METHOD,FIELD,ANNOTATION_TYPE,CONSTRUCTOR,PARAMETER})
Therefore since PARAMETER is a valid target, and multiple targets are available (parameter, field, method [get/set]) it choses PARAMETER which is not what you want. Therefore for a JavaBean host to see the property it will look for the getter (properties are defined by the getter/setter and not the backing field).
In one of the Java samples, it shows:
public class Book {
private String title;
private String description;
// ...
#NotEmpty(groups={FirstLevelCheck.class, Default.class})
#Size(max=30)
public String getTitle() {
return title;
}
// ...
}
Which matches our usage of having it on the getter. If it were to be on the field like some of the validation annotations show, see the field use-site target. Or if the field must also be publicly accessible, see the #JvmField annotation in Kotlin.
NOTE: As mentioned in notes from others, you should likely consider NOT using a data class for entities if they use an auto-generated ID since it will not exist for new objects the same as for retrieved objects; and a data class will generate equals and hashCode to include all fields including the ones it should not. You can read guidance about this from the Hibernate docs.
Use the #get or #field targets for validation annotations. Annotations with the target #param(first default) and #property are not supported.
e.g:
From #NotEmpty To #field:NotEmpty
data class Student(
#field:NotEmpty #field:Size(min= 2, message = "Invalid field") var name: String? = ""
)
GL
Jayson Minard
Annotation use site targets

INET Nordic FIX protocols extending to nanosecond granularity timestamps

All INET Nordic FIX protocols will be enhanced by extending to nanosecond granularity timestamps on 16.oktober 2015 (see notification and section 3.1.1 in the spec).
The timestamps will look like this: 20150924-10:35:20.840117690
quickfix currently rejects messages that contain fields with this new format with the error: Incorrect data format for value
Are there any plans to support this new format? Or maybe some workaround?
You can first try modifying your data dictionary. For example if you are using fix42.xml that comes with QuickFIX, you can change the affected timestamp fields from type='UTCTIMESTAMP' to type='STRING'.
If that isn't enough, you should instead write a patch against QuickFIX in C++, which should be somewhat straightforward once you know where to patch it, which I think is UtcTimeStampConvertor, around here: https://github.com/quickfix/quickfix/blob/master/src/C%2B%2B/FieldConvertors.h#L564
I think you need to add a case 27: above case 21: near the top, because your format has six extra digits. It looks like the rest of the function doesn't care about the total field length.
Of course if you want to actually inspect the sub-millisecond precision part of these timestamps, you'll need to do more.
No plans in QF/n, but only because this is the first I've heard of this.
I'll need to write some tests to see what the repercussions are. It may be that the time/date parser just truncates the extra nano places when it converts the string to a DateTime.
I've opened an issue: https://github.com/connamara/quickfixn/issues/352
This change is as far as I know kind of breaking the fix protocol definition of timestamps but that's another story.
There is a static class in QuickFixn called DateTimeConverter under QuickFix/Fields/Converters.
To get this to work correctly you would need to add format strings in lines in that class.
Add "yyyyMMdd-HH:mm:ss.fffffff" to DATE_TIME_FORMATS and "HH:mm:ss.fffffff" to TIME_ONLY_FORMATS so that it would look like this.
/// <summary>
/// Convert DateTime to/from String
/// </summary>
public static class DateTimeConverter
{
public const string DATE_TIME_FORMAT_WITH_MILLISECONDS = "{0:yyyyMMdd-HH:mm:ss.fff}";
public const string DATE_TIME_FORMAT_WITHOUT_MILLISECONDS = "{0:yyyyMMdd-HH:mm:ss}";
public const string DATE_ONLY_FORMAT = "{0:yyyyMMdd}";
public const string TIME_ONLY_FORMAT_WITH_MILLISECONDS = "{0:HH:mm:ss.fff}";
public const string TIME_ONLY_FORMAT_WITHOUT_MILLISECONDS = "{0:HH:mm:ss}";
public static string[] DATE_TIME_FORMATS = { "yyyyMMdd-HH:mm:ss.fffffff", "yyyyMMdd-HH:mm:ss.fff", "yyyyMMdd-HH:mm:ss" };
public static string[] DATE_ONLY_FORMATS = { "yyyyMMdd" };
public static string[] TIME_ONLY_FORMATS = { "HH:mm:ss.fffffff", "HH:mm:ss.fff", "HH:mm:ss" };
public static DateTimeStyles DATE_TIME_STYLES = DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal;
public static CultureInfo DATE_TIME_CULTURE_INFO = CultureInfo.InvariantCulture;

Swagger Model Schema Response: alternate label for LocalDate in SpringFox

We are using Swagger 2.x and SpringFox 2.0 to document our REST service created with Spring MVC.
We have a REST response with a property List<LocalDate> dates.
In the Model Schema of the response, the label for dates is shown as 'LocalDate'. That is not intended: we would like to have 'date' or 'yyyy-MM-dd' instead.
We have this class:
public class SayHelloResponse {
private List<LocalDate> dates;
private String message;
public SayHelloResponse(String message, LocalDate... dates) {
this.message = message;
this.dates = ImmutableList.copyOf(dates);
}
public List<LocalDate> getDates() {
return dates;
}
public String getMessage() {
return message;
}
}
That results in this Model Schema:
{
"dates": [
"LocalDate"
],
"message": "string"
}
In the Model Schema, I would like to have LocalDate as 'date' or 'yyyy-MM-dd'. The way to do this seems to be with com.wordnik.swagger.annotations.ApiModelProperty but this does not have any effect (it is being picked up, as when I add #ApiModelProperty(hidden=true) it is hidden).
I created a sample rest project that shows the issue.
Any ideas how I can change LocalDate to 'date' or 'yyyy-MM-dd' in the Model Schema of Swagger?
There is a method in Docket object to replace models called directModelSubstitute(). You can use it like this to substitute LocalDate to Date object:
Docket#directModelSubstitute(LocalDate.class, Date.class)
The only problem with it that I found is that you can't change the date format.
See A/Q section in the official Springfox documentation, specifically question "How do we use Java 8 types esply. LocalDateTime?"
This is recommended in the official Springfox documentation, but doesn't effect:
Docket(DocumentationType.SWAGGER_2)..build().directModelSubstitute(LocalDate.class, java.sql.Date.class)
This effect but change format to date-time instead of date:
Docket(DocumentationType.SWAGGER_2)..build().directModelSubstitute(LocalDate.class, java.util.Date.class);
That's why I use the last one and ignore time part.