ferdikoomen / openapi-typescript-codegen

NodeJS library that generates Typescript or Javascript clients based on the OpenAPI specification
MIT License
2.96k stars 525 forks source link

Relative external references for Angular client doesn't work #1996

Open bzick opened 9 months ago

bzick commented 9 months ago

Problem

openapi.yaml spec file has relative references to schemas

openapi: 3.0.0
info:
  title: Reproduce bug
  version: '1.0'
servers:
  - url: /api
paths:
  '/mysql':
    post:
      summary: 'Create MySQL resource'
      operationId: post-database-mysql
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: 'schemas/MySQL.yaml'
      requestBody:
        content:
          application/json:
            schema:
              $ref: 'schemas/MySQL.yaml'

schemas/MySQL.yaml file (but in our case it doesn't matter):

type: object
properties:
  users:
    type: object
    additionalProperties:
      type: object
      properties:
        policy:
          type: string
          enum:
            - rw
            - ro

openapi call generates an invalid Angular client for API:

openapi  --client angular --input openapi.yaml --output ./generated 

services/DefaultService.ts is broken:

/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import type { Observable } from 'rxjs';
import type { paths_1mysql_post_requestBody_content_application_1json_schema } from '../models/paths_1mysql_post_requestBody_content_application_1json_schema';
import { OpenAPI } from '../core/OpenAPI';
import { request as __request } from '../core/request';
@Injectable({
    providedIn: 'root',
})
export class DefaultService {
    constructor(public readonly http: HttpClient) {}
    /**
     * Create MySQL resource
     * @param requestBody
     * @returns paths_1mysql_post_requestBody_content_application_1json_schema Created
     * @throws ApiError
     */
    public postDatabaseMysql(
        requestBody?: {
            users?: Record<string, {
                policy?: 'rw' | 'ro';
            }>;
        },
    ): Observable<paths_1mysql_post_requestBody_content_application_1json_schema> {
        return __request(OpenAPI, this.http, {
            method: 'POST',
            url: '/mysql',
            body: requestBody,
            mediaType: 'application/json',
        });
    }
}

MySQL model has paths_1mysql_post_requestBody_content_application_1json_schema name and that model does't exist (model directory is empty).

Without external refs

If i use local ref instead of external everything works fine:

openapi: 3.0.0
info:
  title: Reproduce bug
  version: '1.0'
servers:
  - url: /api
paths:
  '/mysql':
    post:
      summary: 'Create MySQL resource'
      operationId: post-database-mysql
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MySQL'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MySQL'
components:
  schemas:
    MySQL:
      type: object
      properties:
        users:
          type: object
          additionalProperties:
            type: object
            properties:
              policy:
                type: string
                enum:
                  - rw
                  - ro

openapi generates a valid services/DefaultService.ts:

/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import type { Observable } from 'rxjs';
import type { MySQL } from '../models/MySQL';
import { OpenAPI } from '../core/OpenAPI';
import { request as __request } from '../core/request';
@Injectable({
    providedIn: 'root',
})
export class DefaultService {
    constructor(public readonly http: HttpClient) {}
    /**
     * Create MySQL resource
     * @param requestBody
     * @returns MySQL Created
     * @throws ApiError
     */
    public postDatabaseMysql(
        requestBody?: MySQL,
    ): Observable<MySQL> {
        return __request(OpenAPI, this.http, {
            method: 'POST',
            url: '/mysql',
            body: requestBody,
            mediaType: 'application/json',
        });
    }
}

Code with bug

I attached archive openapi-reproduce.zip with valid (without external $ref) and invalid (with external $ref) code. Archive contains openapi-broken project with bug and external $ref, and openapi-valid without bug and external $ref

bzick commented 9 months ago

Problem reproduces only for Angular client and on 0.22.0—0.27.0 versions (older versions have not been tested)