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

System.InvalidCastException: Unable to cast object of type 'NJsonSchema.JsonSchema' to type 'NSwag.OpenApiResponse'. #2921

Open csmorley opened 4 years ago

csmorley commented 4 years ago

Hi all, I am using NSwagStudio with https://polygon.io/docs/swagger.json and I get the below error:

System.InvalidCastException: Unable to cast object of type 'NJsonSchema.JsonSchema' to type 'NSwag.OpenApiResponse'.

This is generated from swagger endpoint, so not sure what the issue is?

Runtime: NetCore31 at NJsonSchema.References.JsonReferenceBase1.NJsonSchema.References.IJsonReferenceBase.set_Reference(IJsonReference value) at NJsonSchema.JsonSchemaReferenceUtilities.JsonReferenceUpdater.VisitJsonReferenceAsync(IJsonReference reference, String path, String typeNameHint) at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet1 checkedObjects, Action1 replacer) at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet1 checkedObjects, Action1 replacer) at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet1 checkedObjects, Action1 replacer) at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet1 checkedObjects, Action1 replacer) at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet1 checkedObjects, Action1 replacer) at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet1 checkedObjects, Action1 replacer) at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj) at NJsonSchema.JsonSchemaReferenceUtilities.JsonReferenceUpdater.VisitAsync(Object obj) at NJsonSchema.JsonSchemaReferenceUtilities.UpdateSchemaReferencesAsync(Object rootObject, JsonReferenceResolver referenceResolver, IContractResolver contractResolver) at NJsonSchema.Infrastructure.JsonSchemaSerialization.FromJsonAsync[T](String json, SchemaType schemaType, String documentPath, Func2 referenceResolverFactory, IContractResolver contractResolver) at NSwag.OpenApiDocument.FromJsonAsync(String data, String documentPath, SchemaType expectedSchemaType, Func`2 referenceResolverFactory) in C:\projects\nswag\src\NSwag.Core\OpenApiDocument.cs:line 190 at NSwag.Commands.OutputCommandBase.ReadSwaggerDocumentAsync(String input) in C:\projects\nswag\src\NSwag.Commands\Commands\OutputCommandBase.cs:line 65 at NSwag.Commands.Generation.FromDocumentCommand.RunAsync() in C:\projects\nswag\src\NSwag.Commands\Commands\Generation\FromDocumentCommand.cs:line 62 at NSwag.Commands.Generation.FromDocumentCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in C:\projects\nswag\src\NSwag.Commands\Commands\Generation\FromDocumentCommand.cs:line 53 at NSwag.Commands.NSwagDocumentBase.GenerateSwaggerDocumentAsync() in C:\projects\nswag\src\NSwag.Commands\NSwagDocumentBase.cs:line 280 at NSwag.Commands.NSwagDocument.ExecuteAsync() in C:\projects\nswag\src\NSwag.Commands\NSwagDocument.cs:line 81 at NSwag.Commands.Document.ExecuteDocumentCommand.ExecuteDocumentAsync(IConsoleHost host, String filePath) in C:\projects\nswag\src\NSwag.Commands\Commands\Document\ExecuteDocumentCommand.cs:line 86 at NSwag.Commands.Document.ExecuteDocumentCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in C:\projects\nswag\src\NSwag.Commands\Commands\Document\ExecuteDocumentCommand.cs:line 32 at NConsole.CommandLineProcessor.ProcessSingleAsync(String[] args, Object input) at NConsole.CommandLineProcessor.ProcessAsync(String[] args, Object input) at NConsole.CommandLineProcessor.Process(String[] args, Object input) at NSwag.Commands.NSwagCommandProcessor.Process(String[] args) in C:\projects\nswag\src\NSwag.Commands\NSwagCommandProcessor.cs:line 56

anthonybouton commented 4 years ago

Having same issue when providing a specification rather than providing an URL

RicoSuter commented 3 years ago

I think the "definitions" do not only contain schemas but also responses, but at deserialization we assume "definitions" are schemas and serialize them that way => then you get a cast exception. I think this is solved in OpenAPI 3.0 as there you have not only definitions but also other collections where the type is clear...

This is probably very hard to solve and ideally you'd just have an oai 3.0 spec

tmds commented 3 years ago

I also get this exception for https://storage.googleapis.com/libpod-master-releases/swagger-latest-master.yaml.

but at deserialization we assume "definitions" are schemas and serialize them that way

How can I see in the yaml this is the issue?

RicoSuter commented 3 years ago

@tmds are there $refs from an operation response to the "definitions"?

tmds commented 3 years ago

For my understanding: is this something OpenAPI 2.0 doesn't allow? Or that NSwag doesn't support?

are there $refs from an operation response to the "definitions"?

Like the snippit below? Should the $ref: only refer to #/responses and not #/definitions?

  /libpod/manifests/{name:.*}:
    delete:
      description: Remove an image from a manifest list
      operationId: RemoveManifest
      parameters:
      - description: the image associated with the manifest
        in: path
        name: name:.*
        required: true
        type: string
      - description: image digest to be removed
        in: query
        name: digest
        type: string
      produces:
      - application/json
      responses:
        "200":
          $ref: '#/definitions/IDResponse'
tmds commented 3 years ago

I don't know much about about swagger. I'm adding @jwhonce who maintains the https://storage.googleapis.com/libpod-master-releases/swagger-latest-master.yaml file.

@RicoSuter @jwhonce I hope you can figure out what NSwag doesn't like about the podman swagger file.

Jhon, maybe you have an idea based on the comments that are in the issue. In case you want to reproduce the issue on Fedora and try something, you can do this:

$ sudo dnf install dotnet-sdk-3.1
$ dotnet tool install --global NSwag.ConsoleCore --version 13.9.4
$ wget https://storage.googleapis.com/libpod-master-releases/swagger-latest-master.yaml
$ nswag openapi2csclient /Input:swagger-latest-master.yaml /Namespace:Podman /Output:PodmanClient.cs /ClassName:PodmanClient

This is the output from running the last command:

NSwag command line tool for .NET Core Net50, toolchain v13.9.4.0 (NJsonSchema v10.3.1.0 (Newtonsoft.Json v12.0.0.0))
Visit http://NSwag.org for more information.
NSwag bin directory: /home/tmds/.dotnet/tools/.store/nswag.consolecore/13.9.4/nswag.consolecore/13.9.4/tools/net5.0/any
System.InvalidCastException: Unable to cast object of type 'NJsonSchema.JsonSchema' to type 'NSwag.OpenApiResponse'.
   at NJsonSchema.References.JsonReferenceBase`1.NJsonSchema.References.IJsonReferenceBase.set_Reference(IJsonReference value)
   at NJsonSchema.JsonSchemaReferenceUtilities.JsonReferenceUpdater.VisitJsonReferenceAsync(IJsonReference reference, String path, String typeNameHint, CancellationToken cancellationToken)
   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, CancellationToken cancellationToken)
   at NJsonSchema.JsonSchemaReferenceUtilities.JsonReferenceUpdater.VisitAsync(Object obj, CancellationToken cancellationToken)
   at NJsonSchema.JsonSchemaReferenceUtilities.UpdateSchemaReferencesAsync(Object rootObject, JsonReferenceResolver referenceResolver, IContractResolver contractResolver, CancellationToken cancellationToken)
   at NJsonSchema.Infrastructure.JsonSchemaSerialization.FromJsonAsync[T](String json, SchemaType schemaType, String documentPath, Func`2 referenceResolverFactory, IContractResolver contractResolver, CancellationToken cancellationToken)
   at NSwag.OpenApiDocument.FromJsonAsync(String data, String documentPath, SchemaType expectedSchemaType, Func`2 referenceResolverFactory, CancellationToken cancellationToken) in C:\projects\nswag\src\NSwag.Core\OpenApiDocument.cs:line 197
   at NSwag.OpenApiYamlDocument.FromYamlAsync(String data, String documentPath, SchemaType expectedSchemaType, Func`2 referenceResolverFactory, CancellationToken cancellationToken) in C:\projects\nswag\src\NSwag.Core.Yaml\OpenApiYamlDocument.cs:line 76
   at NSwag.OpenApiYamlDocument.FromFileAsync(String filePath, CancellationToken cancellationToken) in C:\projects\nswag\src\NSwag.Core.Yaml\OpenApiYamlDocument.cs:line 98
   at NSwag.Commands.OutputCommandBase.ReadSwaggerDocumentAsync(String input) in C:\projects\nswag\src\NSwag.Commands\Commands\OutputCommandBase.cs:line 49
   at NSwag.Commands.InputOutputCommandBase.GetInputSwaggerDocument() in C:\projects\nswag\src\NSwag.Commands\Commands\InputOutputCommandBase.cs:line 46
   at NSwag.Commands.CodeGeneration.SwaggerToCSharpClientCommand.<RunAsync>b__92_0() in C:\projects\nswag\src\NSwag.Commands\Commands\CodeGeneration\OpenApiToCSharpClientCommand.cs:line 253
   at NSwag.Commands.CodeGeneration.SwaggerToCSharpClientCommand.RunAsync() in C:\projects\nswag\src\NSwag.Commands\Commands\CodeGeneration\OpenApiToCSharpClientCommand.cs:line 251
   at NSwag.Commands.CodeGeneration.SwaggerToCSharpClientCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in C:\projects\nswag\src\NSwag.Commands\Commands\CodeGeneration\OpenApiToCSharpClientCommand.cs:line 240
   at NConsole.CommandLineProcessor.ProcessSingleAsync(String[] args, Object input)
   at NConsole.CommandLineProcessor.ProcessAsync(String[] args, Object input)
   at NConsole.CommandLineProcessor.Process(String[] args, Object input)
   at NSwag.Commands.NSwagCommandProcessor.Process(String[] args) in C:\projects\nswag\src\NSwag.Commands\NSwagCommandProcessor.cs:line 55
tmds commented 3 years ago

I removed paths, and added them back until I hit the exception. It happens when adding this path:

  /libpod/manifests/{name:.*}:
    delete:
      description: Remove an image from a manifest list
      operationId: RemoveManifest
      parameters:
      - description: the image associated with the manifest
        in: path
        name: name:.*
        required: true
        type: string
      - description: image digest to be removed
        in: query
        name: digest
        type: string
      produces:
      - application/json
      responses:
        "200":
          $ref: '#/definitions/IDResponse'
        "400":
          $ref: '#/responses/BadParamError'
        "404":
          $ref: '#/responses/NoSuchManifest'
        "500":
          $ref: '#/responses/InternalError'
      summary: Remove
      tags:
      - manifests

@RicoSuter do you see what is causing the exception?

tmds commented 3 years ago

IDResponse seems to be the common thing on the paths that cause the exception:

definitions:
...
  IDResponse:
    properties:
      Id:
        description: ID
        type: string
        x-go-name: ID
    type: object
    x-go-package: github.com/containers/podman/pkg/api/handlers

There is also a similar IdResponse (different casing) that also has the issue.

jeremyVignelles commented 3 years ago

Is that possible for you to clone the project and debug inside it to pinpoint the issue? run the Core command line project and see where it throws.

If you could make the PR, that would be even better

tmds commented 3 years ago

I've trimmed down the reproducer to the following. IdResponse seems to be the offending thing.

basePath: /
consumes:
- application/json
- application/x-tar
definitions:
  IdResponse:
    description: IDResponse Response to an API call that returns just an Id
    properties:
      Id:
        description: The id of the newly created object.
        type: string
        x-go-name: ID
    required:
    - Id
    type: object
    x-go-name: IDResponse
    x-go-package: github.com/containers/podman/vendor/github.com/docker/docker/api/types
host: podman.io
paths:
  /libpod/pods/create:
    post:
      operationId: CreatePod
      parameters:
      - description: attributes for creating a pod
        in: body
        name: create
        schema:
          $ref: '#/definitions/PodSpecGenerator'
          type: object
      produces:
      - application/json
      responses:
        "200":
          $ref: '#/definitions/IdResponse'
        "400":
          $ref: '#/responses/BadParamError'
        "500":
          $ref: '#/responses/InternalError'
      summary: Create a pod
      tags:
      - pods
produces:
- application/json
- text/plain
- text/html
responses:
  BadParamError:
    description: Bad parameter in request
    schema:
      properties:
        cause:
          description: API root cause formatted for automated parsing
          example: API root cause
          type: string
          x-go-name: Because
        message:
          description: human error message, formatted for a human to read
          example: human error message
          type: string
          x-go-name: Message
        response:
          description: http response code
          format: int64
          type: integer
          x-go-name: ResponseCode
      type: object
  InternalError:
    description: Internal server error
    schema:
      properties:
        cause:
          description: API root cause formatted for automated parsing
          example: API root cause
          type: string
          x-go-name: Because
        message:
          description: human error message, formatted for a human to read
          example: human error message
          type: string
          x-go-name: Message
        response:
          description: http response code
          format: int64
          type: integer
          x-go-name: ResponseCode
      type: object
schemes:
- http
- https
swagger: "2.0"
tmds commented 3 years ago

I had a look at the swagger file for docker, which works with NSwag. Here is the difference:

      responses:
        201:
          description: "no error"
          schema:
            $ref: "#/definitions/IdResponse"

note the schema that shows up.

tmds commented 3 years ago

I've added the schemas in the yaml file and now NSwag is happy.

My changes look like:

@@ -10300,7 +10300,8 @@ paths:
       - application/json
       responses:
         "200":
-          $ref: '#/definitions/IDResponse'
+          schema:
+            $ref: '#/definitions/IDResponse'
         "400":
           $ref: '#/responses/BadParamError'
         "404":
Hu1buerger commented 2 years ago

Would it be possible to report the line that caused this exception?