I am almost done with my Swagger yaml file but there is a particular piece that I cannot seem to write out correctly:
You see where it says invitationCompletedOn and afterwards I need to add createdOn and completedOn:
When I add createdOn like so:
invitationCompletedOn:
type: string
example: 2021-09-22T08:27:49.622Z
createdOn:
type: string
example: 2021-09-22T08:27:49.622Z
But that breaks the output. Here is the entire yaml file:
/**
* #openapi
* /api/v2/organizations/:organizationId/invitations:
* get:
* description: Get a list of Data Access Object invitations.
* responses:
* 200:
* description: Get a list of Data Access Object invitations.
* content:
* application/json:
* schema:
* type: object
* properties:
* data:
* type: array
* items:
* type: object
* properties:
* _type:
* type: string
* description: Data Transfer Object of new invitation.
* example: ViewInvitationDto
* email:
* type: string
* description: Invitation email.
* example: dolittle#qa.co
* pui:
* type: string
* description: The patients user id.
* example: spyrt_p10102acc
* groupId:
* type: string
* description: The organizations identification.
* example: poi_5002
* roleDescription:
* type: string
* description: The role of the organization.
* example: admin
* viewHealthWorkerDto:
* type: object
* properties:
* _type:
* type: string
* description: Data Access Object for HealthWorker
* example: ViewHealthWorkerDto
* _id:
* type: string
* description: Identification for Healthworker
* example: 613ef0964b525196cf8599bf
* assignedRoleCode:
* type: string
* description: Assigned code role for organizations and healthworkers.
* example: armada.organization.doctor
* pui:
* type: string
* description: Assigned code role for organizations and healthworkers.
* example: spyrt_p10102acc
* firstName:
* type: string
* description: The first name of the healthworker.
* example: John
* lastName:
* type: string
* description: The last name of the healthworker.
* example: Dolittle
* healthWorkerTags:
* example:
* - key: migratedOn
* value: 2021-11-19T00:00:43.722Z
* - key: exporterVersion
* value: 2
* - key: _oldAccountId
* value: 10102
* - key: _oldPatientIds
* value: 10729
* schemaVersion:
* type: string
* example: 1
* createdOn:
* type: string
* example: 2020-05-08T07:43:43.000Z
* updatedOn:
* type: string
* example: 2020-05-08T07:43:43.000Z
* roleDescription:
* type: string
* example: admin
* email:
* type: string
* example: dolitle#qa.co
* userTags:
* example:
* - key: migratedOn
* value: 2021-11-19T00:00:43.722Z
* - key: exporterVersion
* value: 2
* - key: _oldAccountId
* value: 10102
* - key: _oldPatientIds
* value: 10729
* invitationCompletedOn:
* type: string
* example: 2021-09-22T08:27:49.622Z
*
*
*/
createdOn and completedOn should probably be sibling properties of viewHealthWorkerDto rather than of invitationCompletedOn. Try this:
* viewHealthWorkerDto:
* type: object
* properties:
* ...
* createdOn:
* type: string
* example: 2021-09-22T08:27:49.622Z
* completedOn:
* type: string
* example: 2021-09-22T08:27:49.622Z
Also, I see that you've defined an example value for userTags but no actual schema. From your screenshot, it looks like userTags (and similar arrays of key-value objects) can be properly defined like so:
* userTags:
* type: array
* items:
* type: object
* properties:
* key:
* type: string
* value:
* type: string
* example:
* - key: migratedOn
* value: 2021-11-19T00:00:43.722Z
* - ...
Related
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.
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
I just need to get a string from this json, for example: https://filesamples.com/samples/code/json/sample1.json
I took the Chainlink example and just change the URL and path. The example works fine, but if you use a different json o jobid it do not work.
Link to chainlink example: https://docs.chain.link/docs/large-responses/
Is there any way to solve this?
Code used:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "#chainlink/contracts/src/v0.8/ChainlinkClient.sol";
/**
* #notice DO NOT USE THIS CODE IN PRODUCTION. This is an example contract.
*/
contract GenericLargeResponse is ChainlinkClient {
using Chainlink for Chainlink.Request;
// variable bytes returned in a signle oracle response
bytes public data;
string public dataS;
/**
* #notice Initialize the link token and target oracle
* #dev The oracle address must be an Operator contract for multiword response
*
*
* Kovan Testnet details:
* Link Token: 0xa36085F69e2889c224210F603D836748e7dC0088
* Oracle: 0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8 (Chainlink DevRel)
*
*/
constructor(
) {
setChainlinkToken(0xa36085F69e2889c224210F603D836748e7dC0088);
setChainlinkOracle(0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8);
}
/**
* #notice Request variable bytes from the oracle
*/
function requestBytes(
)
public
{
bytes32 specId = "7a97ff8493ec406d90621b2531f9251a";
uint256 payment = 100000000000000000;
Chainlink.Request memory req = buildChainlinkRequest(specId, address(this), this.fulfillBytes.selector);
req.add("get","https://filesamples.com/samples/code/json/sample1.json");
req.add("path", "fruit");
requestOracleData(req, payment);
}
event RequestFulfilled(
bytes32 indexed requestId,
bytes indexed data
);
/**
* #notice Fulfillment function for variable bytes
* #dev This is called by the oracle. recordChainlinkFulfillment must be used.
*/
function fulfillBytes(
bytes32 requestId,
bytes memory bytesData
)
public
recordChainlinkFulfillment(requestId)
{
emit RequestFulfilled(requestId, bytesData);
data = bytesData;
dataS = string(data);
}
}
The response of that API is:
{
"fruit": "Apple",
"size": "Large",
"color": "Red"
}
The fruit has a value of Apple. You'll need to have the API return the bytes edition of Apple instead.
You'll notice the example returns JSON that looks like:
{
"image": "0x68747470733a2f2f697066732e696f2f697066732f516d5358416257356b716e3259777435444c336857354d736a654b4a4839724c654c6b51733362527579547871313f66696c656e616d653d73756e2d636861696e6c696e6b2e676966"
}
Which is the hex edition of the URL of the image.
I have been setting up the Nelmio API Doc Bundle with Swagger-PHP. All is working as expected the only thing I cannot seem to figure out/understand is the schemas.
In the User controller I have the following annotation:
* #OA\RequestBody(
* description="Updated user object",
* required=true,
* #OA\MediaType(
* mediaType="multipart/form-data",
* #OA\Schema(ref="#/components/schemas/User")
* )
* )
In my Entity/User class I defined the schema as follows:
/**
* User
*
* #OA\Schema(schema="User")
*
* #ORM\Table(schema="app", name="users")
* #ORM\Entity
*/
class User implements UserInterface
In the User controller I have the use App\Entity\User; defined as well.
In my mind this would be enough for the schema to be found but it doesn't work as I would otherwise not be posting here :)
The only way I was able to make it work is to run vendor/bin/openapi --format yaml src and copy/paste the schema output into the nelmio_api_doc.yaml file. This is the schema part I copy/pasted:
User:
properties:
first_name:
type: string
middle_name:
type: string
last_name:
type: string
initials:
type: string
username:
type: string
password:
type: string
status:
type: integer
email:
type: string
id:
type: integer
customer_id:
type: integer
locked:
type: boolean
type: object
So my question is, is this the way to about it or should the schema section be created automatically?
Thanks for any insights.
NelmioApiDocBundle does not load all files to fetch annotations in opposition to swagger-php, to load a schema, you should use the #Model annotation, see https://symfony.com/doc/current/bundles/NelmioApiDocBundle/index.html#use-models.
In your case, that would give the following:
use Nelmio\ApiDocBundle\Annotation\Model;
/**
* #OA\RequestBody(
* description="Updated user object",
* required=true,
* #OA\MediaType(
* mediaType="multipart/form-data",
* #OA\Schema(ref=#Model(type=User::class))
* )
* )
*/
I tried to generate a API document by apidoc
If my response is a array like
[
{"id" : 1, "name" : "John"},
{"id" : 2, "name" : "Mary"}
]
How could I set in #apiSuccess?
I had try Object[] but did not know how to set the field name.
Thanks.
Lets say in the above example the id and name are of a user-profile, and you have an array of user-profile objects, then the #apiSuccess will look like:
/**
* #api {get} /users
* #apiSuccess {Object[]} profiles List of user profiles.
* #apiSuccess {Number} profiles.Id Users id.
* #apiSuccess {String} profiles.Name Users Name.
*/