When using swagger 2.0, the java.util.Currency class got generated as a seperate definition. But when we generate OpenAPI 3.0, we run into the issue that swagger-core generates it as a property.
Generation of the OpenAPI 3.0 specification from the classes
We have f.e. this class:
import java.util.Currency;
public class Wrapper {
private Currency currency;
}
From this code we generate the openapi specification with the following plugin configuration:
<plugin>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-maven-plugin</artifactId>
<version>2.0.9</version>
<configuration>
<outputFileName>openapi</outputFileName>
<outputFormat>YAML</outputFormat>
<outputPath>....</outputPath>
<resourcePackages>...</resourcePackages>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>resolve</goal>
</goals>
</execution>
</executions>
</plugin>
When generated this will result in this component definition:
components:
schemas:
Wrapper:
type: "object"
properties:
currency:
type: object
properties:
currencyCode:
type: string
defaultFractionDigits:
type: integer
format: int32
numericCode:
type: integer
format: int32
displayName:
type: string
symbol:
type: string
Generation of the classes from the OpenAPI 3.0 specification
We then then generate the classes in another project with the following plugin:
The code to generate the classes form this openapi specification:
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>openapi-generation</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/swagger.yaml</inputSpec>
<language>java</language>
<library>jersey2</library>
<generateSupportingFiles>false</generateSupportingFiles>
<configOptions>
<booleanGetterPrefix>is</booleanGetterPrefix>
<dateLibrary>threetenbp</dateLibrary>
<import-mappings>
Currency=java.util.Currency
</import-mappings>
</configOptions>
<generateApis>false</generateApis>
<generateApiDocumentation>false</generateApiDocumentation>
<generateModelTests>false</generateModelTests>
<generateModelDocumentation>false</generateModelDocumentation>
</configuration>
</execution>
</executions>
</plugin>
This will result in a class named WrapperCurrency. The --import-mappings option does not seem to work since it's a property and not a schema. This would work if the Currency would be generated as a seperate Schema definition.
Desired result
Is there any way to either, annotate the property in such a way that the java.util.Currency will be generated as a schema? something along the lines of:
components:
schemas:
Wrapper:
type: "object"
properties:
currency:
$ref: "components/schemas/Currency"
Currency:
type: object
properties:
currencyCode:
type: string
defaultFractionDigits:
type: integer
format: int32
numericCode:
type: integer
format: int32
displayName:
type: string
symbol:
type: string
Or is there a way to bind the --import-mappings option to a property instead of the object?
Well, you may consider a workaround: adding an annotation #Schema(ref = "Currency") to the currency field will make the plugin skip generating properties. Unfortunately, it won't generate the schema definition for the Currency class either:
components:
schemas:
Wrapper:
type: object
properties:
currency:
$ref: '#/components/schemas/Currency'
I am not sure if it is an issue for you, as you are mapping it back to java.util.Currency anyways. But if it is, there is another hack: you can provide a static file with a partial schema (just the Currency definition) and configure the plugin to merge it with the generated schema (openapiFilePath parameter).
Plugin configuration:
<plugin>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-maven-plugin</artifactId>
<version>2.0.9</version>
<configuration>
...
<openapiFilePath>src/main/resources/currency.yaml</openapiFilePath>
...
</configuration>
...
</plugin>
currency.yaml:
components:
schemas:
Currency:
type: object
properties:
currencyCode:
type: string
defaultFractionDigits:
type: integer
format: int32
numericCode:
type: integer
format: int32
displayName:
type: string
symbol:
type: string
It's a hack, I know, but if there are no other options...
Related
I want to generate a Java API using OpenAPI 3.0, but I don't want any return type to be a ResponseEntity.
I have a small .yaml file that generates APIs that look like this (I've removed lots of annotations for brevity):
#GetMapping(value = "/student", produces = { "application/json" })
ResponseEntity<List<Student>> getAllStudents();
It's very straightforward, but my employer wants an API like this:
#GetMapping(value = "/student")
List<Student> getAllStudents();
The trouble is that I can't find a configuration option that would make the code generator dispense with the ResponseEntities.
Is there any way to configure the OpenAPI generator to not use ResponseEntities?
—
Details:
I build with Maven, and I configure my code generation in my pom file like this:
<build>
<plugins>
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<!-- RELEASE_VERSION -->
<version>5.0.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<!--See https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator-maven-plugin-->
<inputSpec>${project.basedir}/OpenApi.yaml</inputSpec>
<generatorName>spring</generatorName>
<groupId>com.dummy.example</groupId>
<artifactId>dummy</artifactId>
<artifactVersion>2.0</artifactVersion>
<library>spring-boot</library>
<packageName>com.neptunedreams.configuration</packageName>
<apiPackage>com.neptunedreams.api</apiPackage>
<invokerPackage>com.neptunedreams</invokerPackage>
<modelPackage>com.neptunedreams.model</modelPackage>
<configOptions>
<!--See https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators/spring.md -->
<sourceFolder>src/main/java</sourceFolder>
<bigDecimalAsString>true</bigDecimalAsString>
<dateLibrary>java8</dateLibrary>
<interfaceOnly>true</interfaceOnly>
<library>spring-boot</library>
<skipDefaultInterface>true</skipDefaultInterface>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
My yaml file is very basic, if you need to see it:
openapi: 3.0.0
info:
description: StackOverflow ResponseEntity Question
version: 1.0.0
title: ResponseEntity Question
paths:
/student:
get:
summary: Get all students
operationId: getAllStudents
responses:
200:
description: Get all students
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Student'
components:
schemas:
Student:
type: object
properties:
name:
type: string
id:
type: integer
format: int64
required:
- name
- id
Like here How to prevent Mono Request body generation when generating the API interfaces for spring-webflux with open api code generator?
You can override/customize the api.mustache from swagger-codegen template.
Here you can find the original template https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator/src/main/resources/JavaSpring/api.mustache
So you can put your own template inside src/main/java/resource.
openApiGenerate {
generatorName = "spring"
library = "spring-boot"
...
configOptions = [
...
templateDirectory: "src/main/resources/openapi-templates"
...
]
}
Here is the documentation about template https://github.com/OpenAPITools/openapi-generator/blob/master/docs/templating.md
I have openapi contract:
openapi: 3.0.1
info:
version: 1.0.0
paths:
/getInteractions:
post:
requestBody:
content:
application/json:
schema:
$ref: scheme/interactionsRq.json
required: true
responses:
"200":
content:
application/json:
schema:
$ref: scheme/mainRs.json
in this structure:
-resources
--GetInteractionController.yaml
--scheme
----interactionsRq.json
----interactionsRs.json
----mainRs.json
In mainRs.json i have some ref to another json like this:
"resultApi": {
"title": "result",
"type": "object",
"properties": {
"interactionList": {
"type": "array",
"minItems": 0,
"maxItems": 100,
"items": {
"$ref": "interactionsRs.json#/definitions/interactionApi"
}
}
}
And when i try to package this with openapi-generator-maven-plugin:
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>5.3.1</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/GetInteractionController.yaml</inputSpec>
<generatorName>spring</generatorName>
<apiPackage>some.api.package</apiPackage>
<modelPackage>some.dto.package</modelPackage>
<supportingFilesToGenerate>ApiUtil.java</supportingFilesToGenerate>
<configOptions>
<delegatePattern>true</delegatePattern>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
I get a warning and build error:
[WARNING] Failed to get the schema name: ./scheme/interactionsRs.json#/definitions/interactionApi
Can openapi generate code with refs like i have? Or i need to refactor json schemas and delete this refs? Maybe concatenate this in one file or something like this
Now i understand, that this is an issue with my JSON scheme, but not with openapi-generator.
In JSON scheme i've used an jsonschema2pojo feature "customDateTimePattern" : "yyyy-MM-dd'T'HH:mm:ssZ" and it gives me an error.
When i deleted it everything become fine
I'm trying to generate a service that has enum as query parameter, but it keeps generating it wrong.
Here's the part of yaml:
name: language
in: query
description: language
schema:
type: string
enum:
- en
- de
and I'm using it in parameters:
- $ref: '#/components/parameters/language'
The error that I get is:
[ERROR] .../api/TestApi.java:[110,65] illegal start of type
[ERROR] .../api/TestApi.java:[110,66] ')' expected
[ERROR] .../api/TestApi.java:[110,82] ';' expected
Here's what the code looks like:
public Response getByLanguage( #PathParam("id") Long id, , allowableValues="en, de" #QueryParam("language") String language
And here's my plugin:
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>5.0.1</version>
<configuration>
<inputSpec>${basedir}/src/main/resources/openapi.yaml</inputSpec>
<output>${project.build.directory}/generated-sources/java</output>
<generatorName>jaxrs-resteasy-eap</generatorName>
<modelPackage>com.openapi.example.model</modelPackage>
<apiPackage>com.openapi.example.api</apiPackage>
<generateSupportingFiles>false</generateSupportingFiles>
<configOptions>
<sourceFolder>openapi</sourceFolder>
<dateLibrary>java8</dateLibrary>
<interfaceOnly>true</interfaceOnly>
<java8>true</java8>
<serializableModel>true</serializableModel>
<useTags>true</useTags>
<performBeanValidation>true</performBeanValidation>
<useBeanValidation>true</useBeanValidation>
</configOptions>
</configuration>
<executions>
<execution>
<phase>generate-resources</phase>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
The only way I've managed to get it to work is to make it as an array of enum values, but that's not what I need here.
EDIT:
dependencies I have defined in the project:
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-dependency-analyzer</artifactId>
<version>1.11.1</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jaxrs</artifactId>
<version>1.5.8</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.1.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.11.3</version>
</dependency>
Multiple long standing enum bugs exist in the generators (for all languages as far as I can tell).
For example https://github.com/OpenAPITools/openapi-generator/issues/4300
But also .. https://github.com/OpenAPITools/openapi-generator/issues/2645
Try out with below demo yaml.
Enums should be defined as enum: [en, de].
openapi: 3.0.2
info: {title: Demo Info., description: Demo Description., version: 1.0.0}
servers:
- {url: 'https://something.com', description: Demo Description.}
paths:
/something:
post:
tags: [Something]
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/SomethingRequest'}
parameters:
- {$ref: '#/components/parameters/language'}
responses:
200:
description: OK
content:
application/json:
schema: {$ref: '#/components/schemas/SomethingResponse'}
components:
parameters:
language:
name: language
schema:
type: string
enum: [en, de]
default: en
in: query
schemas:
SomethingRequest:
properties:
demo: {type: string}
SomethingResponse:
properties:
demo: {type: string}
It depends what generator you are using but jaxrs-resteasy-eap is broken when using enums in query parameters.
I think this commit is causing the problem.
As a temporary fix in my project I edited the queryParams.mustache template file to as this:
{{#isQueryParam}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{^isContainer}}{{#allowableValues}}#ApiParam(value = "{{{description}}}"{{#required}},required=true{{/required}}, {{> allowableValues }}){{/allowableValues}}{{#defaultValue}} #DefaultValue("{{{.}}}"){{/defaultValue}}{{/isContainer}} #QueryParam("{{baseName}}") {{{dataType}}} {{paramName}}{{/isQueryParam}}
Just noticed that there are also github issue of this. The recommendation of removing the allowableValues from queryParams.mustache might be the correct way to go:
{{#isQueryParam}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{^isContainer}}{{#defaultValue}} #DefaultValue("{{{.}}}"){{/defaultValue}}{{/isContainer}} #QueryParam("{{baseName}}") {{{dataType}}} {{paramName}}{{/isQueryParam}}
It depends how you are using the generator how to use own templates.
I am using it from npm #openapitools/openapi-generator-cli
There you can apply your own templates by adding -t/--template-dir option into your generate command. Here is mine as example:
npx #openapitools/openapi-generator-cli generate -t=templates/jaxrs -i openapi.yaml -o generated/jaxrs -g jaxrs-resteasy-eap -c config-jaxrs.yaml
I just copied my modified queryParams.mustache to root of templates/jaxrs and the generated api functions did not have double comma syntax problems anymore:
public Response getPets( #ApiParam(value = "My param description", allowableValues="AA, BB, CC") #QueryParam("petType") PetType petType,#Context SecurityContext securityContext);
I haven't used these files yet, so I don't know if everything is correct, but atleast the syntax is correct now.
More info here: templating documentation
I am using the openapi-generator-maven-plugin
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>4.1.3</version>
with the <withXml>true</withXml> option.
In my yaml service definition file to describe my REST actions (XML messages). I have a schema like this :
components:
schemas:
LoginRequest:
type: object
properties:
customerName:
type: string
xml:
name: customerName
attribute: false
wrapped: false
password:
type: string
xml:
name: hello
attribute: false
user:
type: string
xml:
name: user
attribute: false
wrapped: false
title: request
xml:
name: request
attribute: false
and the defined service :
paths:
/session/login:
post:
tags:
- sample-controller
summary: login
operationId: loginUsingPOST
requestBody:
content:
application/xml:
schema:
$ref: "#/components/schemas/LoginRequest"
description: request
required: true
responses:
"200":
description: OK
content:
application/xml:
schema:
$ref: "#/components/schemas/LoginResponse"
And I generate the client code. But when I use it, the XML sent to the http request uses <LoginRequest> has a tag name instead of <request>.
It seems that none of my -xml information are taken in account by the generator.
You will need to put the option inside configOptions, for example
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<!-- RELEASE_VERSION -->
<version>4.2.0</version>
<!-- /RELEASE_VERSION -->
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/api.yaml</inputSpec>
<generatorName>java</generatorName>
<configOptions>
<sourceFolder>src/gen/java/main</sourceFolder>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
Ref: https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator-maven-plugin#usage
Given the following Avro Schema:
{
"namespace": "ro.dspr.coreentities",
"type": "record",
"name": "Organization",
"fields": [
{
"name": "id",
"type": "string",
"logicalType": "uuid"
},
{
"name": "name",
"type": "string"
},
{
"name": "description",
"type": "string"
}
]
}
Running avro-maven-plugin 1.9.0 goal schema, I get:
#org.apache.avro.specific.AvroGenerated
public class Organization extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord {
public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"Organization\",\"namespace\":\"ro.dspr.coreentities\",\"fields\":[{\"name\":\"id\",\"type\":{\"type\":\"string\",\"avro.java.string\":\"String\"},\"logicalType\":\"uuid\"},{\"name\":\"name\",\"type\":{\"type\":\"string\",\"avro.java.string\":\"String\"}},{\"name\":\"description\",\"type\":{\"type\":\"string\",\"avro.java.string\":\"String\"}}]}");
// truncated
#Deprecated public java.lang.String id;
#Deprecated public java.lang.String name;
#Deprecated public java.lang.String description;
// truncated
}
I want the generated POJO for Organization to have id UUID, not String (what I have now).
Links I looked at:
I do see the logical type def from Avro and there is the Conversion class I am actually trying to trigger, but I cannot connect the dots.
Other
Relevant Maven pom parts
<plugin>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
<version>${avro.version}</version>
<configuration>
<sourceDirectory>${avro-files-path}</sourceDirectory>
<outputDirectory>${project.build.directory}/generated-sources</outputDirectory>
<stringType>String</stringType>
</configuration>
<executions>
<execution>
<goals>
<goal>schema</goal>
</goals>
<configuration>
<sourceDirectory>${avro-files-path}</sourceDirectory>
<outputDirectory>${project.build.directory}/generated-sources</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
Extra info
I actually trying to use Avro to give my Kafka messages a structure. I am also using Confluent Schema Registry and Confluent Avro Kafka Serializer. Nevertheless, I thought I would only have the id as String, but if I try to send messages to Kafka as something non-UUID, it will fail later. However, I found out that there is actually no constraint, and I managed to send any String to Kafka. So, the "logicalType" in Avro is not enforced at all.
The Question
How can I generate Organization.class#id as UUID?
If there is no Avro-support in doing this, what would be the workaround (preferably reusing the org.apache.avro.Conversions.UUIDConversion) ?
Here is a functional example:
Schema:
{
"name": "pk",
"type": {"type": "string", "logicalType": "uuid"}
},
Maven config:
<plugin>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
<version>1.9.1</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>schema</goal>
</goals>
<configuration>
<sourceDirectory>${project.basedir}/../avro/schemas/commands</sourceDirectory>
<outputDirectory>${project.build.directory}/generated-sources/java</outputDirectory>
<!-- TODO Enable this string type once we have 1.10.0 release, or 1.9.2-->
<!--<stringType>String</stringType>-->
<fieldVisibility>PRIVATE</fieldVisibility>
<customConversions>org.apache.avro.Conversions$UUIDConversion</customConversions>
<imports>
<import>${project.basedir}/../avro/schemas/common</import>
<import>${project.basedir}/../avro/schemas/commands/account</import>
<import>${project.basedir}/../avro/schemas/commands/rec</import>
</imports>
</configuration>
</execution>
</executions>
</plugin>
The important thing to note is that there is a conflict between the UUIDConversion and String in the current release of the plugin, as such you either need to choose which is more important, or use 1.10.0-SNAPSHOT which has a fix merged, but not yet released.