Swashbuckle - Swagger execute button is not working - asp.net-core-3.1

I am trying to integrate swagger in Asp.Net core 3.1 Web API using Swashbuckle.AspNetCore (5.5.1) with OAS3.
I am having one post method where I need multipart form data (two files and one string value) and for that I have applied below OperationFilter, because I don't want to specify any parameters at action level.
public class ComparePostParamTypes : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var listOfOutputFormats = new List<string> { "Rtf", "Doc", "DocX", "Pdf" };
var optionArray = new OpenApiArray();
optionArray.AddRange(listOfOutputFormats.Select(s => new OpenApiString(s)));
string documentOutputFormatText =
"The format to return";
switch (operation.OperationId)
{
case "File_Post":
operation.Parameters.Clear();
operation.Parameters = new List<OpenApiParameter>
{
new OpenApiParameter
{
Name = "file1", In = ParameterLocation.Query,
Required = true,
Description = "First Document",
Schema = new OpenApiSchema()
{
Type="string",
Format="binary"
}
},
new OpenApiParameter
{
Name = "file2", In = ParameterLocation.Query,
Required = true,
Description = "Second Document",
Schema = new OpenApiSchema()
{
Type="string",
Format="binary"
}
},
new OpenApiParameter
{Name = "outputFormat", In = ParameterLocation.Query, Description = documentOutputFormatText,
Schema = new OpenApiSchema()
{
Type="string",
Enum = optionArray,
Default = new OpenApiString("Rtf"),
}
}
};
break;
}
}
}
This is my controller endpoint
/// <summary>
/// POSTing two documents as a multipart/form-data.
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns>The result in the specified format (see outputFormat parameter)</returns>
/// <remarks>
/// Pass two document and output format</remarks>
/// <response code="200">OK</response>
/// <response code="500">Internal error</response>
/// <response code="403">Forbidden</response>
/// <response code="422">UnprocessableEntity</response>
/// <response code="503">ServiceUnavailable</response>
/// <response code="400">BadRequest</response>
[Produces("application/pdf", "application/msword", "application/zip")]
[Consumes("multipart/form-data")]
[ProducesResponseType(StatusCodes.Status200OK, Type = null)]
[ProducesResponseType(StatusCodes.Status500InternalServerError, Type = null)]
[ProducesResponseType(StatusCodes.Status403Forbidden, Type = null)]
[ProducesResponseType(StatusCodes.Status422UnprocessableEntity, Type = null)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable, Type = null)]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = null)]
[HttpPost(Name ="File_Post")]
public IActionResult Post()
{
var builBoundary = Request.GetMultipartBoundary();
return Ok(builBoundary);
}
Correct Swagger UI is rendered
Swagger UI
But when I clicked on execute button after attaching files nothing happened.
This is generated swagger JSON
{
"openapi": "3.0.1",
"info": {
"title": "Demo",
"version": "v1"
},
"paths": {
"/File": {
"post": {
"tags": [
"File"
],
"summary": "POSTing two documents as a multipart/form-data.",
"description": "Pass two document and output format",
"operationId": "File_Post",
"parameters": [
{
"name": "file1",
"in": "query",
"description": "First Document",
"required": true,
"schema": {
"type": "string",
"format": "binary"
}
},
{
"name": "file2",
"in": "query",
"description": "Second Document",
"required": true,
"schema": {
"type": "string",
"format": "binary"
}
},
{
"name": "outputFormat",
"in": "query",
"description": "The format to return",
"schema": {
"enum": [
"Rtf",
"Doc",
"DocX",
"Pdf"
],
"type": "string",
"default": "Rtf"
}
}
],
"responses": {
"200": {
"description": "OK"
},
"500": {
"description": "Internal error"
},
"403": {
"description": "Forbidden"
},
"422": {
"description": "UnprocessableEntity"
},
"503": {
"description": "ServiceUnavailable"
},
"400": {
"description": "BadRequest"
}
}
}
}
},
"components": { }
}
Please tell me what should I do to fix this.

I am able to fix this by updating OperationFilter
public class ComparePostParamTypes : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var listOfOutputFormats = new List<string> { "Rtf", "Doc", "DocX", "Pdf" };
var optionArray = new OpenApiArray();
optionArray.AddRange(listOfOutputFormats.Select(s => new OpenApiString(s)));
string documentOutputFormatText =
"The format to return";
switch (operation.OperationId)
{
case "File_Post":
var multipartBodyPost = new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Type = "object",
Properties =
{
["file1"] = new OpenApiSchema
{
Description = "First Document",
Type = "string",
Format = "binary"
},
["file2"] = new OpenApiSchema
{
Description = "Second Document",
Type = "string",
Format = "binary"
},
["outputFormat"] = new OpenApiSchema
{
Description = documentOutputFormatText,
Type = "string",
Enum = optionArray,
Default = new OpenApiString("Rtf"),
},
},
Required = { "file1", "file2" }
}
};
operation.RequestBody = new OpenApiRequestBody
{
Content =
{
["multipart/form-data"] = multipartBodyPost
}
};
break;
}
}
}
I more details, check this link https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1782

Related

Swagger Codegen OneOf generating incorrectly

I am generating a JavaClient using an OpenAPISpec document. I have used swagger-codegen 3.0 to generate the code. The OpenAPISpec version is 3.0.1.
Below is the OpenAPI snippet I am facing problems with:
"RequestWithInsuranceInfo": {
"type": "object",
"description": "This request schema will produce a response containing an out of pocket estimate for the given service using the patient's insurance information.",
"additionalProperties": false,
"properties": {
"insuranceInfo": {
"$ref": "#/components/schemas/InsuranceInfo"
},
"service": {
"type": "object",
"additionalProperties": false,
"description": "Schema to use when the patient's benefit info is not given in the request.",
"properties": {
"codes": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ServiceCode"
}
},
"provider": {
"$ref": "#/components/schemas/Provider"
},
"costs": {
"$ref": "#/components/schemas/ServiceCosts"
}
},
"required": [
"codes",
"provider",
"costs"
]
}
}
},
"InsuranceInfo": {
"description": "Information about the payer, plan, and members.",
"additionalProperties": false,
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"title": "Option 1: Patient Is Policy Holder",
"description": "Schema to use when the patient the primary on the insurance plan.",
"properties": {
"payer": {
"$ref": "#/components/schemas/Payer"
},
"policyHolderInfo": {
"$ref": "#/components/schemas/PolicyHolderInfo"
}
},
"required": [
"payer",
"policyHolderInfo"
]
},
{
"type": "object",
"additionalProperties": false,
"title": "Option 2: Patient Is Dependent",
"description": "Schema to use when the patient is a dependent on the insurance plan.",
"properties": {
"payer": {
"$ref": "#/components/schemas/Payer"
},
"dependentMemberInfo": {
"$ref": "#/components/schemas/DependentMemberInfo"
},
"policyHolderInfo": {
"$ref": "#/components/schemas/PolicyHolderInfo"
}
},
"required": [
"payer",
"dependentMemberInfo",
"policyHolderInfo"
]
}
]
},
Below is the code which gets generated:
public class InsuranceInfo implements OneOfInsuranceInfo {
#Override
public boolean equals(java.lang.Object o) {..}
#Override
public int hashCode() {..}
#Override
public String toString() {..}
private String toIndentedString(java.lang.Object o) {..}
}
public interface OneOfInsuranceInfo {
}
public class RequestWithInsuranceInfo implements OneOfRequest {
#SerializedName("insuranceInfo")
private InsuranceInfo insuranceInfo = null;
#SerializedName("service")
private RequestWithInsuranceInfoService service = null;
..
}
public class Payer {
#SerializedName("id")
private String id = null;
..
}
public class PolicyHolderInfo {
#SerializedName("memberId")
private String memberId = null;
#SerializedName("firstName")
private String firstName = null;
#SerializedName("lastName")
private String lastName = null;
#SerializedName("dateOfBirth")
private LocalDate dateOfBirth = null;
..
}
public class DependentMemberInfo {
#SerializedName("memberId")
private String memberId = null;
#SerializedName("firstName")
private String firstName = null;
#SerializedName("lastName")
private String lastName = null;
#SerializedName("dateOfBirth")
private LocalDate dateOfBirth = null;
..
}
As shown, the InsuranceInfo object implements the OneOfInsuranceInfo interface but has no variables. Payer, PolicyHolderInfo and dependentMemberInfo class are generated but they are not linked to the InsuranceInfo class anyhow. How do I populate the InsuranceInfo class?
The issue is probably that the InsuranceInfo schema
"InsuranceInfo": {
"description": "Information about the payer, plan, and members.",
"additionalProperties": false,
"oneOf": [
{ ... },
{ ... }
]
}
effectively disallows ALL properties. This is because additionalProperties: false only knows about the properties defined directly alongside it and has no visibility into oneOf subschemas.
To resolve the issue, you can rewrite the InsuranceInfo schema without oneOf, as follows. This schema is basically "Option 2" from the original schema, except the dependentMemberInfo property is defined as optional.
"InsuranceInfo": {
"description": "Information about the payer, plan, and members.",
"additionalProperties": false,
"type": "object",
"required": [
"payer",
"policyHolderInfo"
],
"properties": {
"payer": {
"$ref": "#/components/schemas/Payer"
},
"dependentMemberInfo": {
"$ref": "#/components/schemas/DependentMemberInfo"
},
"policyHolderInfo": {
"$ref": "#/components/schemas/PolicyHolderInfo"
}
}
}

Parse nested json failed flutter

I have this json response that contains servicetype,vehiclemodels,accessories,working status as array but i cannot access any of the class,when i print print(obj); it just shows null
Json file
{
"success": true,
"message": "",
"data": {
"service_types": [
{
"id": 1,
"name": "Car Wash"
},
{
"id": 2,
"name": "Full Body Wash"
},
{
"id": 3,
"name": "Polishing"
},
{
"id": 4,
"name": "Engine Oil Checkup"
},
],
"vehicle_models": [
{
"id": 1,
"name": "SUV"
}
],
"accessories": [
{
"id": 2,
"name": "Mat Cover"
},
{
"id": 4,
"name": "Cash"
},
{
"id": 5,
"name": "Engine Room washing"
}
],
"working_statuses": [
{
"id": 0,
"name": "Registered"
},
{
"id": 1,
"name": "Work started"
},
{
"id": 2,
"name": "Work completed"
},
{
"id": 5,
"name": "Cancelled"
}
]
}
}
Code for extracting data
var response = await http.post(Uri.parse(Urls.DasboardData),
headers: {"Content-Type": "application/json"},
body: json.encode({
"vehicle_models": "",
"service_types": "",
"station_id":Station_id,
"accessories": "",
"working_statuses": "",
}));
Map<String, dynamic> value = json.decode(response.body);
var data = value['data'];
if (data.length > 0) {
for (int i = 0; i < data.length; i++) {
var obj = data[i];
print(obj);
var service_type_obj = obj['service_types'];
var vehicle_models_obj = obj['vehicle_models'];
var accessories_obj = obj['accessories'];
var working_statuses_obj = obj['working_statuses'];
DataList.add(GetAllModel(
service_type_obj['id'],
service_type_obj['name'],
vehicle_models_obj['id'],
vehicle_models_obj['name'],
accessories_obj['id'],
accessories_obj['name'],
working_statuses_obj['id'],
working_statuses_obj['name'],
));
}
setState(() {
print("UI UPDATED");
});
}
else
{
}
Model
class GetAllModel {
String service_type_id,
service_type_name,
vehicle_models_id,
vehicle_models_name,
accessories_id,
accessories_name,
working_statuses_id,
working_statuses_name;
GetAllModel(
this.service_type_id,
this.service_type_name,
this.vehicle_models_id,
this.vehicle_models_name,
this.accessories_id,
this.accessories_name,
this.working_statuses_id,
this.working_statuses_name,
);
}
The problem is at the time of iterating your information, the data size is 4, but this It contains 4 objects, therefore you will not be able to access data[i] because data[i] does not exist, if it exists data ['service_types'] which is a list and here if you could iterate the information that this contains object.
So to be able to access the information of your objects you must iterate them like this:
for (var i = 0; i < data['service_types'].length; i++) {
print(data['service_types'][i]);
}
and the same for the other objects.
An alternative to iterate:
for (var item in data.values) {
print(item);
}
You need to change these:
var service_type_obj = obj['service_types'];
var vehicle_models_obj = obj['vehicle_models'];
var accessories_obj = obj['accessories'];
var working_statuses_obj = obj['working_statuses'];
into this:
var service_type_obj = obj['service_types'][0];
var vehicle_models_obj = obj['vehicle_models'][0];
var accessories_obj = obj['accessories'][0];
var working_statuses_obj = obj['working_statuses'][0];
You're objects are located inside a list, to access them, you have to use an index, like[0].

How to document complex objects in Apache Camel OpenAPI documentation using camel-openapi-java instead of camel-swagger-java component?

I am working on trying to correctly document our rest endpoints. As an example to get this working I created a sample "Healthcheck getStatus()" endpoint which is returning an object called "EndpointStatus" which has 3 fields (class is below). I was able to get this object documenting correctly and using the camel-swagger-java component and the below rest configuration / definition;
restConfiguration()
.apiContextPath(apiContextPath)
.apiProperty("api.title", "Camel Service").apiProperty("api.version", "1.0.0")
// and enable CORS
.apiProperty("cors", "true");
rest()
.path("/healthcheck")
.description("Health Check REST service")
.get("getStatus/{endpointName}")
.param()
.name("endpointName")
.type(RestParamType.path)
.allowableValues(
Stream.of(EndpointName.values())
.map(EndpointName::name)
.collect(Collectors.toList()))
.required(true)
.endParam()
.description("Get Camel Status")
.id("getStatus")
.outType(EndpointStatus.class)
.bindingMode(RestBindingMode.auto)
.responseMessage().code(200).message("Returns an EndpointStatus object representing state of a camel endpoint").endResponseMessage()
.to(CAMEL_STATUS_URI);
Here are the annotations I used on this class:
#ApiModel(description = "Endpoint Status Model")
public class EndpointStatus {
private boolean isAvailable;
private EndpointName name;
private long timestamp;
#ApiModelProperty(value = "Is the endpoint available", required = true)
public boolean isAvailable() {
return isAvailable;
}
public void setAvailable(boolean available) {
isAvailable = available;
}
#ApiModelProperty(value = "The name of the endpoint", required = true)
public EndpointName getName() {
return name;
}
public void setName(EndpointName name) {
this.name = name;
}
#ApiModelProperty(value = "The timestamp the endpoint was checked", required = true)
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
}
Along with the generated swagger documentation:
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Camel Service"
},
"host": "localhost:9000",
"tags": [
{
"name": "healthcheck",
"description": "Health Check REST service"
}
],
"schemes": [
"http"
],
"paths": {
"/healthcheck/getStatus/{endpointName}": {
"get": {
"tags": [
"healthcheck"
],
"summary": "Get Camel Status",
"operationId": "getStatus",
"parameters": [
{
"name": "endpointName",
"in": "path",
"required": true,
"type": "string",
"enum": [
"ENDPOINTA",
"ENDPOINTB"
]
}
],
"responses": {
"200": {
"description": "Returns an EndpointStatus object representing state of a camel endpoint",
"schema": {
"$ref": "#/definitions/EndpointStatus"
}
}
}
}
}
},
"definitions": {
"EndpointStatus": {
"type": "object",
"required": [
"available",
"name",
"timestamp"
],
"properties": {
"name": {
"type": "string",
"description": "The name of the endpoint",
"enum": [
"ENDPOINTA",
"ENDPOINTB"
]
},
"timestamp": {
"type": "integer",
"format": "int64",
"description": "The timestamp the endpoint was checked"
},
"available": {
"type": "boolean",
"description": "Is the endpoint available"
}
},
"description": "Endpoint Status Model"
}
}
}
However, when trying to move to use camel-openapi-java which supports OpenAPI Specification v3 with the same setup I am getting EndpointStatus without any fields / descriptions in my documentation.
{
"openapi": "3.0.2",
"info": {
"title": "SurePath Camel Service",
"version": "1.0.0"
},
"servers": [
{
"url": ""
}
],
"paths": {
"/healthcheck/getStatus/{endpointName}": {
"get": {
"tags": [
"healthcheck"
],
"parameters": [
{
"name": "endpointName",
"schema": {
"enum": [
"ENDPOINTA",
"ENDPOINTB"
],
"type": "string"
},
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "Returns an EndpointStatus object representing state of a camel endpoint"
}
},
"operationId": "getStatus",
"summary": "Get Camel Status",
"x-camelContextId": "camel-1",
"x-routeId": "getStatus"
}
},
"/healthcheck/isAvailable": {
"get": {
"tags": [
"healthcheck"
],
"responses": {
"200": {
"description": "Returns status code 200 when Camel is available"
}
},
"operationId": "verb1",
"summary": "Is Camel Available",
"x-camelContextId": "camel-1",
"x-routeId": "route4"
}
}
},
"components": {
"schemas": {
"EndpointStatus": {
"type": "EndpointStatus",
"x-className": {
"format": "com.sample.bean.EndpointStatus",
"type": "string"
}
}
}
},
"tags": [
{
"name": "healthcheck",
"description": "Health Check REST service"
}
]
}
I have tried adding this into my responseMessage and it is still not documenting correctly;
responseMessage().code(200).responseModel(EndpointStatus.class).message("Returns an EndpointStatus object representing state of a camel endpoint").endResponseMessage()
Do I need different annotations / RestDefinition config to get this EndpointStatus class appearing correctly in the OpenAPI documentation?
This looks to be an issue at the moment with the camel-openapi-java component; waiting for a resolution from this jira https://issues.apache.org/jira/browse/CAMEL-15158

How to pick up data from json with multiple level with scala

I want to pick up data from the json, but i can not code it in scala, i can only write it in php, because it is very simple to use, but i have no idea how can i do the same thing in scala. please help me out.
{
"home": {
"type": "literal",
"options": {
"route": "\/",
"defaults": {
"controller": "Apiv1\\Controller\\Index",
"action": "index"
}
}
},
"praise": {
"type": "literal",
"options": {
"route": "\/apiv1\/praise",
"defaults": {
"controller": "Apiv1\\Controller\\Praise",
"action": "index"
}
},
"may_terminate": true,
"child_routes": {
"status": {
"type": "literal",
"options": {
"route": "\/status",
"defaults": {
"action": "status"
}
}
}
}
},
"admin": {
"type": "literal",
"options": {
"route": "\/admin",
"defaults": {
"controller": "Admin\\Controller\\Index",
"action": "index"
}
},
"may_terminate": true,
"child_routes": {
"routes": {
"type": "literal",
"options": {
"route": "\/routes",
"defaults": {
"controller": "Admin\\Controller\\Routes",
"action": "index"
}
},
"may_terminate": true,
"child_routes": {
"list": {
"type": "literal",
"options": {
"route": "\/list",
"defaults": {
"action": "list"
}
}
}
}
}
}
}
}
I want to pick up the route field from the json, and i would like to list all routes
here is my php version function
function parseRoute($a, $pre = '', &$urls)
{
foreach ($a as $k => $v) {
$route = $pre . $v['options']['route'];
$urls[] = $route;
if (isset($v['child_routes']) && is_array($v['child_routes'])) {
$this->parseRoute($v['child_routes'], $route, $urls);
}
$route = null;
}
}
$urls = array();
var_dump(parseRoute(json_decode($data), '', $urls));
The best thing to do is to use one of the existing JSON libraries for Scala. Here is related question describing your options:
What JSON library to use in Scala?
Next is an example of using Lift-JSON. See the link, there are many examples. Here is what you can do:
import net.liftweb.json._
val rawJson = """{
"home":{
"type":"literal",
"options":{
"route":"/",
"defaults":{
"controller":"Apiv1\\Controller\\Index",
"action":"index"
}
}
},
"praise":{
"type":"literal",
"options":{
"route":"/apiv1/praise",
"defaults":{
"controller":"Apiv1\\Controller\\Praise",
"action":"index"
}
},
"may_terminate":true,
"child_routes":{
"status":{
"type":"literal",
"options":{
"route":"/status",
"defaults":{
"action":"status"
}
}
}
}
},
"admin":{
"type":"literal",
"options":{
"route":"/admin",
"defaults":{
"controller":"Admin\\Controller\\Index",
"action":"index"
}
},
"may_terminate":true,
"child_routes":{
"routes":{
"type":"literal",
"options":{
"route":"/routes",
"defaults":{
"controller":"Admin\\Controller\\Routes",
"action":"index"
}
},
"may_terminate":true,
"child_routes":{
"list":{
"type":"literal",
"options":{
"route":"/list",
"defaults":{
"action":"list"
}
}
}
}
}
}
}
}"""
val json = parse(rawJson)
val routeList = json \\ "route"
// which returns a structure like this:
JObject(List(JField(route,JString(/)), JField(route,JString(/apiv1/praise)), JField(route,JString(/status)), JField(route,JString(/admin)), JField(route,JString(/routes)), JField(route,JString(/list))))
// which is a JSON object
// if you want a list of routes as strings as in your example extract them this way now:
val routeList: List[String] = routeObject \\ classOf[JString]
// which returns:
List[String] = List(/, /apiv1/praise, /status, /admin, /routes, /list)
Note that I'm assuming that routes can be at any level of nesting in JSON doc (you might want to enforce certain level or parent of the 'route'). I also assume that all routes are Strings and ignore the ones which are not - if you want more options you can pattern match on the type of route using derivative classes of JValue: JArray, JString, JObject, etc.

RestFB parsing many objects into one incorrectly

My restFB code results in writing on one App to my ArrayList. I need the arraylist to fill out properly with the number of appIds return in JSON. Any insights?
Here is my code:
public List<String> fetchAppIdsForUser() {
Connection<FacebookApp> appList = getClient().fetchConnection("me/applications/developer", FacebookApp.class);
List<String> appIds = new ArrayList<String> ();
for (List<FacebookApp> appListTwo : appList) {
for (FacebookApp app : appListTwo) {
appIds.add(app.getAppId());
}
}
return appIds;
}
And here is what is returned in JSON:
{
"data": [
{
"name": "x",
"namespace": "x",
"id": "x"
},
{
"name": "xx",
"namespace": "xx",
"id": "xx"
},
{
"name": "xxx",
"namespace": "xxxx",
"id": "xxxxx"
},
{
"name": "xxxx",
"namespace": "xxxxx",
"id": "xxxxx"
}
],
"paging": {
"next": "https://graph.facebook.com/xxxxx/applications?type=developer&format=json&access_token=XXXXXXXX"
}
}
I solved it using the following:
public List<String> fetchAppIdsForUser() {
Connection<FacebookApp> appList = getClient().fetchConnection("me/applications/developer", FacebookApp.class);
List<FacebookApp> list = appList.getData();
ArrayList<String> appNames = new ArrayList<String>();
for (FacebookApp app: list) {
appNames.add(app.getName());
}
return appNames;
}