RicoSuter / NSwag

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

Resolving references fails in 2nd level #4050

Open mskarabot-axess opened 2 years ago

mskarabot-axess commented 2 years ago

Generating dotnet core client works if everything regarding OpenAPI definition is in a single file. However, as APIs get big you need to separate schemas to multiple files. The client code generation works if references are only in a single level. In case references are multi-level - schema file references schemas in itself or into other files, the generator fails.

The CLI command: nswag openapi2csclient /input:CartAPI.yml /classname:CartAPIClient /namespace:CartAPIClient.Models.Generated /output:src/generated/CartAPIClient.cs with the two files bellow fails with:

System.InvalidOperationException: Error while rendering Liquid template CSharp/Class: 
The schema reference path 'CartSchema.yml#/components/schemas/ParameterModel' has not been resolved.
 ---> System.InvalidOperationException: The schema reference path 'CartSchema.yml#/components/schemas/ParameterModel' has not been resolved.
   at NJsonSchema.JsonSchema.<GetActualSchema>g__ThrowInvalidOperationException|265_0(String message)
   at NJsonSchema.JsonSchema.get_ActualSchema()
   at NJsonSchema.CodeGeneration.Models.PropertyModelBase.get_IsStringEnumArray()
   at Fluid.Accessors.PropertyInfoAccessor.Invoker`2.Invoke(Object target)
   at Fluid.Accessors.PropertyInfoAccessor.Get(Object obj, String name, TemplateContext ctx)
   at Fluid.Values.ObjectValueBase.GetValueAsync(String name, TemplateContext context)
   at Fluid.Ast.IdentifierSegment.ResolveAsync(FluidValue value, TemplateContext context)
   at Fluid.Ast.MemberExpression.EvaluateAsync(TemplateContext context)
   at Fluid.Ast.IfStatement.WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
   at Fluid.Ast.ElseStatement.WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
   at Fluid.Ast.IfStatement.WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
   at Fluid.Ast.ForStatement.WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
   at Fluid.Parser.FluidTemplate.Awaited(ValueTask`1 task, TextWriter writer, TextEncoder encoder, TemplateContext context, IReadOnlyList`1 statements, Int32 startIndex)
   at Fluid.FluidTemplateExtensions.RenderAsync(IFluidTemplate template, TemplateContext context, TextEncoder encoder)
   at NJsonSchema.CodeGeneration.DefaultTemplateFactory.LiquidTemplate.Render()
   --- End of inner exception stack trace ---
   at NJsonSchema.CodeGeneration.DefaultTemplateFactory.LiquidTemplate.Render()
   at NJsonSchema.CodeGeneration.CodeArtifact..ctor(String typeName, String baseTypeName, CodeArtifactType type, CodeArtifactLanguage language, CodeArtifactCategory category, ITemplate template)
   at NJsonSchema.CodeGeneration.CSharp.CSharpGenerator.GenerateClass(JsonSchema schema, String typeName)
   at NJsonSchema.CodeGeneration.CSharp.CSharpGenerator.GenerateType(JsonSchema schema, String typeNameHint)
   at NJsonSchema.CodeGeneration.GeneratorBase.GenerateTypes()
   at NJsonSchema.CodeGeneration.CSharp.CSharpGenerator.GenerateTypes()
   at NSwag.CodeGeneration.CSharp.CSharpGeneratorBase.GenerateDtoTypes() in /_/src/NSwag.CodeGeneration.CSharp/CSharpGeneratorBase.cs:line 106
   at NSwag.CodeGeneration.ClientGeneratorBase`3.GenerateFile(ClientGeneratorOutputType outputType) in /_/src/NSwag.CodeGeneration/ClientGeneratorBase.cs:line 75
   at NSwag.Commands.CodeGeneration.SwaggerToCSharpClientCommand.RunAsync() in /_/src/NSwag.Commands/Commands/CodeGeneration/OpenApiToCSharpClientCommand.cs:line 270
   at NSwag.Commands.CodeGeneration.SwaggerToCSharpClientCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/CodeGeneration/OpenApiToCSharpClientCommand.cs:line 248        
   at NConsole.CommandLineProcessor.ProcessSingleAsync(String[] args, Object input)
   at NConsole.CommandLineProcessor.ProcessAsync(String[] args, Object input)
   at NSwag.Commands.NSwagCommandProcessor.ProcessAsync(String[] args) in /_/src/NSwag.Commands/NSwagCommandProcessor.cs:line 61node:child_process:905
    throw err;
    ^

File: CartAPI.yml

openapi: 3.0.3
info:
  title: Cart Service
  version: "1.0.0"
  description: Test
  contact:
    email: a.a@a.com
tags:
  - name: Carts
servers:
  - url: http://localhost:2222
paths:
  /api/v1/carts/{cartId}:
    parameters:
      - $ref: "CartSchema.yml#/components/parameters/CartPath"
    get:
      description: Get cart by id
      operationId: GetCart
      tags:
        - Carts
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "CartSchema.yml#/components/schemas/CartModel"
        "default":
          description: |
            Internal server error.
            Please retry again.

File CartSchema.yml

---
components:
  parameters:
    CartPath:
      name: cartId
      in: path
      required: true
      schema:
        type: string
        pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
        example: "105a76d8-db49-4144-ace7-e683e8f4ba46"
        format: uuid
        nullable: true
  schemas:
    ParameterModel:
      type: object
      required:
        - code
      properties:
        value:
          type: string
          nullable: false
          example: "XS"
          description: |
            Designates the value of the selected parameter.
      additionalProperties: false
    LineItemModel:
      type: object
      properties:
        id:
          type: string
          pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
          example: "105a76d8-db49-4144-ace7-e683e8f4ba46"
          format: uuid
          nullable: true
        productId:
          type: string
          pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
          example: "105a76d8-db49-4144-ace7-e683e8f4ba46"
          format: uuid
          nullable: true
        parentCartLineItemId:
          type: string
          pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
          example: "105a76d8-db49-4144-ace7-e683e8f4ba46"
          format: uuid
          nullable: true
        quantity:
          type: number
          example: 2.5
          format: decimal
        unitPrice:
          type: number
          description: "Value is rounded to 2 decimals"
          example: 19.99
          format: decimal
        parameters:
          type: array
          minItems: 0
          maxItems: 100
          items:
            $ref: "CartSchema.yml#/components/schemas/ParameterModel"
          nullable: true
      additionalProperties: false
    CartModel:
      type: object
      properties:
        id:
          type: string
          pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
          example: "105a76d8-db49-4144-ace7-e683e8f4ba46"
          format: uuid
          nullable: true
        currency:
          type: string
          description: 3 letter currency code as defined by ISO-4217
          format: iso-4217
          example: EUR
        lineItems:
          type: array
          minItems: 0
          maxItems: 200
          items:
            $ref: "CartSchema.yml#/components/schemas/LineItemModel"
          nullable: true
      additionalProperties: false
xamele0n commented 1 month ago

https://github.com/RicoSuter/NSwag/issues/3175#issuecomment-2205662205