RicoSuter / NSwag

The Swagger/OpenAPI toolchain for .NET, ASP.NET Core and TypeScript.
http://NSwag.org
MIT License
6.7k stars 1.24k forks source link

InvalidCastException: 'NJsonSchema.JsonSchema' to type 'NSwag.OpenApiResponse'. #4473

Open Panguaneta opened 1 year ago

Panguaneta commented 1 year ago

Hello,

I'm trying to use NSwagStudio with https://[cgm.kiwisat.it/doc/merged-swagger-base.json](https://cgm.kiwisat.it/doc/merged-swagger-base.json) I get the below error: System.InvalidCastException: Unable to cast object of type 'NJsonSchema.JsonSchema' to type 'NSwag.OpenApiResponse'.

I don't know why I got this error. Can anyone help me?

JimHume commented 1 year ago

TL;DR: another URL that reproduces the issue can be found at https://docs.oracle.com/en/industries/food-beverage/simphony/omsstsg2api/swagger.json

More detail:

Using the swagger doc mentioned in the original post one can narrow down the issue to (at least) this path:

"/api_v2/hmi": {
    "post": {
        "description": "send hmi message",
        "requestBody": {
            "content": {
                "application/json": {
                    "schema": {
                        "required": ["assetId", "message", "deliveryType"],
                        "properties": {
                            "assetId": {
                                "description": "Asset receiver (id)",
                                "type": "integer"
                            },
                            "message": {
                                "description": "Message to send",
                                "type": "string"
                            },
                            "deliveryType": {
                                "description": "Force delivery type: '1' (over http) or '2' (send with SMS)",
                                "type": "string"
                            }
                        },
                        "type": "object"
                    }
                }
            }
        },
        "responses": {
            "200": {
                "$ref": "#/components/schemas/InsertEntityResult"
            }
        }
    }
},

That path links to this response:

"InsertEntityResult": {
    "title": "Insert Entity Result Model",
    "description": "Generic response of insert item",
    "properties": {
        "success": {
            "description": "Operation status",
            "type": "boolean",
            "format": "bool"
        },
        "message": {
            "description": "Generic message",
            "type": "string",
            "format": "string"
        },
        "item": {
            "description": "Item definition",
            "type": "object",
            "format": "object"
        },
        "id": {
            "description": "Item id definition",
            "type": "object",
            "format": "integer"
        }
    },
    "type": "object"
}

I am encountering the same issue with a different swagger doc: https://docs.oracle.com/en/industries/food-beverage/simphony/omsstsg2api/swagger.json

This path in particular can reproduce the issue:

"{basePath}/notifications/subscriptions": {
    "post": {
        "tags": [
            "Notifications API"
        ],
        "summary": "Post create a subscription",
        "description": "The API creates a subscription to a resource for the current subscriber.\n",
        "operationId": "CreateSubscription",
        "parameters": [{
                "$ref": "#/parameters/body.SubscriptionViewModel"
            }
        ],
        "responses": {
            "200": {
                "description": "The subscription",
                "schema": {
                    "$ref": "#/definitions/Subscription"
                }
            },
            "400": {
                "description": "400 Bad Request\nNote - A subscription for message type ID 'EmployeesNotification' will return 400 if RvcRef has a value. Such a subscription would never receive any notifications.\n",
                "schema": {
                    "$ref": "#/responses/NotificationsApiBadRequest"
                }
            },
            "403": {
                "$ref": "#/responses/Forbidden"
            },
            "404": {
                "$ref": "#/responses/NotFound"
            }
        },
        "x-internal-id": "basePath}-notifications-subscriptions-post",
        "x-filename-id": "basepath-notifications-subscriptions-post"
    }
}

More specifically: if you remove the "400" response type then it will work just fine. This is the section in question that acts as the starting point:

"400": {
    "description": "400 Bad Request\nNote - A subscription for message type ID 'EmployeesNotification' will return 400 if RvcRef has a value. Such a subscription would never receive any notifications.\n",
    "schema": {
        "$ref": "#/responses/NotificationsApiBadRequest"
    }
}

This "400" links to the "NotificationsApiBadRequest" response:

"responses": {
    "NotificationsApiBadRequest": {
      "description": "400 Bad Request",
      "schema": {
        "$ref": "#/definitions/NotificationsApiProblemDetails"
      }
    }
}

The "NotificationsApiBadRequest" is a reference to the "NotificationsApiProblemDetails" definition:

"NotificationsApiProblemDetails": {
  "description": "Problem details is used as standard model for reporting details when HTTP error status code is returned. This definition is defined by [RFC7807](https://tools.ietf.org/html/rfc7807).\n\nThe content type for this response is `application/problem+json`\n",
  "type": "object",
  "example": {
    "type": "error:validation",
    "title": "Required value not specified.",
    "details": "The OrgShortName value is required.",
    "instance": "required_value_missing"
  },
  "properties": {
    "type": {
      "description": "A URI reference that identifies the problem type. When this member is not present, its value is assumed to be \"about:blank\".",
      "type": "string",
      "example": "error:validation"
    },
    "title": {
      "description": "A short, human-readable summary of the problem type.",
      "type": "string",
      "example": "Required value not specified."
    },
    "details": {
      "description": "A human-readable explanation specific to this occurrence of the problem.",
      "type": "string",
      "example": "The OrgShortName value is required."
    },
    "instance": {
      "description": "A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced.",
      "type": "string",
      "example": "required_value_missing"
    }
  }
}

There is seemingly no obvious link between the originally-reported issue and the issue I am facing. The schema doesn't overlap all that much and no amount of removing or changing the response or definitions seems to get past the issue.

This appears related to the following issues in one way or another, all of which remain in the "open" state: https://github.com/RicoSuter/NSwag/issues/2921 https://github.com/RicoSuter/NSwag/issues/3031 https://github.com/RicoSuter/NSwag/issues/3513 https://github.com/RicoSuter/NSwag/issues/3930 https://github.com/RicoSuter/NSwag/issues/2899

To summarize my issue: "path -> response -> definition" seemingly fails for at least one path but it's not clear what the issue is.

JimHume commented 1 year ago

Minor update: I receive a similar issue when attempting to add a service reference via Visual Studio. My (perhaps incorrect) assumption is that adding the service reference via Visual Studio is using a similar path as NSwag Studio. The stack trace (build output) is as follows:

1>------ Build started: Project: <My Project>, Configuration: Debug Any CPU ------
1>
1>GenerateNSwagCSharp:
1>  dotnet --roll-forward-on-no-candidate-fx 2 "d:\nuget\nswag.msbuild\13.18.2\build\../tools/Net70//dotnet-nswag.dll" openapi2csclient /className:swaggerClient /namespace:<My Project>.Simphony /input:"D:\source\<My Solution>\src\<My Project>\OpenAPIs\swagger.json" /output:"obj\swaggerClient.cs"
1>NSwag command line tool for .NET Core Net70, toolchain v13.18.2.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v13.0.0.0))
1>Visit http://NSwag.org for more information.
1>NSwag bin directory: d:\nuget\nswag.msbuild\13.18.2\tools\Net70
1>System.InvalidCastException: Unable to cast object of type 'NSwag.OpenApiResponse' to type 'NJsonSchema.JsonSchema'.
1>   at NJsonSchema.References.JsonReferenceBase`1.NJsonSchema.References.IJsonReferenceBase.set_Reference(IJsonReference value)
1>   at NJsonSchema.JsonSchemaReferenceUtilities.JsonReferenceUpdater.VisitJsonReferenceAsync(IJsonReference reference, String path, String typeNameHint, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, CancellationToken cancellationToken)
1>   at NJsonSchema.JsonSchemaReferenceUtilities.JsonReferenceUpdater.VisitAsync(Object obj, CancellationToken cancellationToken)
1>   at NJsonSchema.JsonSchemaReferenceUtilities.UpdateSchemaReferencesAsync(Object rootObject, JsonReferenceResolver referenceResolver, IContractResolver contractResolver, CancellationToken cancellationToken)
1>   at NJsonSchema.Infrastructure.JsonSchemaSerialization.FromJsonWithLoaderAsync[T](Func`1 loader, SchemaType schemaType, String documentPath, Func`2 referenceResolverFactory, IContractResolver contractResolver, CancellationToken cancellationToken)
1>   at NSwag.OpenApiDocument.FromJsonAsync(String data, String documentPath, SchemaType expectedSchemaType, Func`2 referenceResolverFactory, CancellationToken cancellationToken) in /_/src/NSwag.Core/OpenApiDocument.cs:line 203
1>   at NSwag.OpenApiDocument.FromFileAsync(String filePath, CancellationToken cancellationToken) in /_/src/NSwag.Core/OpenApiDocument.cs:line 225
1>   at NSwag.Commands.InputOutputCommandBase.GetInputSwaggerDocument() in /_/src/NSwag.Commands/Commands/InputOutputCommandBase.cs:line 46
1>   at NSwag.Commands.CodeGeneration.SwaggerToCSharpClientCommand.RunAsync() in /_/src/NSwag.Commands/Commands/CodeGeneration/OpenApiToCSharpClientCommand.cs:line 259
1>   at NSwag.Commands.CodeGeneration.SwaggerToCSharpClientCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/CodeGeneration/OpenApiToCSharpClientCommand.cs:line 248
1>   at NConsole.CommandLineProcessor.ProcessSingleAsync(String[] args, Object input)
1>   at NConsole.CommandLineProcessor.ProcessAsync(String[] args, Object input)
1>   at NSwag.Commands.NSwagCommandProcessor.ProcessAsync(String[] args) in /_/src/NSwag.Commands/NSwagCommandProcessor.cs:line 61
1>d:\nuget\nswag.apidescription.client\13.18.2\build\NSwag.ApiDescription.Client.targets(34,5): error MSB3073: The command "dotnet --roll-forward-on-no-candidate-fx 2 "d:\nuget\nswag.msbuild\13.18.2\build\../tools/Net70//dotnet-nswag.dll" openapi2csclient /className:swaggerClient /namespace:<My Project>.Simphony /input:"D:\source\<My Solution>\src\<My Project>\OpenAPIs\swagger.json" /output:"obj\swaggerClient.cs" " exited with code -1.
1>Done building project "<My Project>.csproj" -- FAILED.

I'll update the post with a workaround should I find one but I'm quite open to suggestion should someone have any input or feedback.

Minor update: the Swagger editor from SmartBear (https://editor.swagger.io/) fails to produce the C# classes. The document may be of the wrong version as there are parameters that are unresolved. This is a few of the errors that are shown:

Structural error at paths
should only have path names that start with `/`
Jump to line 107
Semantic error at paths.{basePath}/notifications/discovery
Declared path parameter "basePath" needs to be defined as a path parameter at either the path or operation level
Jump to line 108
Semantic error at paths.{basePath}/notifications/registration
Declared path parameter "basePath" needs to be defined as a path parameter at either the path or operation level
Jump to line 131

A workaround is to use the 'vNext' editor (https://editor-next.swagger.io/) which not only renders the document as expected but also produces the C# code ("Generate Client -> csharp-dotnet2").