openapi generates minimum and maximum which is not working properly - spring

openapi minimum/maximum
put:
summary: add
operationId: add
requestBody:
description: value
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Value'
components:
schemas:
Value:
type: object
required:
- value
properties:
value:
type: integer
format: int64
minimum: 1
maximum: 999
generates
#Min(1L) #Max(999L)
public Long getValue() {
return value;
}
Which is not working properly. I try
mockMvc.perform(
put(RESOURCE_URL + "/1/add")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"value\":0}"))
I've no idea is it API problem or spring #Min(1L) #Max(999L) validator problem?

dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-validation'
}

Related

Enumerating an array of exactly 2 (slightly different) objects in OpenAPI 3.1.0?

I need to document a REST endpoint that takes the following request body:
{
"variables": [
{
"name": "groupGuid",
"value": "...",
"typeConstraint": "string",
},
{
"name": "addMembership",
"value": "...",
"typeConstraint": "boolean",
}
]
}
The variables array must take exactly 2 objects:
one with the required name groupGuid and typeConstraint string,
and the other with the required name addMembership and typeConstraint boolean.
The type of their respective values are specified by typeConstraint, but the actual values of the value properties are otherwise unconstrained.
Currently, I've got this, which is very underspecified (and possibly wrong) and relies on notes I've manually included:
'/test':
post:
requestBody:
content:
application/json:
schema:
type: object
properties:
variables:
type: array
uniqueItems: true
description: 'Must contain exactly 2 objects, with the following `name`s: (1) `groupGuid`: GUID of the group, (2) `addMembership`: Whether the task will add (true) or remove (false) members from the group.'
items:
type: object
additionalProperties: false
properties:
name:
enum:
- groupGuid
- addMembership
type: string
value:
type:
- string
- boolean
typeConstraint:
type: string
enum:
- string
- boolean
description: The type of `value`.
Is it possible to properly spec these requirements in YAML / OpenAPI 3.1.0, and if so, how? Thanks.
If what you want is to show both objects in the example body and accept either or both of them, perhaps you could use anyOf.
Below an example
paths:
/test:
post:
tags:
- Test
description: "Must contain exactly 2 objects, with the following `name`s: (1) `groupGuid`: GUID of the group, (2) `addMembership`: Whether the task will add (true) or remove (false) members from the group."
requestBody:
required: true
content:
application/json:
schema:
properties:
variables:
type: array
uniqueItems: true
items:
anyOf:
- $ref: '#/components/schemas/Object1'
- $ref: '#/components/schemas/Object2'
responses:
'200':
description: Sucess
components:
schemas:
Object1:
type: object
description: "The value here is string"
properties:
name:
type: string
value:
type: string
typeConstraint:
type: string
Object2:
type: object
description: "The value here is boolean"
properties:
name:
type: string
value:
type: string
typeConstraint:
type: boolean

OpenAPI: Specifying an optional request object but with mandatory fields

I'm using an OpenApi generator setup on Maven + Spring Boot (2.7.7) to generate API interfaces to implement in my software. This includes Validation.
I'm trying to understand if there is a way to specify validation in an OpenApi (3.0.1 atm but I'm flexible) yaml in such a way that I can have an object not be mandatory, but if ANY fields are included, then all of it must be included.
For example:
paths:
/api/complexobject:
get:
tags:
- complexobject
summary: generic search
operationId: findAll
parameters:
- in: query
name: complexobject
schema:
$ref: '#/components/schemas/ComplexObject'
explode: true
- in: query
name: sort
schema:
$ref: '#/components/schemas/Sorting'
- in: query
name: page
schema:
$ref: '#/components/schemas/Pagination'
responses:
"200":
description: successful operation
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ComplexObject'
components:
schemas:
ComplexObject:
type: object
<cut>
Pagination:
type: object
properties:
page:
type: integer
minimum: 0
description: 0-indexed page number for pagination
size:
type: integer
minimum: 1
description: Number of returned records (page size). Suggested default is 20
required:
- page
- size
Sorting:
type: object
properties:
dir:
$ref: '#/components/schemas/SortDir'
sorted:
type: array
items:
type: string
required:
- dir
- sorted
SortDir:
type: string
enum: ["asc", "desc"]
The intention here is that the Sorting and the Pagination objects can be omitted from the query, however IF they're included, they must be wholly included (and with valid inputs too).
However it seems that at runtime the Pagination and Sorting Java Objects are instanced even without any parameters being sent to the Controller, which makes them fail the validation.
Is there some different validation setup I can use in the yaml, or do I need to change something in Java code?
Or, just remove the requirements altogether from the OpenAPI field declation and do programmatic validation directly instead?
As far as I can tell the specification I wrote is correct, however the generator (the most recent one available at the time of writing) doesn't support this kind of behaviour.
However what it does support is the use of defaults:
Pagination:
type: object
nullable: true
properties:
page:
type: integer
minimum: 0
default: 0 # Added default
description: 0-indexed page number for pagination
size:
type: integer
minimum: 1
default: 20 # Added default
description: Number of returned records (page size).
required:
- page
- size
Sorting:
type: object
nullable: true
properties:
dir:
$ref: '#/components/schemas/SortDir'
sorted:
type: array # No default needed, see below!
items:
type: string
required:
- dir
- sorted
SortDir:
type: string
default: "asc" # Added default
enum: ["asc", "desc"]
In this case you'll actually be bypassing the problem a bit, in that you'll never end up in a situation when any of the required fields will ever be null, see the generated code for reference:
#Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2023-01-30T16:25:58.375+01:00[Europe/Berlin]")
public class Pagination {
#JsonProperty("page")
private Integer page = 0;
#JsonProperty("size")
private Integer size = 20;
As a side note, the validation for the "sorted" property actually works as expected, even though the way the codegen attains that is weird. It'll work the same way whether you specify it as required or not AND without specifying defaults.
However we have the two following codegens:
Without required:
#JsonProperty("sorted")
#Valid
private List<String> sorted = null;
// Cut for brevity
/**
* Get sorted
* #return sorted
*/
#NotNull
#Schema(name = "sorted", required = false)
public List<String> getSorted() {
return sorted;
}
WITH required:
#JsonProperty("sorted")
#Valid
private List<String> sorted = new ArrayList<>();
// Cut for brevity
/**
* Get sorted
* #return sorted
*/
#NotNull
#Schema(name = "sorted", required = true)
public List<String> getSorted() {
return sorted;
}
This also means that the validation in the second case WILL be active... but never fail.

Get list of object in the swagger API response section

I am using Open API 3.0 to generate the code.
Below is Open API specification:
spec.yml:
openapi: 3.0.0
info:
version: 1.0.0
title: Store API
description: A store API to get store and related department information
termsOfService: http://www.***.com/xml,
contact:
name: MTAMP Support,
url: http://www.****.com/xml,
email: mtamp#mt.com
paths:
/api/stores:
get:
summary: To get all stores
tags:
- Stores
description: To get all store and department information
operationId: getStores
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/StoresDTO'
/api/stores/{storeId}:
parameters:
- in: path
name: storeId
required: true
schema:
type: integer
format: int64
description: Pass a store Id as a parameter
get:
summary: To get store infomation based on storeId. When you pass storeId as request related store information along with department will get returned.
tags:
- Stores
description: To get store information based on storeId
operationId: getStoresById
x-mtampException:
- throws com.mt.mtamp.storeservice.exception.ResourceNotFoundException
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/StoresDTO'
/api/stores/{storeId}/regions/{regionId}:
parameters:
- in: path
name: storeId
required: true
schema:
type: integer
format: int64
description: Pass a store Id as a parameter
- in: path
name: regionId
required: true
schema:
type: integer
format: int64
description: Pass a region Id as a parameter
get:
summary: To get store infomation based on regionId. When you pass region as request related store information will get returned.
tags:
- Stores
description: To get store information based on regionId.
operationId: getStoresByRegionId
x-mtampException:
- throws com.mt.mtamp.storeservice.exception.ResourceNotFoundException
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/StoresDTO'
/api/departments/{departmentId}/stores/{storeId}:
parameters:
- in: path
name: storeId
required: true
schema:
type: integer
format: int64
description: Pass a department Id as a parameter
- in: path
name: departmentId
required: true
schema:
type: integer
format: int64
description: Pass a store Id as a parameter
get:
summary: To get department infomation based on storeId. When you pass store Id as request related department information will get returned.
tags:
- Departments
description: To get department information based on storeId.
operationId: getDepartmentsByStoreId
x-mtampException:
- throws com.mt.mtamp.storeservice.exception.ResourceNotFoundException
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/DepartmentsDTO'
/api/regions/{regionId}/customer/{customerId}:
parameters:
- in: path
name: customerId
required: true
schema:
type: integer
format: int32
description: Pass a customer Id as a parameter
- in: path
name: regionId
required: true
schema:
type: integer
format: int64
description: Pass a region Id as a parameter
get:
summary: To get region based on customer Id.
tags:
- Regions
description: To get region information based on customer Id.
operationId: getRegionByCustomerId
x-mtampException:
- throws com.mt.mtamp.storeservice.exception.ResourceNotFoundException
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/RegionsDTO'
components:
schemas:
StoresDTO:
required:
- storeName
- storeLocation
properties:
storeId:
type: integer
format: int64
example: 1
storeName:
type: string
example: FreshPro
storeLocation:
type: string
example: Germany
departments:
type: array
items:
$ref: '#/components/schemas/DepartmentsDTO'
DepartmentsDTO:
required:
- departmentId
- departmentName
properties:
departmentId:
type: integer
format: int64
example: 42
departmentName:
type: string
example: Vegetables
storeId:
type: integer
format: int64
example: 34
RegionsDTO:
required:
- regionId
- regionName
properties:
regionId:
type: integer
format: int64
example: 22
regionName:
type: string
example: Fresh Pro
customerId:
type: integer
example: 19
stores:
type: array
items:
$ref: '#/components/schemas/StoresDTO'
Generated Code:
#RequestMapping(
method = RequestMethod.GET,
value = "/api/stores/{storeId}/regions/{regionId}",
produces = { "application/json" }
)
ResponseEntity<List<StoresDTO>> getStoresByRegionId(
#Parameter(name = "storeId", description = "Pass a store Id as a parameter", required = true) #PathVariable("storeId") Long storeId,
#Parameter(name = "regionId", description = "Pass a region Id as a parameter", required = true) #PathVariable("regionId") Long regionId
)throws com.mt.mtamp.storeservice.exception.ResourceNotFoundException;
Here when I load the swagger for example for service /api/stores/{storesId}/regions/{regionId}, I am seeing the response showing stores and departments object.
This API returns a list of store object but in swagger it showing be single store object.
Can I know how to fix this swagger and Openapi issue

Object query parameters are not listed in the generated OpenAPI file

I'm trying to list the object query parameters in an OpenAPI file generated by swagger-maven-plugin. I have the following endpoint which contains two objects RecargaCupoFilter and Pageable, each object has its own parameters.
#GetMapping(value = "/wallet/recharge", produces = {"application/json; charset=UTF-8"})
#ApiOperation(value = "Allow to query recharge history", produces = "application/json")
#PreAuthorize("isAuthenticated() and (#filter.username == authentication.name or hasAnyRole('ROLE_COORDINADOR_CUPO', 'ROLE_ADMINISTRADOR'))")
#ApiPageable
public ResponseEntity<List<OperacionDTO>> consultarRecargas(RecargaCupoFilter filter, Pageable pageable) {
pageable = PaginationUtil.ensurePageMaxSize(pageable);
Page<OperacionDTO> page = cupoService.consultarRecargaCupo(filter, pageable);
HttpHeaders headers = PaginationUtil
.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page);
return ResponseEntity.ok().headers(headers).body(page.getContent());
}
When I try to generate an OpenAPI file I get the following:
/api/wallet/recharge:
get:
summary: "Allow to query recharge history"
description: ""
operationId: "BilleteraResource_consultarRecargas_GET"
produces:
- "application/json; charset=UTF-8"
parameters:
- name: "filter"
in: "query"
required: true
- name: "sort"
in: "query"
description: "Criterios de ordenamiento, con formato: propiedad(,asc|desc).\
\ El orden por defecto es ascendente. Se soportan mĂșltiples criterios de\
\ ordenamiento."
required: false
type: "array"
items:
type: "string"
collectionFormat: "multi"
responses:
200:
description: "successful operation"
schema:
type: "array"
items:
$ref: "#/definitions/OperacionDTO"
I expect to have something like the file generated by SpringFox.
/api/wallet/recharge:
get:
tags:
- billetera-resource
summary: Allow to query recharge historyo
operationId: consultarRecargasUsingGET
produces:
- application/json;charset=UTF-8
- application/json
parameters:
- name: offset
in: query
required: false
type: integer
format: int64
- name: pageNumber
in: query
required: false
type: integer
format: int32
- name: pageSize
in: query
required: false
type: integer
format: int32
PS: I'm using swagger-maven-plugin because it generates the OpenAPI file in Maven build phase without execute the app.

How to define array-of-objects as parameter?

I am quite new to Swagger, so this might be a basic question.
I am able to create .yml file for an API which takes an array of integers as parameter, as follows:
Add samples
---
tags:
- MY API
parameters:
- name: my_id
in: path
type: integer
required: true
description: Some des
- name: body
in: body
schema:
id: add_samples
required:
- sample_ids
properties:
sample_ids:
type: array
items:
type: integer
description: A list of sample ids to be added
responses:
'200':
description: Added samples.
'400':
description: Error adding samples.
This is what I send to the above API and everything works fine:
{"sample_ids": [475690,475689,475688]}
Now, instead of an array of integers, if I want to use some complex object as parameter, how to do it?
E.g. If this is what I want to send:
{"sample_ids": [{
"sample_id": "7",
"some_prop": "123"
},
{
"sample_id": "17",
"some_prop": "134"
}]}
How should the .yml file look? I have tried something like this and it doesn't seem to work:
Add sample
---
tags:
- Samples API
models:
Sample:
id: Sample
properties:
sample_id:
type: string
default: ""
description: The id for this sample
some_prop:
type: integer
description: Some prop this sample
parameters:
- name: body
in: body
schema:
id: add_sample
required:
- sample_ids
properties:
samples:
type: array
description: A list of samples to be added
items:
$ref: Sample
responses:
'201':
description: Created a new sample with the provided parameters
'400':
description: SOME ERROR CODE
This one seems to work, mostly:
Add sample
---
tags:
- Samples API
models:
Sample:
id: Sample
properties:
sample_id:
type: string
default: ""
description: The id for this sample
some_prop:
type: integer
description: Some prop this sample
parameters:
- name: body
in: body
schema:
id: add_sample
required:
- sample_ids
properties:
samples:
type: array
description: A list of samples to be added
items:
$ref: Sample
responses:
'201':
description: Created a new sample with the provided parameters
'400':
description: SOME ERROR CODE
Now only problem is, in the Swagger UI, it is not showing member variables and their default values. Rather is showing it as null:
{
"samples": [
null
]
}

Resources