Code Monkey home page Code Monkey logo

openapi-codegen-ts's Introduction

Utilities and tools for the Digital Citizenship initiative

This package provide some tools that are used across the projects of the Digital Citizenship initiative.

To add the tools to a project:

$ yarn add -D @pagopa/openapi-codegen-ts

Commands

gen-api-models

This tool generates TypeScript definitions of OpenAPI specs.

In simple terms it converts an OpenAPI spec like this one into:

Note: the generated models requires the runtime dependency @pagopa/ts-commons.

About string pattern definition

Up until version 12.x, when handling string pattern definitions given in the OpenAPI specifications, the production of runtime types has a bug: when using a backslash (\) for escaping regular expression digits (e.g., \d), the generator drops it. Double backslashes (\\) can be used in the pattern description as a fix for this issue. Starting from version 12.x the codegen will notify you whenever it detects a \\ inside a pattern definition as this bug has been resolved for OpenAPI 3.x.

Usage

$ gen-api-models --help
Options:
  --version               Show version number                          [boolean]
  --api-spec              Path to input OpenAPI spec file    [string] [required]
  --strict                Generate strict interfaces (default: true)
                                                                 [default: true]
  --out-dir               Output directory to store generated definition files
                                                             [string] [required]
  --ts-spec-file          If defined, converts the OpenAPI specs to TypeScript
                          source and writes it to this file             [string]
  --request-types         Generate request types (default: false)
                                                                [default: false]
  --response-decoders     Generate response decoders (default:
                          false, implies --request-types)       [default: false]
  --client                Generate request client SDK           [default: false]
  --default-success-type  Default type for success responses (
                          default: 'undefined')  [string] [default: "undefined"]
  --default-error-type    Default type for error responses (
                          default: 'undefined')  [string] [default: "undefined"]
  --help                  Show help                                    [boolean]

Example:

$ gen-api-models --api-spec ./api/public_api_v1.yaml --out-dir ./lib/api/definitions --ts-spec-file ./lib/api/public_api_v1.ts
Writing TS Specs to lib/api/public_api_v1.ts
ProblemJson -> lib/api/definitions/ProblemJson.ts
NotificationChannel -> lib/api/definitions/NotificationChannel.ts
NotificationChannelStatusValue -> lib/api/definitions/NotificationChannelStatusValue.ts
...
done

Generated client

The http client is defined in client.ts module file. It exports the following:

  • a type Client<K> which define the set of operations
    • K a union of keys that represent the parameters omitted by the operations (see withDefaults below)
  • a function createClient<K>(parameters): Client<K> accepting the following parameters:
    • baseUrl the base hostname of the api, including protocol and port
    • fetchApi an implementation of the fetch-api as defined in your platform (for example: node-fetch if you are in node)
    • basePath (optional) if defined, is appended to baseUrl for every operations. Its default is the basePath value defined in the specification
    • withDefaults (optional) an adapter function that wraps every operations. It may shadow some parameters to the wrapped operations. The use case is: you have a parameter which is common to many operations and you want it to be fixed (example: a session token).
  • a type WithDefaultsT<K> that defines an adapter function to be used as withDefaults
    • K the set of parameters that the adapter will shadow
Example
import { createClient, WithDefaultsT } from "my-api/client";


// Without withDefaults
const simpleClient: Client = createClient({
    baseUrl: `http://localhost:8080`,
    fetchApi: (nodeFetch as any) as typeof fetch
});

// myOperation is defined to accept { id: string; Bearer: string; }
const result = await simpleClient.myOperation({
    id: "id123",
    Bearer: "VALID_TOKEN"
});


// with withDefaults
const withBearer: WithDefaultsT<"Bearer"> = 
    wrappedOperation => 
        params => { // wrappedOperation and params are correctly inferred
            return wrappedOperation({
              ...params,
              Bearer: "VALID_TOKEN"
            });
          };
//  this is the same of using createClient<"Bearer">. K type is being inferred from withBearer
const clientWithGlobalToken: Client<"Bearer"> = createClient({
    baseUrl: `http://localhost:8080`,
    fetchApi: (nodeFetch as any) as typeof fetch,
    withDefaults: withBearer
});

// myOperation doesn't require "Bearer" anymore, as it's defined in "withBearer" adapter 
const result = await clientWithGlobalToken.myOperation({
    id: "id123"
});

gen-api-sdk

Bundles a generated api models and clients into a node package ready to be published into a registry. The script is expected to be executed in the root of an application exposing an API, thus it infers package attributes from the expected ./package.json file. Values can be still overridden by provinding the respective CLI argument. To avoid this behavior, use --no-infer-attrs or -N.

Usage

$ gen-api-sdk --help
Package options:
  --no-infer-attr, -N                 Infer package attributes from a
                                      package.json file present in the current
                                      directory       [boolean] [default: false]
  --package-name, -n, --name          Name of the generated package     [string]
  --package-version, -V               Version of the generated package  [string]
  --package-description, -d, --desc   Description of the package        [string]
  --package-author, -a, --author      The author of the API exposed     [string]
  --package-license, -L, --license    The license of the API Exposed    [string]
  --package-registry, -r, --registry  Url of the registry the package is
                                      published in                      [string]
  --package-access, -x, --access      Either 'public' or 'private', depending of
                                      the accessibility of the package in the
                                      registry
                                         [string] [choices: "public", "private"]

Code generation options:
  --api-spec, -i          Path to input OpenAPI spec file    [string] [required]
  --strict                Generate strict interfaces (default: true)
                                                                 [default: true]
  --out-dir, -o           Output directory to store generated definition files
                                                             [string] [required]
  --default-success-type  Default type for success responses (experimental,
                          default: 'undefined')  [string] [default: "undefined"]
  --default-error-type    Default type for error responses (experimental,
                          default: 'undefined')  [string] [default: "undefined"]
  --camel-cased           Generate camelCased properties name (default: false)
                                                                [default: false]

Options:
  --version  Show version number                                       [boolean]
  --help     Show help                                                 [boolean]

bundle-api-spec

Takes a given api spec file and resolves its esternal references by creating a new file with only internal refereces

$ bundle-api-spec --help
Code generation options:
  --api-spec, -i     Path to input OpenAPI spec file         [string] [required]
  --out-path, -o     Output path of the spec file            [string] [required]
  --api-version, -V  Version of the api. If provided, override the version in
                     the original spec file                             [string]

Options:
  --version  Show version number                                       [boolean]
  --help     Show help                                                 [boolean]

Requirements

  • node version >= 10.8.0

TEST

Unit test

Run test over utils' implementation

yarn test

End-to-end test

Run test over generated files

yarn e2e

Known issues, tradeoffs and throubleshooting

A model file for a definition is not generated

When using gen-api-models against a specification file which references an external definition file, some of such remote definitions do not result in a dedicated model file. This is somehow intended and the rationale is explained here. Quick takeaway is that to have a definition to result in a model file, it must be explicitly referenced by the specification file. In short: if you need to keep the references between the generated classes, the specification file must contain all the schema definitions. See example below.

example:

if the Pets schema uses the Pet, import both into the main document

components:
  schemas:
    Pets:
        $ref: "animal.yaml#/Pets"
    Pet:
        $ref: "animal.yaml#/Pet"

animal.yaml

Pets:
  type: array
  items:
    $ref: '#/definitions/Pet'
Pet:
  type: "object"
  required:
    - name
  properties:
    name:
      type: string

Migration from old versions

Generated code is slightly different from v4 as it implements some bug fixes that result in breaking changes. Here's a list of what to be aware of:

from 4.3.0 to 5.x

  • On request type definitions, parameters are named after the name field in the spec. This applies to both local and global parameters. In the previous version, this used to be true only for local ones, while global parameters were named after the parameter's definition name.
  • The above rule doesn't apply to headers: in case of a security definition or a global parameter which has in: header, the definition name is considered, as the name attribute refers to the actual header name to be used as for OpenApi specification.
  • Generated decoders now support multiple success codes (i.e. 200 and 202), so we don't need to write custom decoders for such case as we used to do.
  • When using gen-api-models command, --request-types flag must be used explicitly in order to have requestTypes file generated.
  • Parameters that has a schema reference (like this) now use the name attribute as the parameter name. It used to be the lower-cased reference's name instead.
  • The script creates the destination folder automatically, there is no need to mkdir anymore.
  • Both ranged number and integers now correctly include upper bound values. This is achieved without using the add 1 trick implemented in [#182], which is reverted. Breaking changes may arise in target application if values are assigned to variables of type WithinRangeInteger or WithinRangeNumber. See [#205].

from 4.0.0 to 4.3.0

  • Attributes with type: string and format: date used to result in a String definition, while now produce Date. #184
  • Allow camel-cased prop names. #183
  • Numeric attributes with maximum value now produce a WithinRangeInteger which maximum is the next integer to solve off-by-one comparison error. #182

openapi-codegen-ts's People

Contributors

aledore avatar arcogabbo avatar aymen94 avatar balanza avatar burnedmarshal avatar cloudify avatar dependabot-support avatar dependabot[bot] avatar fabriziopapi avatar francescopersico avatar garma00 avatar gquadrati avatar gunzip avatar infantesimone avatar lussoluca avatar mccalv avatar michaeldisaro avatar pagopa-github-bot avatar raicastino avatar ruphy avatar snyk-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

openapi-codegen-ts's Issues

Custom Header brokes the autogen client

I try to use this tool to generate the client from this swagger

In the swagger we have a custom X-Request-Id header in request and response and this is a problem for the tool.

I found that the only allowed headers are:
export declare type RequestHeaderKey = "Accept-Encoding" | "Authorization" | "Content-Type" | "Host" | "If-None-Match" | "Ocp-Apim-Subscription-Key" | "X-Functions-Key";

Optional Header can be undefined

Issue

If in the openapi v3 there is an optional header for the request the generated client expects it as param (can be undefined)

Example openapi:

 "headers" : {
    "X-Request-Id" : {
      "description" : "This header identifies the call",
      "schema" : {
        "type" : "string"
      }
    }
  }

Expected:

I want to ignore the optional header. Now I'm forced to pass it to the client.

Support OpenAPI v3 - GSOC

I expect

This repo supports OpenAPI v3

Instead

Just supports Swagger v2

Notes

  • syntax validation of current tests #92 @aymen94
  • familiarize with io-utils and basic oas3 parsing #74
  • identify a strategy for supporting both Swagger 2 and OAS3
  • create tests for OAS3
  • basic OAS3 support - proof of concept
  • implement OAS3 basic
  • define all other tests
  • implement all the remaining tests and be consistent with the older ones

This is for Google Summer of Code

Support referencing a property

We have examples of specifications using reference not only to a definition but to one specific property. Is it allowed? Reading OpenAPI docs I found neither confirmation nor disprove, and online validators don't complaint.

We should support such case in our generator.

Example

definitions:
  Item:
    type: object
    properties:
      status:
        type: string
      revocation_date:
        $ref: '#/definitions/Otp/properties/expires_at'
      revocation_reason:
        type: string
        minLength: 1
  Otp:
    type: object
    properties:
      code:
        type: string
      expires_at:
        type: string
        format: UTCISODateFromString
        description: A date-time field in ISO-8601 format and UTC timezone.
      ttl:
        type: number
    required:
      - code
      - expires_at
      - ttl

Handle default error types in decoders generation

Expected behavior

The gen-api-models command generates right code when specs don't specify any type for error responses (!== 200)

Actual behavior

The gen-api-models command generats wrong code when specs don't specify any type for error responses and the command uses the default error type as a fallback --default-error-type

Fix swagger syntax of api.yaml. swagger2 does not support `oneOf`

I expect

syntax of src/tests/api.yaml to be correct

Instead

opening on swagger editor I get the following error:

Structural error at definitions.OneOfTest
should NOT have additional properties
additionalProperty: oneOf
Jump to line 69

Note

This is because gen-api-models supports oneOf schema feature from OAS3. This ticket
could be closed once OAS3 support will be completed:

  • the swagger2 schema will remove this test from the yaml
  • the openapi3 schema will support this use-case

Array conversion

I am this parameter in a request

 {
    "name" : "bundleType",
    "in" : "query",
    "description" : "Boundles's type",
    "required" : false,
    "style" : "form",
    "explode" : true,
    "schema" : {
      "type" : "string",
      "enum" : [ "GLOBAL", "PRIVATE", "PUBLIC" ]
    }
  }

the generated class is

// Request type definition
export type GetBundlesByPSPT = r.IGetApiRequestType<
  {
    readonly JWT: string;
    readonly limit?: number;
    readonly "bundle-type"?: array;
    readonly page?: number;
    readonly "psp-code": string;
    readonly name?: string;
  },
  "Authorization",
  never,
  r.IResponseType<200, Bundles, "X-Request-Id">
>;

I have a compile error due to the line readonly "bundle-type"?: array;

How I fixed it manually

readonly "bundle-type"?: Array<string>;

Support minLength=0 on Yaml->Ts generator

Issue Description
Yaml->Ts Generator miss minLength=0 from Yaml Def and generate wrong types (just string instead of WithinRangeString)

Current Behavior

Yaml Source:

EmptyString:
      type: string
      minLength: 0
      maxLength: 0

LimitedString:
      type: string
      minLength: 0
      maxLength: 4000

Ts Output:

export type EmptyString = t.TypeOf<typeof EmptyString>;
export const EmptyString = t.string;

export type LimitedString= t.TypeOf<typeof LimitedString>;
export const LimitedString= t.string;

Expected/Correct Behavior
Ts Output:

export type EmptyString = t.TypeOf<typeof EmptyString>;
export const EmptyString = WithinRangeString(0, 1);

export type LimitedString= t.TypeOf<typeof LimitedString>;
export const LimitedString= WithinRangeString(0, 4001);

Solution:
Into macros.njk file, change
{% elif definition.minLength and definition.maxLength %}
TO
{% elif definition.minLength!==undefined and definition.maxLength!==undefined %}
In this way, 0 value will be recognized and used

`Buffer` type breaks generated SDKs

Operations which produce octet/stream responses will be resolved into generated definitions using Buffer as defined in #293

This breaks the build of SDK packages as Buffer is not a native Typescript type

Example

swagger: '2.0'
info:
  version: 1.0.0
  title: API
host: localhost
basePath: /api/v1
schemes:
  - https
paths:
  /mypath:
    get:
      summary: An endpoint
      produces:
        - application/octet-stream
      responses:
        '200':
          description: Success
          schema:
            format: binary
            type: string
        '400':
          description: Bad Request
        '500':
          description: Internal Server Error
requestTypes.ts:2047:8 - error TS4016: Type parameter 'A0' of exported function has or is using private name 'Buffer'.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.