In my previous post
How to accept multiple query parameters with different parameter name in one class
I learned about passing the query parameters, and I also achieved it
But now the problem is I am in a swagger setup where I use codegen for generating API default code which provides me an interface using which I implement it into my controller layer and overrides those methods and add my logic
The problem is I have an internal api-doc file that generates a custom code, in that file how should I mention that I am using my own custom class for passing it to be used as a query parameter object? Currently, my api doc file looks like below. Problem with below file is I have to define each and every query parameter and as I learned in my previous stack article that we can wrap it into an class object. But my question here is how should I mention path to my custom class into this file.
/tasks:
get:
parameters:
- $ref: 'api-params.yaml#/name'
- $ref: 'api-params.yaml#/age'
- $ref: 'api-params.yaml#/credentials'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: 'api-resp.yaml#/someResponse'
"401":
description: "Unauthorized"
content: { }
"403":
description: "Forbidden"
content: { }
And also can we directly define a model using swagger? If yes how can we do that?
I mean I am using custom models for referring them to be used as request body and I usually mention them like below, but my question is can we use similar behaviour for queryParams also
schema:
$ref: '#/components/schemas/RquestBodyPayload'
but is the similar this is also allowed for query parameters ?
Related
Let's say we have a base class Pet, with subclasses Dog, Cat and Fish defined as our service response:
openapi: '3.0.3'
paths:
'/pets':
get:
responses:
200:
description: Ok
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
components:
schemas:
Pet:
type: object
description: Base class for all types of pets
properties:
kind:
type: string
required:
- kind
discriminator:
propertyName: kind
mapping:
Dog: '#/components/schemas/Dog'
Cat: '#/components/schemas/Cat'
Fish: '#/components/schemas/Fish'
We have several clients consuming our service using this OpenApi definition.
Now, we want to add a fourth pet type, Snakes, so we add it to the "mapping" section of the Pet schema. But if we deploy such a change to our service production system, our clients will fail to parse the service responses whenever a Snake is returned.
We can publish our new improved API in advance and give some time to our clients for upgrading and add support for this new type, but we cannot afford to delay the upgrade indefinitely. Therefore, we want to be able to deploy our new service version and send snakes in its response without breaking things at our clients side.
Is there any way to define this polymorphic type in an "open" way, so clients can parse snakes and other future unknown-yet types without getting an InvalidTypeIdException? I don't need to be able to get the snake fields (I am aware of the security issues involved), I would be happy to have just a "Pet" instance with kind set to "Snake", and no other info.
If this cannot be accomplished at the server side, is there any way to get client side code generated to be resilient against these errors?
If no solution exists at either definition or client code generation, could such a feature be implemented (at the client side) in Java using Jackson annotations or configuration?
So far, I have not found any way to get what we are looking for from the server side, or through the OpenApi spec. However, I have found two possible ways to get it from the client side using Jackson.
The first way is by disabling FAIL_ON_INVALID_SUBTYPE at the ObjectMapper:
var mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE);
Which will make the unknown subtype instances to be deserialized into nulls. It is important to take this into account for Pet lists, as they may have null items in the future.
The other way would be to create a DeserializationProblemHandler subclass that would force the unknown instance to be deserialized into an instance of the root type:
public class UnknownPetTypeHandler extends DeserializationProblemHandler {
#Override
public JavaType handleUnknownTypeId(DeserializationContext ctx,
JavaType baseType, String subTypeId,
TypeIdResolver idResolver, String failureMsg) {
return Pet.class.equals(baseType.getRawClass()) ? baseType : null;
}
}
and add it to the ObjectMapper used for deserialization:
var mapper = new ObjectMapper().addHandler(new UnknownPetTypeHandler());
This second option avoids null values and provides a mechanism for detecting and notifying on unknown types received, at the cost of extra complexity.
One of my API returns an object of type ServiceResponse<Pet>. I want to specify this as the response type for the 200 status code in my Swagger specification file.
API :
public interface api(){
ServiceResponse<Pet> getPet()
throws ServiceException;
}
Swagger specification :
...
responses:
200:
description : OK
schema: # what should i put here?
...
There is support for response types of simple arrays and objects but i'm having a hard time finding a solution for this. Please help!
I'm learning creating rest Api using spring boot. FOr reference I was checking some existing code where in yaml file I found two parameters mentioned below
name: "name"
in: "query"
description: "doing something"
x-phase-dataelem: "CONF//Y/N"
required: false
schema:
type: string
maxlength:15
name: "tame"
in: "header"
description: "doing something something"
x-phase-dataelem: "CONF//Y/N"
required: true
schema:
type: string
maxlength:15
am literally not able to understand these parameters
in: "query"
in: "header"
x-phase-dataelem: "CONF//Y/N"
I know that, these are some values which are being passed to client url to process, but not able to understand these parameters. what's significance of using these 3 parameters ?
can anyone help ?
This YAML snippet looks like a Swagger/OpenAPI contract. You can find more about OpenAPi and read its specification here - https://swagger.io/specification/
in describes the location of the HTTP parameter.
Quote from the OpenAPI specification:
There are four possible parameter locations specified by the in
field:
path - Used together with Path Templating, where the parameter value is actually part of the operation's URL. This does not include
the host or base path of the API. For example, in /items/{itemId}, the
path parameter is itemId.
query - Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id.
header - Custom headers that are expected as part of the request. Note that RFC7230 states header names are case insensitive.
cookie - Used to pass a specific cookie value to the API.
Regarding the x-phase-dataelem, it is a custom extension in your OpenAPI contract. It is used for providing some additional metadata/information/properties about the described items (including parameters).
Quote from the OpenAPI specification:
While the OpenAPI Specification tries to accommodate most use cases,
additional data can be added to extend the specification at certain
points.
The extensions properties are implemented as patterned fields that are always prefixed by x-, for example, x-internal-id. The value can be null, a primitive, an array or an object. Can have any valid JSON format value.
I have OpenAPI descriptor of the service that has cofigurable path and it can have multiple parameters in body (just strings but it may have multiple or none in them):
openapi: "3.0.0"
paths:
/{pagePath}:
post:
operationId: modifyWithPost
parameters:
- name: pagePath
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
additionalProperties:
type: string
responses:
'201':
description: Page created
Unfortunately, it generates me API that has just path parameter, and I don't know how to send additional part of data in Java API:
public void modifyWithPost(String pagePath) throws ApiException {
this.modifyWithPost(pagePath);
}
I have tried to create separated scheme also, but the exact result is generated. Moreover, even if I will provide separated schema and generate the model, it inlines all named parameter into methods. Is there a way to force the generator create an object that provides multiple different key-value pairs?
I'm generating Rest endpoints including adding Openapi/Swagger annotations to the generated code.
While it works quite well with basic types, I have some problems with custom classes.
Right now I have a lot of duplicate schema entries for the custom classes (using #Schema(implementation = MyClass.class)) but at least the needed information is there. However I'd like to find a way to remove the duplicate schema entries while retaining the additional information.
On a github-issue discussing the $ref and lack of sibling properties I found an example how you would write it manually in yaml in order to get the result I'm looking for, however I can't figure out how to set the annotations to produce it.
This is how I think the annotation should look like if I follow the example (just to be on the safe side it is added to both the getter and the setter):
import io.swagger.v3.oas.annotations.media.Schema;
...
public class SepaPaymentRequest {
...
#Schema(name = "w307BetrBeg", description = "BETRAG BEGUENSTIGTER ", allOf = { com.diesoftware.services.utils.Betrag.class }, required = true)
public void setW307BetrBeg(final Betrag w307BetrBeg) {
this.w307BetrBeg = w307BetrBeg;
}
...
}
However what I get when I fetch the openapi.yaml (snippet):
w307BetrBeg:
$ref: '#/components/schemas/Betrag'
What I'd like to have:
w307BetrBeg:
title: 'Betrag'
description: 'BETRAG BEGUENSTIGTER'
allOf:
- $ref: '#/components/schemas/Betrag'
Any hints are more than welcome.
I haven't found a way to do it using annotations, i.e. by annotating the class.
I think it's possible to do, by:
Creating a model
Injecting the model using a ModelConverter
When I say "a model" I mean an instance of io.swagger.v3.oas.models.media.Schema.
In particular I think you'd want to create and inject a io.swagger.v3.oas.models.media.ComposedSchema instance, which supports allOf.
Doing this (i.e. creating model instances) isn't very different from hand-writing the YAML.
Another possibility -- which I haven't tried -- might be to write a slightly different ModelConverter, which you install into the chain of converters. Then, intercept calls to resolve which return a SchemaObject whose name is Betrag, and (sometimes?) replace that with a ComposedSchema instance which uses allOf.