Swagger/ Swashbuckle nested responses not showing up correctly - swagger

I'm trying to upgrade from Swashbuckle 4 to 5 and swagger 1.2 to 2.
The issue I'm having is the response schema isn't showing up correctly if the response has another object within it.
While using Swagger 1.2 my Model Schema looked like this:
{
"Id": "",
"FirstName": "",
"LastName": "",
"Address": {
"StreetNumber": "",
"City": "",
"PostalCode": ""
},
"Preferences": [
{
"Name": "",
"Brand": "",
"MarketingName": ""
}
]
}
And the model was:
Customer {
Id (string, optional),
FirstName (string, optional),
LastName (string, optional),
Address(Address, optional),
Preferences(array[Preference], optional)
}
Address {
StreetNumber (string, optional),
City (string, optional),
PostalCode (string, optional)
}
Preference{
Name (string, optional),
Brand (string, optional),
MarketingName (string, optional),
}
Now the model schema is:
{
"Id": "",
"FirstName": "",
"LastName": "",
"Preferences": [
null,
]
}
And the model is:
Customer {
Id (string, optional),
FirstName (string, optional),
LastName (string, optional),
Address(#/definitions/Address, optional),
Preferences(Array[#/definitions/Preference, optional)
}
The JSON hasn't really changed. From swagger 1.2 to 2.0 the response type has moved from type to responses ref but the JSON appears to be correct and if Preferences or address isn't nested within Customer it appears correctly in the UI.
How can I get the models for Address and preference to appear in the Customers model?

That actually has nothing to do with Swashbuckle but rather swagger-ui. By the looks of it, it's a possible bug. What you can do to test it is get the latest version of swagger-ui directly from its repository, and try running it against the Swagger definition that's produced from Swashbuckle. The URL is described at the getting started section - https://github.com/domaindrivendev/Swashbuckle#getting-started.
If the problem is solved, you can ask the Swashbuckle's author to upgrade the UI version. If not, I'd recommend opening a ticket directly on swagger-ui and it will be addressed.

Related

Displaying enum values together with a referenced class in Swagger using Swashbuckle

I am using Swashbuckle to generate Swagger output. On my model I the following property:
public FieldValue? MyProperty { get; set; }
FieldValue is a simple class that can hold an ID and a name:
public class FieldValue
{
/// <summary>
/// The numerical represenation of the value.
/// </summary>
/// <example>1</example>
public string Id { get; set; }
/// <summary>
/// The textual represenation of the value.
/// </summary>
/// <example>SomeValue</example>
public string Name { get; set; }
}
Via some business logic in our code, a set of possible key-value pairs (ID and name) are mapped to this property.
Now, I would like to show all the possible values for MyProperty in the enum tag in my Swagger output. Using an implementation of ISchemaFilter, a custom attribute on the MyProperty property and using allOf instead of a simple ref, I have managed to add the values in my JSON output, see below. The reason for using allOf instead of just ref is that ref will overwrite all properties on the referencing schema. So, when I use allOf it keeps my description and enum fields in the document.
{
"components": {
"schemas": {
"FieldValue": {
"type": "object",
"properties": {
"Id": {
"type": "string",
"description": "The numerical represenation of the value.",
"nullable": true,
},
"Name": {
"type": "string",
"description": "The textual represenation of the value.",
"nullable": true,
}
}
},
"MyProperty": {
"enum": [
"1: EnumValue1",
"2: EnumValue2",
"3: EnumValue3"
],
"allOf": [{
"$ref": "#/components/schemas/FieldValue"
}
],
"description": "Some description",
"nullable": true
}
}
}
This does not show the enum-values in the Swagger UI, though. It does show the description.
Is this Swagger doc valid? If not, how can I make it valid and display the enums for MyProperty?
The correct representation of your use case is:
"MyProperty": {
"enum": [
{"Id": "1", "Name": "EnumValue1"},
{"Id": "2", "Name": "EnumValue2"},
{"Id": "3", "Name": "EnumValue3"}
],
"allOf": [
{ "$ref": "#/components/schemas/FieldValue" }
],
"description": "Some description",
"nullable": true
}
However, there's little to no tooling support for enums containing object literals. I suggest mentioning the possible Id and Name values in the description of the MyProperty property.

How can I get OpenApi-Generator to produce nullable DateTimeOffset?

My API exposes some endpoints with DateTime? and others with DateTimeOffset?.
I want to generate an API Client using OpenApi-Generator, that will create client code for each endpoint, respecting the differences between the types.
OpenApi-Generator offers the option to useDateTimeOffset=true, which will generate the client using DateTimeOffset everywhere regardless of whether the API was exposing DateTime or DateTimeOffset. As such, I don't want to use that.
So to resolve the requirement without that, I'm attempting to create a Filter in which I will specify that in the case of DateTimeOffset? the generator should use the type DateTimeOffset and make it nullable.
This is the Filter:
public class FixDateTimeOffsetTypeSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema model, SchemaFilterContext context)
{
if (context.Type == typeof(DateTimeOffset?))
{
model.Format = "date-time-offset-nullable";
model.Type = "DateTimeOffset";
model.Nullable = true;
}
}
}
and this is an example of the output from that:
"/Api/MyApiEndpoint": {
"get": {
"tags": [
"General"
],
"operationId": "General_MyApiEndpoint",
"parameters": [
{
"name": "startDatetime",
"in": "query",
"schema": {
"type": "DateTimeOffset",
"format": "date-time-offset-nullable",
"nullable": true
}
},
When I run this and generate the client from that JSON, the the DateTimeOffset objects are never nullable. It doesn't seem to respect that instruction.
What do I need to change to make it work, so that when I specify DateTimeOffset should be nullable, it appears as nullable in the code?
It looks like this is a known problem:
https://github.com/OpenAPITools/openapi-generator/pull/3530
And here is the pull request that solves it: https://github.com/OpenAPITools/openapi-generator/pull/3530/commits/89fd5d70e9f15a5be9ffe26c2beddc07770043c0
Also check this link: https://jane.readthedocs.io/en/latest/tips/nullable.html

Adding Example RequestBody in swagger micronaut

I generated swagger for micronaut using the instructions provided in https://micronaut-projects.github.io/micronaut-openapi/latest/guide/index.html
So I have a controller method like:
#Consumes("application/vnd.api+json")
#Produces("application/vnd.api+json")
#Post("/{id}/users")
#RequestBody
public HttpResponse addAndAssignTarget(#PathVariable("id") Long projectId, #Body #Parameter() JsonNode user) {
I am not using a POJO for adding users for another reason which is out of context for this question. Thus, the generated swagger ui shows {} as example for request body. I would like to change this to something like. How can I do this?
{
"data" : {
"type": "projects",
"attributes": {
"name": "some-name1",
"description": "some-description",
"partner_company": "some-compnay"
}
}
}
It looks much better if you use POJO. However, you can add an example as string, but the problem with that if you have any nested objects the quotation marks won't look nice (will be escaped "):
#Consumes("application/vnd.api+json")
#Produces("application/vnd.api+json")
#Post("/{id}/users")
#RequestBody
public HttpResponse addAndAssignTarget(#PathVariable("id") Long projectId,
#Body #RequestBody(content = #Content(schema = #Schema(example = "[0, 2, 3]"))) JsonNode user){
}
Also it is not a Paramater, but a #Body and #RequestBody for annotation purposes.
Output will be:
"[0, 2, 3]"

Custom Web API Formatting

I've been playing around with asp.net web api, and I noticed that default generated returned json doesn't include the object level key. I was also wondering how to customize the output of the json string. For example:
Getting a User usually returns
{
"Name": "ChaoticLoki",
"Age": 22,
"Sex": "Male"
}
I was hoping I could return something like:
{
"data": {
"Name": "ChaoticLoki",
"Age": 22,
"Sex": "Male",
},
"success": true
}
You can then create a class wrapping the data and status like this
public class JsonResult{
public object data { get; set;}
public boolean success { get; set;}
}
And then in your Web Api methods, you can do
var data = ... //getting from repo
return Json(new JsonResult{data = data, success = true});

Breeze is not deserializing entity change sets when saving changes

I am using Breeze against a standard RPC style WebAPI. I have decorated the controller with the [BreezeController] attribute. I have defined entity metadata on the client for the entities being returned by the WebAPI actions. My Breeze DataService is setup as follows:
var dataService = new breeze.DataService({
serviceName: "http://localhost/api/PartReceiptPurchaseOrders",
hasServerMetadata: false
});
When calling the EntityManager's SaveChanges method after modifying an entity, the EntityInfo object on the server is empty. It appears the serialized entity passed to the SaveChanges method is not being deserialized properly into the expected entity on the server. I'm having a hard time understanding what I'm doing wrong.
When I inspect the JObject saveBundle argument passed to the SaveChanges method on the WebAPI controller, I get the expected entity details:
{
"entities": [{
"PurchaseOrderPartId": 1,
"PartNumber": "ABC",
"SupplierPartNumber": "12345",
"Description": "Some Part",
"Bin": "1",
"Qty": 24,
"QtyReceived": 24,
"QtyBackordered": 0,
"Cost": 60,
"Currency": "USD",
"PurchaseOrderId":1,
"entityAspect": {
"entityTypeName": "PurchaseOrderPart:#MyApp.Models",
"entityState": "Modified",
"originalValuesMap": {
"QtyReceived": 0
},
"autoGeneratedKey":{
"propertyName": "PurchaseOrderPartId",
"autoGeneratedKeyType": "Identity"
}
}
}],
"saveOptions": { "allowConcurrentSaves": false }
}
However, after the call to the base class method Breeze.WebApi.ContextProvider.SaveChanges() the entityInfo.Entity property contains an empty object as follows:
entityInfo {Breeze.WebApi.EntityInfo}
AutoGeneratedKey: null {Breeze.WebApi.AutoGeneratedKey}
Entity {MyApp.Models.PurchaseOrderPart}
Bin: null
Cost: 0
Currency: null
Description: null
PartNumber: null
PurchaseOrder: null {MyApp.Models.PurchaseOrder}
PurchaseOrderId: 0
PurchaseOrderPartId: 0
Qty: 0
QtyBackordered: 0
QtyReceived: 0
SupplierPartNumber: null
If I breakpoint into the CreateEntityInfoFromJson in the Breeze.WebApi.ContextProvider class, I see that the call to jsonSerializer.Deserialize(new JTokenReader(jo), entityType) sets entityInfo.Entity to an empty entity object. There is no error raised during the deserialization so I can't tell why this is happening.
Can anyone point me towards a possible resolution?
Thanks,
Richard
Ok, I figured this out and it was a dumb mistake on my part. My entity type on the server had been declared with internal setters like public decimal QtyReceived { get; **internal** set; }. This meant the JSON deserializer couldn't set the property value. Interestingly enough, the error is just ignored by the deserilizer.
Changing the setters to be public fixed this issue.

Resources