How to declare a $ref property as readOnly in OpenAPI (Swagger)? - swagger

I am trying to add a read-only field for 'House' in this example. The house is another model that I want to be read-only.
In this example, the array of Dogs can be set to readOnly without an error, but when I set the single definition of House to readOnly I get the following warning in the Swagger Editor:
Sibling values are not allowed alongside $refs
I understand that this is because everything in the model is inherited here. So how do I define that the write API calls cannot define a 'House' in this endpoint whilst also allowing House to be created and updated in another API endpoints?
Pets:
properties:
id:
type: string
example: AAAAE12-1123AEF-1122312123
readOnly: true
name:
type: string
example: My Default Name
text:
type: string
example: My Default Text
Dogs:
type: array
readOnly: true
items:
$ref: '#/definitions/Dog'
House:
readOnly: true
$ref: '#/definitions/House'

OpenAPI 3.1
In OAS 3.1, schema definitions support sibling keywords alongside $ref:
House:
$ref: '#/components/schemas/House'
readOnly: true
OpenAPI 3.0 and 2.0
Sibling keywords alongside $ref are ignored. The workaround is to use allOf to combine a $ref with other attributes:
House:
readOnly: true
allOf:
- $ref: '#/definitions/House'

I just have found the result and want to share with you like below, you can use readyonly attribute to hide any field:
Java code:
#ApiModelProperty(example = "1", readOnly = true, value = "User status")
public String getUserStatus() { return userStatus; }
Swagger:
enter image description here

Related

Override property definition in derived type in OpenAPI

dears,
In my OpenAPI schema, I have a base type UserBase, where the property manager is a string.
now I want to create a RealUser type derived from UserBase and want to override its property manager to be a custom type instead scalar.
UserBase:
title: User Base
properties:
name:
description: User name
type: string
manager:
description: Manager
type: string
UserRef:
title: UserRef
type: object
properties:
id:
description: User ID
type: string
example: e58ed763-928c-4155-bee9-fdbaaadc15f3
name:
description: User name
type: string
example: Jon Snow
RealUser:
title: Real User
required:
- name
allOf:
- $ref: #/components/schemas/UserBase
- properties:
manager:
description: Reference to Manager
allOf:
- $ref: #/components/schemas/UserRef
Swagger validator does not show any error, however the original documentation says the following:
It is recommended to avoid using conflicting properties (like properties that have the same names, but different data types)
So, my question - is it legal to do such override?
Any kind of clarification is appreciated!

How to document JSON Merge Patch endpoints in OpenAPI?

I have implemented a REST API which is documented via OpenAPI. In detail, the specification is generated from Java source code using springdoc-openapi.
However, I have the need for accepting patches. So, I
/customers/{id}:
patch:
tags:
- Customers
summary: Updates an existing user
description: Updates an existing user
operationId: partialUpdateMergePatchCustomer
parameters:
- name: id
in: path
description: The numeric ID of the customer
required: true
schema:
type: integer
format: int64
requestBody:
content:
application/merge-patch+json:
schema:
type: object
required: true
I have read through the OpenAPI specification/documentation but did not find any information regarding JSON Merge Patch, JSON Patch or the like.
I am experiencing the following issues I'd like to overcome:
There is no clue information of which type of resource is to be patched here. The rest of the API, esp. in SwaggerUI, is well documented and has schema references everywhere. How can I specify the resources schema that actually is to be patched?
Apart from the pure schema reference, in my API, some resources have read-only fields or there may be other restrictions that apply to certain fields that I'd like to document. Is there a better way as doing it in natural language as part of the original schema?
I had the same issue as your issue #1. That is, providing more info to swagger on the JsonPatch RequestBody parameter than the default generated by springdoc-openapi. I'm posting my solution here for others who may run into the same issue.
My solution was to add the following annotation to the RestController patch method:
#PatchMapping(value = "/{id}", consumes = "application/json-patch+json")
#io.swagger.v3.oas.annotations.parameters.RequestBody(content = #Content(array = #ArraySchema(schema = #Schema(implementation = JsonPatchSchema.class))))
public void patch(#PathVariable #Min(0) long id, #RequestBody JsonPatch patch) {
Where JsonPatchSchema contains the requested format:
public class JsonPatchSchema {
#NotBlank
public Op op;
public enum Op {
replace, add, remove, copy, move, test
}
#NotBlank
#Schema(example = "/name")
public String path;
#NotBlank
public String value;
}
Using the #Schema annotation you could provide more detail on what is allowed, see the Schema javadoc. For example the pattern parameter allows you to define a regular expression. I haven't found a way to generate the allowed content unfortunately.

Using Swagger Annotations to generate XML namespace information

My XML request looks like this:
<ns1: MyRequest ns1="http://example.com/api/2.0">:
<reference>A request to my endpoint</reference>
</ns1:MyRequest>
I believe it should be modeled in Swagger/OpenAPI 3 spec as follows:
MyRequest:
type: object
properties:
reference:
type: string
xml:
name: reference
xml:
prefix: ns1
namespace: https://example.com/api/2.0
Is it possible to annotate my MyRequest.java class appropriately using JAXB or Swagger annotations to get the prefix and namespace entries in the generated OpenAPI spec?
I tried setting the namespace attribute on the #XmlRootElement as below, but the namespace is not appearing in the generated OpenAPI spec. Further, I can't use that the XmlRootElement annotation to specify the prefix.
#XmlRootElement(namespace = "https://example.com/api/2.0")
public class MyRequest{
//omitted
}

Cannot serialize to Delta<> in an OData Controller

I'm implementing an ODataController. It's OData V3 for compatibility reasons with Breeze.js:
using System.Web.Http.OData;
public class OffersController : ODataMetadataController
{
...
Somwhere in the middle I want to implement merge/patch as seen in examples:
[AcceptVerbs("PATCH", "MERGE")]
public IHttpActionResult Patch([FromODataUri] int key, Delta<BOOffer> delta)
{
...
For some reason I'm getting the following error:
No MediaTypeFormatter is available to read an object of type 'Delta`1' from content with media type 'application/json'.;
Ok. Delta<> is OData related, I would need an OData formatter for that.
Iterating through the formatters (as on this page), it does not seem to be an OData formatter there:
JsonMediaTypeFormatter
CanReadType: True
CanWriteType: True
Base: BaseJsonMediaTypeFormatter
Media Types: application/json, text/json
XmlMediaTypeFormatter
CanReadType: True
CanWriteType: True
Base: MediaTypeFormatter
Media Types: application/xml, text/xml
FormUrlEncodedMediaTypeFormatter
CanReadType: False
CanWriteType: False
Base: MediaTypeFormatter
Media Types: application/x-www-form-urlencoded
JQueryMvcFormUrlEncodedFormatter
CanReadType: True
CanWriteType: False
Base: FormUrlEncodedMediaTypeFormatter
Media Types: application/x-www-form-urlencoded
Should I register this formatter? Shouldn't it be automatic? If I need to register it manually, how?
If I change the input parameter form Delta<BOOffer> to BOOffer the method gets called, but since only the changed properties are sent, this is not something I can use.
I configure my controller in app_start like this:
System.Web.Http.OData.Builder.ODataConventionModelBuilder builderV3 = new System.Web.Http.OData.Builder.ODataConventionModelBuilder();
var entitySetConfigV3 = builderV3.EntitySet<BOOffer>("Offers");
entitySetConfigV3.EntityType.HasKey(o => o.ID);
config.Routes.MapODataServiceRoute(
routeName: "odata/v3",
routePrefix: "odata/v3",
model: builderV3.GetEdmModel(),
batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));
The reason for this was referencing both System.Web.Http.OData (odatav3) and System.Web.OData (odatav4) in the project and mixing up references.
the System.Web.Http.OData.Formatter.ODataMediaTypeFormatter is not configured to be able to serialize into System.Web.OData.Delta<T>.
Using to System.Web.Http.OData.Delta<T> worked as intended.
Be careful about referencing different OData versions in one projects.

How do you properly bind a list of objects into a Grails command?

I'm trying to figure out how to deserialize and validate nested objects in JSON request into a Grails 2.1.1 command object.
Currently I have a command object in my controller that has a few basic properties and then list of domain objects,
protected static class CustomCommand {
String name
String description
List<DomainObject> objs
}
And a JSON body to my POST request,
{
name: 'test name',
description: 'test description',
objs: [
{
name: 'test sub object',
description: 'test description'
}
]
}
I'm seeing the command object created with an empty array. Any idea how I can get the sub objects my in JSON body to deserialize into the command object and then validate them?
Previously I've worked around this by manually creating an object from the parameter map and validating that directly, but that feels like a workaround that isn't taking advantage of everything Grails offers.
We had a similiar issue with binding post data to a list inside a command. Our workaround that worked was to define a default value for collection elements:
class MyCommand {
List<MyClass> items= [].withLazyDefault {
new MyClass()
}
}
After that the post data was correctly bound to the list. I think the reason is that Groovy ignores the generic type parameter of the list and does not know which object to instantiate at runtime.
I'am not sure if this works in your case but it might be worth a try
Update:
I used this a few minutes ago:
public static class MyCommand {
String foo
List<Bar> bars
public String toString() {
return "foo: " + foo + ", bars: " + bars
}
}
public static class Bar {
String baz
}
controller method:
def test() {
println new MyCommand(request.JSON)
}
I posted some json using jquery:
$.ajax({
type: "POST",
url: '...',
data: JSON.stringify({
'foo': '12345',
bars: [
{baz: '1'},
{baz: '2'}
]
}),
contentType : 'application/json',
});
The output printed by the controller:
foo: 12345, bars: [[baz:1], [baz:2]]
So it seems to work :o
I never got this to work under Grails 2.1.1, but apparently this was fixed in Grails 2.3,
Binding Request Body To Command Objects
If a request is made to a controller action which accepts a command object and the request includes a body, the body will be parsed and used to do data binding to the command object. This simplifies use cases where a request includes a JSON or XML body (for example) that can be bound to a command object. See the Command Objects documentation for more details.

Resources