Swagger jaxrs. How to generate single enum reference for all data objects - kotlin

I use kotlin + Jersey and swagger documentation generator with the approach code first.
I have two DTO
enum class DataType {
FIRST, SECOND;
}
data class Data1(type: DataType)
data class Data2(type: DataType)
gradle dependency
implementation("io.swagger.core.v3:swagger-jaxrs2-jakarta:2.2.6")
Expected result for documentation:
components:
schemas:
DataType:
type: string
enum: [ FIRST, SECOND ]
Data1:
type: object
properties:
type:
$ref: '#/components/schemas/DataType'
Data2:
type: object
properties:
type:
$ref: '#/components/schemas/DataType'
Actual result
components:
schemas:
Data1:
type: object
properties:
type:
type: string
enum: [ FIRST, SECOND ]
Data2:
type: object
properties:
type:
type: string
enum: [ FIRST, SECOND ]
The problem is, that if any client reuses schema to generate classes for his own app, he gets two separate enum classes for the type property.
Q: How to make generator to detect that it is the same enum and extract it to separate type reference?

Ok, I found one approach myself
import io.swagger.v3.core.jackson.ModelResolver.SET_PROPERTY_OF_ENUMS_AS_REF
init {
System.setProperty(SET_PROPERTY_OF_ENUMS_AS_REF, "any value")
}
The other way is to implement a custom ModelResolver, but the class implementation looks quite complicated
UDPATE
Just noticed, that public static boolean enumsAsRef = System.getProperty("enums-as-ref") != null; in ModelResolver is not final. So we can do even simpler
init {
io.swagger.v3.core.jackson.ModelResolver.enumsAsRef = true
}

Related

How to define a message fixed field value in AsyncAPI

We are looking at using CloudEvents as a message envelope for events generated in our system, and defining those messages using AsyncAPI.
We are using the CloudEvents type property to identify each event type, with a different data property schema for each type. Whilst I have worked out how to define the individual event structures using AsyncAPI, I can't identify how to set the type property to the value required for that event type.
What would seem a mis-use of the enum keyword is the only way so far that I've been able to associated the event type to the property, as shown in the example below. Is there an accepted pattern for achieving the definition of these fixed properties within the AsyncAPI specification?
channels:
user:
subscribe:
message:
payload:
$ref: '#/components/schemas/firstEventPayload'
components:
schemas:
firstEventPayload:
type: object
allOf:
- $ref: 'https://raw.githubusercontent.com/cloudevents/spec/v1.0.1/spec.json'
properties:
type:
type: string
enum: [test.cloud.event.new-user]
data:
type: object
properties:
userId:
type: string
format: uuid
email:
type: string
format: email
Credit to developers.redhat.com for the cloudevent/spec $ref
In JSON Schema (AsyncAPI Schema is a superset of it) you can also use const like:
type:
type: string
const: test.cloud.event.new-user

Can I use "type" to name an attribute in an openAPI 3.0 spec? [duplicate]

Is there any way to name a custom property 'type', given that there already exists a special 'type' property which is a reserved keyword.
components:
schemas:
element:
type: object
properties:
name:
type: string #type here is the keyword
type: #type here is the actual name of the property!
type: string
enum:
- radiogroup
- checkbox
The back-end system which produces the JSON messages cannot be modified to rename the property.
Thanks.
Reserved keywords can be used as property/parameter names in all OpenAPI versions.
The only issue with your example is that YAML indentation is off, other than that your object and property definitions are perfectly valid.
components:
schemas:
element:
type: object
properties:
name:
type: string
type: # <----- yes, property name can be "type"
type: string
enum:
- radiogroup
- checkbox

Kotlin, mapstruct and generated DTO error "does not have an accessible parameterless constructor."

I am trying to use mapstruct to convert my complex dtos on my kotlin project.
mapstruct : 1.3.1.final
kotlin: 1.3.71
openapi generator: 4.2.3
For example, i want to convert from a simple object to TestObjectDTO
#Mapping(source = "mydescription", target = "description")
fun convertToDto(dto: TestObject): TestObjectDTO
I use OpenApi to generate my DTO :
yaml
components:
schemas:
TestObject:
title: TestObject
description: ''
type: object
properties:
mode:
type: string
description:
type: string
required:
- mode
- description
generated DTO
/**
*
* #param mode
* #param description
*/
data class TestObjectDTO (
#get:NotNull
#JsonProperty("mode") var mode: kotlin.String,
#get:NotNull
#JsonProperty("description") var description: kotlin.String
) {
}
A always have an error, because my constructor does not permit parameterless.
Did you have any idea how to fix this?
You can instantiate the DTO class manually using #ObjectFactory. The problem is that TestObjectDTO does not accept nulls, so you will need to use dummy values, which is not that pretty:
#Mapper
interface TestObjectMapper {
#ObjectFactory
fun createDto() = TestObjectDto("", "")
#Mapping(source = "mydescription", target = "description")
fun convertToDto(dto: TestObject): TestObjectDto
}
1.3.1.Final does not support using constructors to create your objects. You will have to define a parameter less constructor as the error message says.
However, you can try 1.4.0.Beta3 that adde support for mapping using constructors. And this works with Kotlin data classes
In Java
A possible solution for this is to annotate the dto class with #NoArgsConstructor, for example:
#NoArgsConstructor
public class MyClass {
private String myString;
private Int myInt;
}

RAML default envelope

My API has a default envelope for all collection-type resources, like the following:
{
"data": [{},{},{}],
"self": "http://api-url/members",
"total": 120,
"limit": 10,
"offset": 0
}
Is it possible to define this using RAML? Inside a resourceType? How?
It depends on what you are using for specifying your API's entities:
JSON Schema Draft 3 - Create a base schema with these common fields and use the extends mechanism in each concrete schema,
JSON Schema Draft 4 - Create a shared schema with these common fields and use the allOf mechanism to mix it in each concrete schema,
RAML 1.0 Types - Create a common type and refer to it in each concrete type via the type attribute.
With RAML 1 you can use types and inheritance to define your envelope and data types. And resource types for applying to all collection-type resources, for example:
types:
person:
type: object
properties:
name:
age:
car:
type: object
properties:
model:
brand:
envelop:
type: object
properties:
data: person[] | car[]
self: string
total: integer
limit: integer
offset: integer
resourceTypes:
- collection:
get:
responses:
200:
body:
type: envelope
/users:
type: collection
get:
More info here, here and here

How can I specify that an object's type is one of several possible object types?

I have an API where I post a set of object to the server, differentiated by type, where each type has some different parameters. The commands are structured like so:
{
"type": <command-name>,
"args": {
<command-specific args>
}
}
For example, these may be two possible commands:
{
"type": "Flooblinate",
"args": {
"intensity": "High",
"frequency": "Every blue moon"
}
}
{
"type": "Blagostrate",
"args": {
"temperature": 34.5,
"darkMatter": true
}
}
How can I specify this in Swagger? I can specify an enum for "type", but how do I say ""args" is one of these possible objects"?
I've checked out the docs but nothing stands out. The most promising one was allOf because it displayed nicely in the editor (pastebin, paste into the online editor):
definitions:
Product:
type: object
allOf:
- type: object
title: Flooblinate
properties:
intensity:
type: string
frequency:
type: string
- type: object
title: Blagostrate
properties:
temperature:
type: number
darkMatter:
type: boolean
Which looks like this:
However, this isn't semantically what I need, and, not surprisingly, in the online viewer (that one's not set up for my test case, not sure how to link up a local file easily), they are presented as if all the fields appear at the same time, which is of course what allOf means:
What's the proper way to represent this with Swagger?
According to input from Ron on the google group, what I want is to use the discriminator property:
definitions:
Product:
type: object
discriminator: type
properties:
type: string
required: [type]
Flooblinate:
allOf:
- $ref: '#/definitions/Product'
- type: object
properties:
intensity:
type: string
frequency:
type: string
Blagostrate:
allOf:
- $ref: '#/definitions/Product'
- type: object
properties:
temperature:
type: number
darkMatter:
type: boolean
Semantically this means what I want it to mean. I should specify $ref: '#/definitions/Product' wherever the API accepts or returns any one of Flooblinate or Blagostrate. Note this requires a field (called type here) on the objects to act as the discriminator.
I thought this may be the approach, but the tools didn't show what I expected. However:
That's because the tools are not 100% in support of the discriminator yet - however, that's the right way to describe your use case.
Once you define the discriminator in the top level model, anything that 'allOf's it will be considered a viable option, and indeed you're refer to the top level model for usage.