Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Regressions with the new OpenAPI generator #863

Closed
rubenfonseca opened this issue Oct 18, 2024 · 6 comments · Fixed by #864
Closed

[BUG] Regressions with the new OpenAPI generator #863

rubenfonseca opened this issue Oct 18, 2024 · 6 comments · Fixed by #864
Labels
bug Something isn't working needs-triage

Comments

@rubenfonseca
Copy link

Describe the bug

When upgrading to the newer versions of PDK that use the new custom generator, my OpenAPI based TypeSafeApi project stopped compiling.

Expected Behavior

I expect to be able to upgrade the PDK and continue using it without changing my OpenAPI spec.

Current Behavior

Compiling the project ends with these errors:

myapi-typescript-runtime: src/apis/DefaultApi/OperationConfig.ts(302,55): error TS2693: 'AssetResponse' only refers to a type, but is being used as a value here.
myapi-typescript-runtime: src/apis/DefaultApi/OperationConfig.ts(302,69): error TS2552: Cannot find name 'ToJSON'. Did you mean 'JSON'?
myapi-typescript-runtime: src/models/ArchiverAssetResponse.ts(52,5): error TS2322: Type '{ category: string; key: string; bucket: string; }' is not assignable to type 'ArchiverAssetResponse'.
myapi-typescript-runtime:   Type '{ category: string; key: string; bucket: string; }' is not assignable to type 'AssetBaseResponse'.
myapi-typescript-runtime:     Types of property 'category' are incompatible.
myapi-typescript-runtime:       Type 'string' is not assignable to type 'Category'.
myapi-typescript-runtime: src/models/TemporaryUploadResponse.ts(52,5): error TS2322: Type '{ category: string; key: string; bucket: string; }' is not assignable to type 'TemporaryUploadResponse'.
myapi-typescript-runtime:   Type '{ category: string; key: string; bucket: string; }' is not assignable to type 'AssetBaseResponse'.
myapi-typescript-runtime:     Types of property 'category' are incompatible.
myapi-typescript-runtime:       Type 'string' is not assignable to type 'Category'.

Reproduction Steps

Current .projenrc.ts:

import { MonorepoTsProject } from "@aws/pdk/monorepo";
import {
  DocumentationFormat,
  Language,
  ModelLanguage,
  TypeSafeApiProject,
} from "@aws/pdk/type-safe-api";
import { javascript } from "projen";

const project = new MonorepoTsProject({
  name: "aws-pdk-test",
  packageManager: javascript.NodePackageManager.YARN,
  projenrcTs: true,
});

new TypeSafeApiProject({
  parent: project,
  outdir: "packages/api",
  name: "myapi",
  infrastructure: {
    language: Language.TYPESCRIPT,
  },
  model: {
    language: ModelLanguage.OPENAPI,
    options: {
      openapi: {
        title: "assets",
      },
    },
  },
  runtime: {
    languages: [Language.TYPESCRIPT],
  },
  documentation: {
    formats: [DocumentationFormat.HTML_REDOC],
  },
  handlers: {
    languages: [Language.TYPESCRIPT],
  },
});

project.synth();

Current minimal OpenAPI spec:

openapi: 3.0.0
info:
  title: Foo API
  version: 1.0.0
  description: A service
  license:
    name: UNLICENSED
  contact:
    name: John Smith
    email: [email protected]
externalDocs:
  description: Swagger Specification Documents
  url: https://swagger.io/docs/specification/about/
servers:
  - url: /api
    description: Development server
components:
  schemas:
    Category:
      type: string
      enum:
        - temporary-upload
        - archiver
    AssetBaseResponse:
      type: object
      properties:
        key:
          type: string
          description: AWS S3 key
          example: some-s3-key-for-file
        bucket:
          type: string
          description: AWS S3 Bucket
          example: some-bucket
        category:
          $ref: "#/components/schemas/Category"
      required:
        - key
        - bucket
        - category
      additionalProperties: false
    TemporaryUploadResponse:
      allOf:
        - $ref: "#/components/schemas/AssetBaseResponse"
        - type: object
          properties:
            category:
              type: string
              enum:
                - temporary-upload
          required:
            - category
    ArchiverAssetResponse:
      allOf:
        - $ref: "#/components/schemas/AssetBaseResponse"
        - type: object
          properties:
            category:
              type: string
              enum:
                - archiver
          required:
            - category
      additionalProperties: false
    AssetResponse:
      anyOf:
        - $ref: "#/components/schemas/TemporaryUploadResponse"
        - $ref: "#/components/schemas/ArchiverAssetResponse"
    CategoryResponse:
      type: object
      properties:
        categories:
          $ref: "#/components/schemas/Category"
      required:
        - categories
      additionalProperties: false
    InternalFailureErrorResponseContent:
      type: object
      properties:
        message:
          type: string
      required:
        - message
    BadRequestErrorResponseContent:
      type: object
      properties:
        message:
          type: string
      required:
        - message
    NotAuthorizedErrorResponseContent:
      type: object
      properties:
        message:
          type: string
      required:
        - message
paths:
  /assets/{category}:
    get:
      operationId: getAssetsByCategory
      x-handler:
        language: typescript
      description: Get Assets by category & keys
      tags:
        - Assets
      parameters:
        - in: path
          name: category
          required: true
          schema:
            $ref: "#/components/schemas/Category"
          description: The category
        - in: query
          name: key
          explode: true
          required: true
          schema:
            type: array
            items:
              type: string
            minItems: 1
          description: AWS S3 keys for the asset
      responses:
        "200":
          description: >-
            Returns a list of assets based on the category and AWS S3 key.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/AssetResponse"
        500:
          description: An internal failure at the fault of the server
          content:
            "application/json":
              schema:
                $ref: "#/components/schemas/InternalFailureErrorResponseContent"
        400:
          description: An error at the fault of the client sending invalid input
          content:
            "application/json":
              schema:
                $ref: "#/components/schemas/BadRequestErrorResponseContent"
        403:
          description: An error due to the client not being authorized to access the resource
          content:
            "application/json":
              schema:
                $ref: "#/components/schemas/NotAuthorizedErrorResponseContent"
  /categories:
    get:
      operationId: getCategories
      x-handler:
        language: typescript
      description: Get Categories
      tags:
        - Categories
      responses:
        "200":
          description: Returns a list of categories.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CategoryResponse"
        500:
          description: An internal failure at the fault of the server
          content:
            "application/json":
              schema:
                $ref: "#/components/schemas/InternalFailureErrorResponseContent"
        400:
          description: An error at the fault of the client sending invalid input
          content:
            "application/json":
              schema:
                $ref: "#/components/schemas/BadRequestErrorResponseContent"
        403:
          description: An error due to the client not being authorized to access the resource
          content:
            "application/json":
              schema:
                $ref: "#/components/schemas/NotAuthorizedErrorResponseContent"

Possible Solution

No response

Additional Information/Context

No response

PDK version used

0.23.69

What languages are you seeing this issue on?

Typescript

Environment details (OS name and version, etc.)

macOS 15.0.1

@rubenfonseca rubenfonseca added bug Something isn't working needs-triage labels Oct 18, 2024
cogwirrel added a commit that referenced this issue Oct 18, 2024
…ted code

Hoist inline enums to ensure a model is created for them and they can therefore be typed correctly.

Fixes #863
@cogwirrel
Copy link
Member

Hey @rubenfonseca! Thanks for raising this! Apologies for the regression!

Looks like we need to hoist any inline enums such that we generate a type for them! I'll raise a PR to address this shortly - but it won't be reviewed until Monday :/

To unblock yourself for now you could try defining each enum as its own named schema and referencing them, eg for your example you'd change these:

    TemporaryUploadResponse:
      allOf:
        - $ref: "#/components/schemas/AssetBaseResponse"
        - type: object
          properties:
            category:
              type: string
              enum:
                - temporary-upload
          required:
            - category
    ArchiverAssetResponse:
      allOf:
        - $ref: "#/components/schemas/AssetBaseResponse"
        - type: object
          properties:
            category:
              type: string
              enum:
                - archiver
          required:
            - category
      additionalProperties: false

to something like

    TemporaryUploadResponse:
      allOf:
        - $ref: "#/components/schemas/AssetBaseResponse"
        - type: object
          properties:
            category:
              $ref: "#/components/schemas/TemporaryUploadCategory"
          required:
            - category
    ArchiverAssetResponse:
      allOf:
        - $ref: "#/components/schemas/AssetBaseResponse"
        - type: object
          properties:
            category:
              $ref: "#/components/schemas/ArchiverCategory"
          required:
            - category
      additionalProperties: false
    ArchiverCategory:
      type: string
      enum:
        - archiver
    TemporaryUploadCategory:
      type: string
      enum:
        - temporary-upload

Hope this helps!

@rubenfonseca
Copy link
Author

Thank you for the fast response! I've tried your workaround, but I'm still stuck on a different part:

myapi-typescript-runtime: src/apis/DefaultApi/OperationConfig.ts(308,55): error TS2693: 'AssetResponse' only refers to a type, but is being used as a value here.
myapi-typescript-runtime: src/apis/DefaultApi/OperationConfig.ts(308,69): error TS2552: Cannot find name 'ToJSON'. Did you mean 'JSON'?

The relevant generated code looks like this:

    const marshal = (statusCode: number, responseBody: any): string => {
        let marshalledBody = responseBody;
        switch(statusCode) {
            case 200:
                marshalledBody = JSON.stringify(Array<AssetResponse>ToJSON(marshalledBody));
                break;

@cogwirrel
Copy link
Member

Ooh so I think this is from returning an array at the top level- did this work before the new codegen? It should work fine if you return an object with an array property as a workaround :)

I’ve got to sign off unfortunately but I’ll look at this first thing on Monday!

@rubenfonseca
Copy link
Author

@cogwirrel that's correct, for this case I was already overriding the operationConfig.mustache this way

    const marshal = (statusCode: number, responseBody: any): string => {
        let marshalledBody = responseBody;
        switch(statusCode) {
        {{#responses}}
            case {{code}}:
                {{^isPrimitiveType}}
                  {{#isArray}}
                    marshalledBody = JSON.stringify(marshalledBody.map({{baseType}}ToJSON));
                  {{/isArray}}
                  {{^isArray}}
                    marshalledBody = JSON.stringify({{dataType}}ToJSON(marshalledBody));
                  {{/isArray}}
                {{/isPrimitiveType}}
                break;
        {{/responses}}
            default:
                break;
        }

        return marshalledBody;
    };

So I guess this is a separate bug / not a regression!

@cogwirrel
Copy link
Member

Ah yep cool - would definitely be good to fix this in the new codegen too! 😄

@cogwirrel
Copy link
Member

Raised #866 to track the array response issue :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working needs-triage
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants