How can I use the useBeanValidation option when generating code in Swagger? - swagger

Swagger's code generation for a Spring server has an option called useBeanValidation, but I can't figure out how to use it. I couldn't find any documentation telling me which validations it supports, so I decided to try it out myself. The OpenAPI spec's description of the properties of a schema object lists these properties:
title
multipleOf
maximum
exclusiveMaximum
minimum
exclusiveMinimum
maxLength
minLength
pattern
maxItems
minItems
uniqueItems
maxProperties
minProperties
required
enum
So I tried adding some of these properties to fields of an Object I created. Here's the relevant portion of my .yaml file:
components:
schemas:
Dummy:
type: object
properties:
iMinMax:
type: integer
format: int32
minimum: 0
maximum: 100
dMinMaxEx:
type: number
format: int32
minimum: 5.0
maximum: 10.0
exclusiveMinimum: false
exclusiveMaximum: true
dMinExMaxEx:
type: number
format: int32
minimum: 5.0
maximum: 10.0
exclusiveMinimum: true
exclusiveMaximum: true
dMinExMax:
type: number
format: int32
minimum: 5.0
maximum: 10.0
exclusiveMinimum: true
exclusiveMaximum: false
sArray:
type: array
items:
type: string
minItems: 5
maxItems: 10
uniqueItems: true
sLen:
type: string
format: text
minLength: 5
maxLength: 10
I turned on the bean validation option of the Spring code generator and generated server code, but it didn't have any effect. The code it generated was exactly the same as with the option turned off. Does anyone know how to use Swagger's Bean Validation option?

There are 2 properties that influence bean validation within the latest version of the generator (3.3.4 last I checked). These properties are performBeanValidation and useBeanValidation (both false by default). To understand how they work you should look at the mustache templates that the generator uses in conjunction with the generator properties. These can be found in the JavaSpring Mustache files.
For example, you will see different behavior with and without performBeanValidation if your API yaml contains an attribute defined with format: email. With performBeanValidation=true the generator outputs an #Email validation annotation. With performBeanValidation=false you will not see this annotation. This can be understood by looking at the following mustache file: beanValidationCore
You can override any of these Mustache templates by copying the original Mustache file from the source location to your own project location and modify it as required. You then supply the project location as a parameter or property to the generator. e.g templateDirectory=src/main/resources/mustache
Blockquote
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>${openapi-codegen-version}</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>api.yaml</inputSpec>
<output>target/generated-sources</output>
<apiPackage>my.package.api</apiPackage>
<modelPackage>my.package.api.model</modelPackage>
<generatorName>spring</generatorName>
<templateDirectory>src/main/resources/mustache</templateDirectory>
<!--<configHelp>true</configHelp>-->
<!--<verbose>true</verbose>-->
<configOptions>
<dateLibrary>java8-localdatetime</dateLibrary>
<java8>false</java8>
<interfaceOnly>true</interfaceOnly>
<performBeanValidation>true</performBeanValidation>
<useBeanValidation>true</useBeanValidation>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>

Related

Multiple examples for object properties swagger

I am trying to add multiple examples for an Object property.
The Swagger-Ui and Editor version that I am using are
'{"swaggerEditor":"3.6.31/g10642b3c-dirty","swaggerUi":{"version":"3.23.0","gitRevision":"g23d7260f","gitDirty":true,"buildTimestamp":"Sat, 29 Jun 2019 19:42:59 GMT","machine":"jenins-swagger-oss"}}'
Based on OpenAPI doc, this version of swagger UI and editor have support for multiple examples but I still see this error:
Structural error at components.schemas.MainObject.allOf.3.properties.partitionProperty
should NOT have additional properties
additionalProperty: examples
Jump to line 3016
This is how I have added the examples in the property:
MainObject:
allOf:
- $ref: '#/components/schemas/MainObjectLite'
- type: object
description: foobar.
readOnly: true
required:
- fooRequired
properties:
fooRequired:
type: string
description: system only field used for table data indexing
partitionProperty:
type: string
description: foobar
examples:
sampleExample:
value: 2016-03-04T03:00:00
summary: sample partition
OpenAPI 3.0
Multiple examples are only supported at the media type level and are not supported inside schemas. Schemas and properties can only have a single example, e.g.
partitionProperty:
type: string
example: '2016-03-04T03:00:00'
In other words, this won't work:
MainObject:
type: object
properties:
..
examples: # <--- Won't work
minimal:
summary: Minimal example
value:
foo: bar
full:
summary: Example with all properties
value:
foo: bar
baz: xyzzy
If you want multiple examples, you need to use request examples or response examples instead:
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/MainObject'
examples:
minimal:
summary: Minimal example
value:
foo: bar
full:
summary: Example with all properties
value:
foo: bar
baz: xyzzy
OpenAPI 3.1
OAS 3.1 uses a newer version of JSON Schema which supports multiple examples in schemas and properties. Unlike media type examples which is a map of named Example Objects, schema-level and property-level examples is a plain list of example values.
MyObject:
type: object
properties:
prop1:
type: string
# Property-level examples
examples:
- foo
- bar
prop2:
type: integer
# Object-level examples
examples:
- prop1: hello
prop2: 5
- prop1: world
prop2: 0

OpenApi: generate java/kotlin DTOs out of multiple files

I have a problem with the imports of the generated code of the openapi-generator for Java and Kotlin.
Let's say I have a root.yaml/child1.yaml/child2.yaml with the following content:
components:
schemas:
Transfer:
type: object
allOf:
- $ref: "child1.yaml#/components/schemas/Pet1"
- $ref: "child2.yaml#/components/schemas/Pet2"
child1.yaml:
components:
schemas:
Pet1:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
child2.yaml:
components:
schemas:
Pet2:
type: object
required:
- id2
- name2
properties:
id2:
type: integer
format: int64
name2:
type: string
tag2:
type: string
In this case, I do not care for the entities in child1/child2 and I only want the Transfer-object to be build, so I fill the the modelsToGenerate-setting with Transfer only. My problem is that the generated Transfer class always contains imports for the childs, even when they are not needed. For example:
import com.model.Pet1
import com.model.Pet2
data class Transfer (
val id: kotlin.Long,
val name: kotlin.String,
val id2: kotlin.Long,
val name2: kotlin.String,
val tag: kotlin.String? = null,
val tag2: kotlin.String? = null
)
The generated class is not dependend on the childs, but the imports are always generated. Is there a setting or a workaround that I missed? The unnecessary imports also appear when Pet1 and Pet2 are generated, but Transfer is still not dependend on the childs.
My use case is that I do have some very large models in the spec and I would like to split them into multiple files to reduce clutter/duplication without a public class for every single child.
Thanks in advance.
Found the answer myself. You have to run a formatter like Google Java formatter after the classes are generated.

How to use AWS CDK FindInMap to set property of number type?

I'm building a CloudFormation template with properties which should be set with different value according to stage (e.g., beta, prod). The property's type is number. However, the Fn.FindInMap returns string token. So how to use FindInMap to set such property of number type?
This is for AWS CDK Java language APIs.
This is code section of Mapping:
Mapping writeCapacityMapping = new Mapping(parent, "TableWriteCapacityMapping", MappingProps.builder()
.withMapping(ImmutableMap.of(
Stage.beta.name(), ImmutableMap.of(
"min", 5,
"max", 100),
Stage.prod.name(), ImmutableMap.of(
"min", 400,
"max", 1200)))
.build());
This is code section trying to get value from mapping by stage parameter and assign to properties (minCapacity and maxCapacity) which requires Number type.
String minCapacity = Fn.findInMap("TableWriteCapacityMapping", stageParameter.getValueAsString(), "min");
String maxCapacity = Fn.findInMap("TableWriteCapacityMapping", stageParameter.getValueAsString(), "max");
ScalableTargetProps props = ScalableTargetProps.builder()
.withMinCapacity(Integer.valueOf(minCapacity))
.withMaxCapacity(Integer.valueOf(maxCapacity))
.build();
The expected the CloudFormation template is:
Mappings:
WriteCapacityMap:
beta:
min: 5
max: 100
prod:
min: 400
max: 1200
TableWriteCapacityScalableTarget:
Type: "AWS::ApplicationAutoScaling::ScalableTarget"
Properties:
MinCapacity:
Fn::FindInMap: [WriteCapacityMap, { Ref: "Stage" }, min]
MaxCapacity:
Fn::FindInMap: [WriteCapacityMap, { Ref: "Stage" }, max]
However, the following exception will be thrown because minCapacity or maxCapacity are token string: Exception in thread "main" java.lang.NumberFormatException: For input string: "${Token[TOKEN.14]}"
In case it is useful for you, I'd like to share my solution - using AWS CloudFormation layer library.
As mentioned in AWS CDK docs [1], under the hood, CDK constructs are implemented using AWS CloudFormation libraries which are available in the CfnXxx classes in each library. For advanced use cases and gaps between CDK and CloudFormation, it might be required to use these CloudFormation libraries.
For example:
AWS CDK construct: The minCapacity property in software.amazon.awscdk.services.applicationautoscaling.ScalableTarget accepts parameter of Number type.
void setMinCapacity(final java.lang.Number value);
AWS CloudFormation library: The minCapacity property in software.amazon.awscdk.services.applicationautoscaling.CfnScalableTargetProps
CfnScalableTargetProps can be set with Number or Token.
void setMinCapacity(final java.lang.Number value);
void setMinCapacity(final software.amazon.awscdk.Token value);
[1] https://docs.aws.amazon.com/CDK/latest/userguide/cloudformation.html

Default string size for Postgresql and Grails 3.3.8 seems to be 20 characters

My string columns are defaulting to 20 characters. All the documentation suggests the default is 255. How do I set that without changing each individual column. I am using Grails 3.3.8 and Postgresql 9.3.
class DbConnection {
String name
String platform
creates 20 character columns
If I add a mapping:
static mapping = {
name sqlType: 'varchar(255)'
platform sqlType: 'varchar(255)'
url sqlType: 'varchar(255)'
}
I get the proper 255 characters, however, grails fails the string on validation:
Field error in object 'dop_etc.DbConnection' on field 'url': rejected value [localhost:5432/rbc48_fantasy]; codes [dop_etc.DbConnection.url.size.error.dop_etc.DbConnection.url,dop_etc.DbConnection.url.size.error.url,dop_etc.DbConnection.url.size.error.java.lang.String...
There seems to be a default size set somewhere and I can't seem to find it. Thanks for your responses.
Apologies, found the problem in some pasted code (in application.groovy)
grails.gorm.default.constraints = {
'*'(nullable: true, size: 1..255)
}
Here's the helpful article:
Overriding default `maxSize` in domain class

Unable to get JIBX IBindingFactory based on binding file

I use Eclipse with Maven (m2eclipse plugin) and JIBX to (un)marshall XML.
It works if I use the factory like this:
IBindingFactory bindingFactory = BindingDirectory.getFactory(mappedClass);
However I want to create a factory based on the binding file, because this is done by the automatically generated service stub. So I run the following test in TestNG:
#Test
public void testBindingFactory() {
try
{
IBindingFactory factory = BindingDirectory.getFactory("binding", "");
} catch (JiBXException e)
{
e.printStackTrace();
fail(e.getMessage());
}
}
and it fails with the following error message:
Unable to access binding 'binding'
Make sure classes generated by the binding compiler are available at runtime
java.lang.ClassNotFoundException: .JiBX_bindingFactory
The name (filename is binding.xml) is correct, the empty "" means no package, which is also correct, but could be a problem I guess?
The factory is generated under target/classes/JiBX_bindingFactory.class in my project folder, so it should be found! (Remember everything works if I specify a concrete toplevel binded class)
Any help would be appreciated!
The build section in my pom.xml file:
<build>
<plugins>
<plugin>
<groupId>org.jibx</groupId>
<artifactId>jibx-maven-plugin</artifactId>
<version>1.2.5</version>
<configuration>
<schemaBindingDirectory>src/main/config</schemaBindingDirectory>
<includeSchemaBindings>
<includeSchemaBinding>binding.xml</includeSchemaBinding>
</includeSchemaBindings>
<verbose>true</verbose>
</configuration>
<executions>
<execution>
<goals>
<!-- Do we require other goals? http://jibx.sourceforge.net/maven-jibx-plugin/ -->
<goal>bind</goal>
<!-- goal>test-bind</goal-->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Found the solution, its a bug in the implemention of getFactory(java.lang.String bname, java.lang.String pack, java.lang.ClassLoader loader)!
Consider the first lines of code:
public static IBindingFactory [More ...] getFactory(String bname, String pack,
ClassLoader loader) throws JiBXException {
String cname = (pack == null ? "" : pack + '.') +
GENERATE_PREFIX + convertName(bname) + BINDINGFACTORY_SUFFIX;
...
Remember that the auto-generated service stub called the getFactory method like this: factory = org.jibx.runtime.BindingDirectory.getFactory("binding", "",
ISService_1_0_deStub.class.getClassLoader());
So the empty quotes are replaced with ".", which leads to the Exception: java.lang.ClassNotFoundException: .JiBX_bindingFactory as said (note the dot before the class name!).
The solution is to call the method like this: factory = org.jibx.runtime.BindingDirectory.getFactory("binding", (String)null,
ISService_1_0_deStub.class.getClassLoader());
Note the cast to String, otherwise the call is ambiguous!
Sidenote: The method getFactory(String, String) calls the method getFactory(String, String, ClassLoader) in BindingDirectory

Resources