Java Spring Rest and Swagger - java

I faced the problem related to Swagger and Java. My lecturer sent me a Swagger file from which I should create a REST API. Also, that REST API should export the same Swagger documentation as Lecturers.
In the Swagger definitions I found that there should be created 2 Models: Odd(object) and Bet(array). Everything is fine with the Odd Model, but I do not find a solution on how to create Bet array. If I simply create an ArrayList named Bet in the getOdd method and put all Odd objects inside, the model will not be created.
I was looking for solutions, but I did not succeed. Thank you in advance.
Lecturer Swagger file:
swagger: "2.0"
info:
description: "Schema"
version: "1.0.0"
title: "API"
tags:
- name: "odds"
description: "Offer and return Odds"
schemes:
- "http"
paths:
/odds:
post:
tags:
- "odds"
summary: "Offer odds for a bet"
consumes:
- "application/json"
produces:
- "application/json"
parameters:
- in: "body"
name: "body"
description: "Odds that should be offered for a bet"
required: true
schema:
$ref: "#/definitions/Odds"
responses:
201:
description: "Odds have been created for bet"
400:
description: "Invalid format of Odds"
/odds/{betId}:
get:
tags:
- "odds"
summary: "Find Odds by Bet ID"
description: "Returns a list of odds for a given bet ID"
produces:
- "application/json"
parameters:
- name: "betId"
in: "path"
description: "ID of bet to return"
required: true
type: "integer"
format: "int64"
responses:
200:
description: "Odds are returned for bet ID"
schema:
$ref: "#/definitions/Bet"
400:
description: "Invalid Bet ID supplied"
404:
description: "Bet not found for given ID"
definitions:
Odds:
type: "object"
properties:
betId:
type: "integer"
format: "int64"
userId:
type: "string"
description: "ID of user who is offering the odds"
odds:
type: "string"
example: "1/10"
**Bet:
type: "array"
items:
$ref: '#/definitions/Odds'**
How Models should look like in Swagger
How getOdd method should look like in Swagger
I will paste some of my work done:
How my Models looks like in Swagger
How my getOdd method looks like in Swagger
My Rest Controller:
#RestController
#RequestMapping("/api")
public class OddController {
#Autowired
OddRepository oddRepository;
#GetMapping("/odds/{betId}")
public Optional<Odd> getOdd(#PathVariable Long betId) {
Optional<Odd> theOdd=oddRepository.findById(betId);
return theOdd;
}
#PostMapping("/odds")
public Odd addOdd(#RequestBody Odd odd) {
odd.setBetId((long) 0);
oddRepository.save(odd);
return odd;
}
My Odd class:
#Entity
#Table(name="odds")
#Data
public class Odd {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="betid")
private Long betId;
#Column(name="userid")
private String userId;
#Column(name="odds")
private String odds;
}

You can use annotations to control the generation of the swagger definitions. There is a old and a new api to do that:
Old:
https://github.com/swagger-api/swagger-core/wiki/Annotations-1.5.X
New:
https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Annotations
In the lecture swagger file 'swagger: "2.0"' is used. Therefore it would be the old one. The new one is producing swagger files for OpenApi 3.0.
Specially the annotation #ApiOperation and #ApiModelOperation could be interesting for you to solve your problem.
See also the JavaDoc:
#ApiOperation: https://docs.swagger.io/swagger-core/v1.5.X/apidocs/index.html?io/swagger/annotations/ApiOperation.html
#ApiModelProperty: https://docs.swagger.io/swagger-core/v1.5.X/apidocs/index.html?io/swagger/annotations/ApiModelProperty.html

Related

Is using Object class to represent all kind of Json payload a good idea

I am currently working in a code base where for every REST API response they use a java class like below
{
Object payload;
List<Error> errors;
String responseStatus;
}
The problem is, when we refer to the swagger documentation of the REST APIs it shows a json structure like below.
{
"payload":{},
"errors": [
{
"errMsg":"",
"errCode": ""
}
],
"responseStatus":""
}
So the response will have payload if response is success, error list in case of errors and response status set to success or failure respectively.
Is it a good practice to use same json structure for error and success?
Is there any way to improve swagger documentation, so that I can show what the response payload json will look like for a particular API response.
EDIT: I just want to mention that I cannot change the response payload to any thing else, as it is being used in more than 1000 APIs and is distributed into different services.
Is there any way to improve at least the swagger documentation, without changing the Response Object in java, because that ship has sailed a long time ago.
It is not a good practice to use the same json structure for error and success responses.
Yes, if you have control over the Swagger definitions, you can specify different responses per response code.
Here is an example from the Swagger documentation
paths:
/users/{id}:
get:
summary: Gets a user by ID.
response:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
# Descriptions of common components
components:
responses:
NotFound:
description: The specified resource was not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
schemas:
# Schema for error response body
Error:
type: object
properties:
code:
type: string
message:
type: string
required:
- code
- message
# Schema for the User response
User:
type: object
properties:
# Add properties for the User object
# ...
you can use ResponseEntity<? extends Response> return type in your methods. so that if response is success, both of them returned, as well as response is error.
public ResponseEntity<? extends ResponseDto> foo(RequestDto request){
if(success){
return new ResponseEntity<>(new SuccessResponse(Enum.SuccessResponse.getMessage,200,dto),HttpStatus.OK);
}
return new ResponseEntity<>(new ErrorResponse(Enum.ErrorResponse.getMessage,400),HttpStatus.BAD_REQUEST);
}

openapi-generator-maven-plugin (Java) does not handle allOf properly

With org.openapitools:openapi-generator-maven-plugin, I have noticed that using allOf composed of multiple objects in a response does not generate a class combining these multiple objects. Instead it uses the first class defined in the allOf section.
Here is a minimal example (openapi.yaml) :
openapi: 3.0.0
info:
title: Test
version: v1
paths:
/test:
get:
operationId: get
responses:
'200':
description: Get
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/A'
- $ref: '#/components/schemas/B'
components:
schemas:
A:
type: object
properties:
attA:
type: string
B:
type: object
properties:
attB:
type: integer
When generating the classes in Java via :
mvn org.openapitools:openapi-generator-maven-plugin:5.2.0:generate \
-Dopenapi.generator.maven.plugin.inputSpec=openapi.yaml \
-Dopenapi.generator.maven.plugin.generatorName=java
It shows a warning:
[WARNING] allOf with multiple schemas defined. Using only the first one: A
As expected, it generates classes A and B. But, when calling get(), the value returned by the call is of type A:
DefaultApi api = new DefaultApi();
A a = api.get();
Instead, I would have expected a composite object containing A and B properties (attA and attB), like this (result from https://editor.swagger.io/):
I have created an issue on GitHub, but hopefully someone here may have had the same issue and managed to fix it.
Also, I can't modify the openapi.yaml file because it's an OpenAPI spec provided by an API I have to call, so modifying it would make no sense and will make it so difficult to manage if their OpenAPI spec change over time.

How to generate a base model class that has no properties using openapi-generator-maven-plugin

I'm using maven plugin for my java project:
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>4.3.1</version>
And in openapi config file (yml) I described one post request and response models for it:
post:
tags:
- instance
summary: createInstances
description: Creates instances for given ids
operationId: createInstances
requestBody:
description: Params for creation
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateInstancesRequest'
responses:
200:
description: Ok
content:
application/json:
schema:
$ref: '#/components/schemas/CreateInstancesResponse'
206:
description: Partial Content
content:
application/json:
schema:
$ref: '#/components/schemas/PartialCreateInstancesResponse'
CreateInstancesResponse:
type: object
PartialCreateInstancesResponseResponse:
allOf:
- $ref: '#/components/schemas/CreateInstancesResponse'
required:
- failedIds
properties:
failedIds:
$ref: '#/components/schemas/Ids'
I expect that it will generate a base java class (CreateInstancesResponse) without fields and an inherited class (PartialCreateInstancesResponse). It is curious that PartialCreateInstancesResponse class is generated. But the base class is not generated. Could you help me please to solve the problem?
Looks like it is not supported yet. You can refer an open issue here for this.
A work around can be, add dummy property in super class.
"Pet": {
"title": "AbstractPet",
"type": "object",
"properties": {
"dummyProperty": {
"type": "string",
"description": "Workaround - OpenAPI generator does not consider definitions without properties"
}
},
"description": "model containing all the details of a pet",
"discriminator": {
"propertyName": "petTypeName"
}
}

How do you have the maven swagger codegen plugin generate Java classes using inheritance?

In our swagger.yaml file, we have a definition Cat that uses allOf to include all properties of Pet.
Cat:
allOf:
- $ref: '#/definitions/Pet'
- type: object
properties:
# ...
The expectation is that when generating Java sources, we get
public class Cat extends Pet {
This works when using the Swagger Editor.
When using swagger-codegen-maven-plugin with no configOptions set, we get the following:
public class Cat {
Cat implements all of Pet's properties itself, instead of extending it.
How do you tell the swagger-codegen-maven-plugin to use Java with inheritance (i.e. extends)? (We tried both spring and java as languages.)
Here's a sample swagger.yaml file:
swagger: '2.0'
info:
version: 1.0.0
title: simple inheritance
tags:
- name: "pet"
paths:
/cat:
put:
tags:
- "pet"
operationId: "updateCat"
consumes:
- "application/json"
parameters:
- in: "body"
name: "body"
required: true
schema:
$ref: "#/definitions/Cat"
responses:
200:
description: Nada
definitions:
Pet:
type: "object"
required:
- "name"
properties:
name:
type: "string"
example: "doggie"
Cat:
allOf:
- $ref: '#/definitions/Pet'
- type: object
properties:
huntingSkill:
type: string
required:
- huntingSkill
As pointed out on github by chiochuan, the workaround is to add
discriminator: "type"
to the parent's definition to make the Java child classes extend it.
definitions:
Pet:
type: "object"
discriminator: "type"
required:
- "name"
properties:
name:
type: "string"
example: "doggie"
It's a bit strange as the OpenAPI Specification Version 2.0 state that the discriminator Fixed Field must reference a property from the same schema, and that it must be a required property, both of which isn't the case - but it works, at least as of today. :)

How to define date example in MicroProfile OpenAPI

I tried to create minimal example of the problem.
Let's say we have simple return object:
public class Result {
#Schema(example = "2012-01-01")
private LocalDate sampleDate;
// omitted getter and setter
}
returned by simple JAX-RS endpoint:
#Path("/example")
#Produces(MediaType.APPLICATION_JSON)
public class Resource {
public List<Result> example() {
// omitted implementation
}
}
MicroProfile OpenAPI in Open Liberty will automatically generate following OpenAPI (Swagger) file:
openapi: 3.0.0
info:
title: Deployed APIs
version: 1.0.0
servers:
- url: http://localhost:9080/api
paths:
/example:
get:
operationId: example
responses:
default:
description: default response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Result'
components:
schemas:
Result:
type: object
properties:
sampleDate:
type: string
format: date
example: 2012-01-01
The problem is that embedded Swagger UI is displaying the date example as empty JS object:
I'm not sure if this is bug on Swagger UI side because if I don't provide any example in Java annotation = any example in OpenAPI file it will render the example as current day, e.g.:
[
{
"sampleDate": "2018-11-27"
}
]
Everything works correctly when I edit the OpenAPI output manually. Both single and double quotes fix the problem:
...
sampleDate:
type: string
format: date
example: '2012-01-01'
or
...
sampleDate:
type: string
format: date
example: "2012-01-01"
will produce expected output:
[
{
"sampleDate": "2012-01-01"
}
]
Question is how to change the annotations to get desired OpenAPI output.
Single quotes are automatically escaped:
#Schema(example = "'2012-01-01'")
private LocalDate sampleDate;
will produce:
...
sampleDate:
type: string
format: date
example: '''2012-01-01'''
Additional double quotes in Java doesn't have any effect on ouput:
#Schema(example = "\"2012-01-01\"")
private LocalDate sampleDate;
will produce same unquoted output:
...
sampleDate:
type: string
format: date
example: 2012-01-01
I know that I can write the OpenAPI yaml output manually but that is my last resort because I don't want to sacrifice automatic generation just because date examples are not behaving as I want. Maybe some OASFilter can be implemented to automatically wrap date's example values or I'm just missing something obvious here.
I've confirmed the behaviour that you're describing.
It the issue with Swagger-UI which is packaged with Microprofile OpenAPI, you could open issue here:
Swagger UI GitHub.
The value produced, without quotes is completely valid yaml, so UI
should be able to parse it as is.

Categories

Resources