microsoft / typespec

https://typespec.io/
MIT License
4.17k stars 199 forks source link

[Bug]: Missing mapping for discriminated model #4375

Open DMaro28 opened 1 week ago

DMaro28 commented 1 week ago

Describe the bug

On version 0.55.0 of the compiler, mapping is incomplete for a discriminated model if a child model is used elsewhere in the spec. Please note that in WidgetBase mapping, discriminator maps only LightWidget. If HeavyWidget is not used in SomeModel (by commenting out line 47, and uncommenting 48), both child models are mapped correctly. To be precise, HeavyWidget is defined in the spec, but not mapped in discriminator. Since we are using discriminated models for polymorphism, code generators are not outputting correctly due to missing mapping. Please confirm if this is a bug.

I've also tried the same code sample on latest 0.59.1 compiler. Mapping is emitted correctly in that case, for this simple sample. But if I try to run in on entire codebase (with packages, other discriminated models with inheritance and multiple files), issue still persists. Mapping is missing for child models that are used elsewhere in the spec. Any suggestions on root cause of the issue and solution would be very much appreciated.

Reproduction

import "@typespec/http";
import "@typespec/rest";
import "@typespec/openapi3";

@service({
  title: "Widget Service",
})
namespace DemoService;
using TypeSpec.Rest;
using TypeSpec.Http;
using TypeSpec.OpenAPI;

@discriminator("kind")
model WidgetBase {
  kind: WidgetKind,
  @key id: string;
  weight: int32;
  color: "red" | "blue";
}

enum WidgetKind {
  Heavy,
  Light,
}

model HeavyWidget extends WidgetBase {
  kind: WidgetKind.Heavy;
  someproperty1: string
}

model LightWidget extends WidgetBase {
  kind: WidgetKind.Light;
  someproperty2: string
}

model SomeModel{
  someproperty3: HeavyWidget[];
  someproperty4: string
}

@error
model Error {
  code: int32;
  message: string;
}

@post op calc(data: SomeModel): WidgetBase | Error;
// @post op calc(): WidgetBase | Error;

produces:

openapi: 3.0.0
info:
  title: Widget Service
  version: 0.0.0
tags: []
paths:
  /:
    post:
      operationId: calc
      parameters: []
      responses:
        '200':
          description: The request has succeeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WidgetBase'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                data:
                  $ref: '#/components/schemas/SomeModel'
              required:
                - data
components:
  schemas:
    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: integer
          format: int32
        message:
          type: string
    HeavyWidget:
      type: object
      required:
        - kind
        - someproperty1
      properties:
        kind:
          type: string
          enum:
            - Heavy
        someproperty1:
          type: string
      allOf:
        - $ref: '#/components/schemas/WidgetBase'
    LightWidget:
      type: object
      required:
        - kind
        - someproperty2
      properties:
        kind:
          type: string
          enum:
            - Light
        someproperty2:
          type: string
      allOf:
        - $ref: '#/components/schemas/WidgetBase'
    SomeModel:
      type: object
      required:
        - someproperty3
        - someproperty4
      properties:
        someproperty3:
          type: array
          items:
            $ref: '#/components/schemas/HeavyWidget'
        someproperty4:
          type: string
    WidgetBase:
      type: object
      required:
        - kind
        - id
        - weight
        - color
      properties:
        kind:
          $ref: '#/components/schemas/WidgetKind'
        id:
          type: string
        weight:
          type: integer
          format: int32
        color:
          type: string
          enum:
            - red
            - blue
      discriminator:
        propertyName: kind
        mapping:
          Light: '#/components/schemas/LightWidget'
    WidgetKind:
      type: string
      enum:
        - Heavy
        - Light

Checklist

timotheeguerin commented 1 week ago

I do vaguely remember fixing it so if you still have it with the full code base it would be good to have the recent repro. My guess is something to do with circular reference and the order of evaluation of those. When you have your whole code base it must change and hit a broken flow. You could try moving models around to see if that solve the issue