Search code examples
typescriptenumsswagger-codegenopenapi-generator

openapi-typescript generate enums


given this input:

openapi: 3.0.1
info:
  title: test enums
  description: test enums
  version: "1.00"

components:
  schemas:
    VideoProcessingStateDto:
      type: string
      enum:
        - IN_PROGRESS
        - FAILED
        - FINISHED

I wish to generate the following typescript enum

export enum VideoProcessingStateDto {
  IN_PROGRESS = 'IN_PROGRESS',
  FAILED = 'FAILED',
  FINISHED = 'FINISHED',
}

However, openapi-typescript continues to give me a string union as such:

/** @enum {string} */
VideoProcessingStateDto: "IN_PROGRESS" | "FAILED" | "FINISHED";

Is it possible to configure openapi-typescript to generate enums in this case?

Based on a very superficial glance at the source code, I am not seeing any configuration options

Perhaps I am not defining the enum correctly in the input?

Any guidance would be greatly appreciated, thank you!


Solution

  • Ok so I decided to engage with the transform option on the API, which allows users to reshape the output as needed. In this case, I am replacing the value with the name of an enum, generating a enum from the values in the union, and adding them to a cache.

    When it comes time to output the results to the file, I am then appending the enums to the end of the file so that they exist when they are referenced in the schema components.

    This is very rough code and you will probably need to modify it to meet you needs but here is all the code.

    import openapiTS, { SchemaObject } from 'openapi-typescript';
    import * as fs from 'node:fs';
    
    // Utility function to get the name of the schema from the path
    function getSchemaName(str: string) {
      const arr = str.split('/');
      const schemaName = arr[arr.length - 1];
      return schemaName;
    }
    
    /** trasform the sting union of "IN_PROGRESS" | "FAILED" | "FINISHED"
     * to a typescript enum like:
     * enum Status {
     * IN_PROGRESS = "IN_PROGRESS",
     * FAILED = "FAILED",
     * FINISHED = "FINISHED"
     * }
     */
    function unionToEnum(enumValues: string[], enumName: string) {
      const enumString = enumValues
        .map((value) => `${value} = "${value}"`)
        .join(',\n');
      const enumType = `enum ${enumName} {
        ${enumString}
      }`;
      return enumType;
    }
    
    async function generateTypesFromSchema(): Promise<string> {
      const localPath = new URL('api.yml', import.meta.url);
      const output = await openapiTS(localPath, {
        transform: collectAndTransformEnums,
      });
      if (output) {
        return output;
      }
      return '';
    }
    
    const enumCache = new Map();
    
    function collectAndTransformEnums(
      schemaObject: SchemaObject,
      metadata: any
    ): string {
      if ('enum' in schemaObject) {
        const enumName = getSchemaName(metadata.path);
        const enumValues = schemaObject.enum as string[];
        enumCache.set(getSchemaName(enumName), unionToEnum(enumValues, enumName));
        return enumName;
      }
      return '';
    }
    
    /**
     * 1. generate the types from the schema
     * 2. iterate over the enumCache and add the enums to the types
     * 3. write the types to a file
     */
    generateTypesFromSchema().then((types) => {
      enumCache.forEach((enumType) => {
        types += enumType;
      });
    
      // write the types to a file
      return fs.writeFile('defs/types.ts', types, (err) => {
        if (err) {
          console.error(err);
          return;
        }
      });
    });
    
    

    Using the script above, given this input:

    openapi: 3.0.1
    
    info:
      title: Sample Video API
      description: Sample Video API
      version: '1.0'
    
    paths:
      /api/v1/video-process:
        get:
          tags:
            - video
          summary: Process a video input
          operationId: VideoProcess
          responses:
            '200':
              description: Successful operation
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/VideoProcessingStateDto'
    
    components:
      schemas:
        VideoProcessingStateDto:
          type: string
          enum:
            - IN_PROGRESS
            - FAILED
            - FINISHED
    
    

    The output will be:

    /**
     * This file was auto-generated by openapi-typescript.
     * Do not make direct changes to the file.
     */
    
    export interface paths {
      '/api/v1/video-process': {
        /** Process a video input */
        get: operations['VideoProcess'];
      };
    }
    
    export type webhooks = Record<string, never>;
    
    export interface components {
      schemas: {
        /** @enum {string} */
        VideoProcessingStateDto: VideoProcessingStateDto;
      };
      responses: never;
      parameters: never;
      requestBodies: never;
      headers: never;
      pathItems: never;
    }
    
    export type external = Record<string, never>;
    
    export interface operations {
      /** Process a video input */
      VideoProcess: {
        responses: {
          /** @description Successful operation */
          200: {
            content: {
              'application/json': components['schemas']['VideoProcessingStateDto'];
            };
          };
        };
      };
    }
    enum VideoProcessingStateDto {
      IN_PROGRESS = 'IN_PROGRESS',
      FAILED = 'FAILED',
      FINISHED = 'FINISHED',
    }