Giter VIP home page Giter VIP logo

swagger-typescript-api's People

Contributors

ajmnz avatar allcontributors[bot] avatar azz avatar brookjordan avatar dependabot[bot] avatar emilecantin avatar feargalicious avatar greenkeeper[bot] avatar ivanovart avatar js2me avatar juliusbaranauskas avatar kel666 avatar koolmonke avatar mvbraathen avatar nikalun avatar pataiadam avatar qboot avatar seivan avatar smorimoto avatar soarc avatar to-long 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  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  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

swagger-typescript-api's Issues

Additional properties map to empty interfaces (OpenAPI v3)

Hi!

Thinks for a great library, we use it quite a lot for generating API types for our frontend, based on the backend spec.

However, I'm having issues with additionalProperties (using the OpenAPI v3 spec) mapping to empty interfaces.

As an example from our source code, we have the following Typescript interfaces:

export type Primitive = string | number | boolean | null

export interface PrimitiveMap {
  [key: string]: Primitive
}

We use tsoa to generate an OpenAPI v3 spec which looks something like this:

"Primitive": {
  "anyOf": [
    {
      "type": "string"
    },
    {
      "type": "number",
      "format": "double"
    },
    {
      "type": "boolean"
    },
    {
      "type": "number",
      "enum": [
        null
      ],
      "nullable": true
    }
  ]
},
"PrimitiveMap": {
  "properties": {},
  "type": "object",
  "additionalProperties": {
    "$ref": "#/components/schemas/Primitive"
  }
},

AFAICT, the spec looks right. But when I generate the types with swagger-typescript-api I get the following results:

export type Primitive = string | number | boolean | "null" | (string & number & boolean & "null");

export interface PrimitiveMap {}

Notice how the PrimitiveMap is empty.

Instead, I would have expected:

export type Primitive = string | number | boolean | "null" | (string & number & boolean & "null");

export interface PrimitiveMap {
  [key: string]: Primitive
}

All help would be much appreciated!

Type error for parameter with explode

Getting type error in addQueryParams
using typescript 3.9

this.request<any, any>(`/something/${this.addQueryParams(query)}`, "GET", params, null)
Argument of type '{ params?: QueryParams | undefined; } | undefined' is not assignable to parameter of type 'Record<string, string | number | boolean | string[] | number[] | undefined> | undefined'.
  Type '{ params?: QueryParams | undefined; }' is not assignable to type 'Record<string, string | number | boolean | string[] | number[] | undefined>'.
    Property 'params' is incompatible with index signature.
      Type 'QueryParams | undefined' is not assignable to type 'string | number | boolean | string[] | number[] | undefined'.
        Type 'QueryParams' is not assignable to type 'string | number | boolean | string[] | number[] | undefined'.
          Type 'QueryParams' is missing the following properties from type 'number[]': length, pop, push, concat, and 28 more.

where QueryParams is generated as

export interface QueryParams {
  /**
   * Page number
   */
  page?: number | null;

  /**
   * Page size
   */
  "page-size"?: number | null;
}
openapi: 3.0.1
info:
  title: API
  description: Documentation
  version: "0.1"
paths:
  /something/:
    get:
      operationId: gets
      parameters:
      - name: params
        in: query
        required: false
        explode: true
        schema:
          $ref: '#/components/schemas/QueryParams'

components:
  schemas:
    QueryParams:
      type: object
      properties:
        page:
          minimum: 0
          type: integer
          description: Page number
          format: int32
          nullable: true
        page-size:
          minimum: 0
          type: integer
          description: Page size
          format: int32
          nullable: true

With explode set the query url would have ?page=0&page-size=0
Any help would be appreciated.

Trying to convert Amazon API gives Error

SyntaxError: An identifier or keyword cannot immediately follow a numeric literal.

SyntaxError: An identifier or keyword cannot immediately follow a numeric literal. (161:4)
159 | NONE = "NONE",
160 | FEED = "FEED",

161 | 2DBARCODE = "2D_BARCODE",
| ^
162 | INTERACTIVE = "INTERACTIVE"
163 | }
164 |

The corresponding schema can be seen here

The error is resulting variable name start with number which is against JS variable naming rules.
I will see if I can fix and raise a PR but if you have a solution already please share

Self signed certificate error

My dev machine use swagger endpoint on https with a self-signed certificate.
If I run swagger-typescript-api I get this error:

(node:14680) UnhandledPromiseRejectionWarning: Error: self signed certificate in certificate chain at TLSSocket.onConnectSecure (_tls_wrap.js:1498:34) at TLSSocket.emit (events.js:315:20) at TLSSocket._finishInit (_tls_wrap.js:940:8) at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:698:12) (Use node --trace-warnings ...to show where the warning was created) (node:14680) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag--unhandled-rejections=strict(see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2) (node:14680) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Would be possibile to add an options to accept also self-signed certiificate?

Tnx
Fabio

Readonly Prop and .d.ts option

Currently, the plugin does not reflect readonly properties. Also, there should be an option to allow export to .d.ts file

additionalProperties: true

When a swagger model contains additionalProperties: true I think that the created TypeScript interfaces should deal with this somehow.
Maybe by adding [key: string]?: any,, at least as a stop-gap.

Remove const enum from default template

Hi,

I really liked your library but I have an issue with a React project. Create React App project does not support "const enums" because of Babel. You can look at link for more information: facebook/create-react-app#5681

Could you remove "const" modifier from "enum" at client.mustache?

P.S: I know that in a ideal world I should create an issue at Create React App project but it looks like they will or can not fix it. So I create here.

Handling of nullable for $ref in OpenAPI 3.0

I have an API that needs to differentiate between properties that are T | null and T | undefined. It looks like using nullable works for basic types but is ignored for $ref:

schemas:
  TestObject:
    type: object
    properties:
      stringMaybeUndefined:
        type: string
      stringMaybeNullA:
        type: string
        nullable: true
      stringMaybeNullB:
        anyOf:
        - type: string
        nullable: true
      otherObjectMaybeUndefined:
        $ref: "#/components/schemas/OtherObject"
      otherObjectMaybeNullA:
        $ref: "#/components/schemas/OtherObject"
        nullable: true
      otherObjectMaybeNullB:
        anyOf:
        - $ref: "#/components/schemas/OtherObject"
        nullable: true
    required:
    - stringMaybeNullA
    - stringMaybeNullB
    - otherObjectMaybeNullA
    - otherObjectMaybeNullB

  OtherObject:
    type: object

Generates:

export interface TestObject {
  stringMaybeUndefined?: string;
  stringMaybeNullA?: string | null;
  stringMaybeNullB?: string;
  otherObjectMaybeUndefined?: OtherObject;
  otherObjectMaybeNullA?: OtherObject;
  otherObjectMaybeNullB?: OtherObject;
}

Note: The OpenAPI 3.1 syntax

oneOf:
- $ref: "#/components/schemas/OtherObject"
- type: 'null'

works, but I'm forced to use OpenAPI 3.0 because of constraints from other tooling.

Can this be achieved somehow?

Edit, relevant resources:

If I interpret these (long) discussions correctly, at least the allOf wrapping should work?

Underscores are omitted from enum keys

Starting from version 1.12 enum values like this ERROR_NO_KEY converted to ERRORNOKEY = "ERROR_NO_KEY" which is incorrect and ideally should remain the same.

Unexpected token '.' on v4

Hi, when running v4 with the following params I am getting an error. Running on node v12.18.3. I can include the swagger definition if needed.

swagger-typescript-api -p "url" -o ./src/api -n file.ts

\node_modules\swagger-typescript-api\src\routes.js:415
tags?.length && * @tags ${tags.join(", ")},
^

SyntaxError: Unexpected token '.'
at wrapSafe (internal/modules/cjs/loader.js:1053:16)
at Module._compile (internal/modules/cjs/loader.js:1101:27)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)
at Module.load (internal/modules/cjs/loader.js:985:32)
at Function.Module._load (internal/modules/cjs/loader.js:878:14)
at Module.require (internal/modules/cjs/loader.js:1025:19)
at require (internal/modules/cjs/helpers.js:72:18)
at Object. (C:\Code\SBIR_TeacherPortal\node_modules\swagger-typescript-api\src\index.js:12:38)
at Module._compile (internal/modules/cjs/loader.js:1137:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)

Question: Is there a way to not add List at end of method name

I have a spring rest endpoint like:

@RestController
@RequestMapping("/util")
public class UtilResource {

    @Transactional
    @GetMapping
    @RequestMapping("/generator")
    public void generate(@RequestParam("count") int count) {

It generates below Typescript code but I do not want List at end of method name. Is there way to prevent that?

util = {
    /**
     * @name generatorList
     * @request GET:/util/generator
     */
    generatorList: (query?: { count?: number }, params?: RequestParams) =>
      this.request<any, any>(`/util/generator${this.addQueryParams(query)}`, "GET", params),
  };

Bug: Generating invalid code in composed schema contexts

An invalid Typescript type is generated in contexts where all of the following criteria are met:

  • The schema is a composed schema with an allOf specification
  • The allOf contains at least two type specifications
  • One of the allOf types contains an array property of enum values
  • That array contains an enum value with a hyphen (-)

I suspect the issue to be the following code snippet:

if (content.includes(" & ")) {
return content.split(" & ").map(checkAndRenameModelName).join(" & ");
}
if (content.includes(" | ")) {
return content.split(" | ").map(checkAndRenameModelName).join(" | ");
}

Since checkAndRenameModelName will find a hyphen in the described situation, Lodash's startCase function and whitespace removal is executed on the whole subtype, causing distortions in the output as shown below (undesired casing, missing punctuation and missing spacing). It might be a sufficient fix to just remove the checkAndRenameModelName there.

Swagger specification for reproduction

openapi: 3.0.1
info:
  title: Test
  version: test
paths: {}
components:
  schemas:
    Test:
      type: object
      allOf:
        - type: object
          properties:
            x:
              type: array
              items:
                type: string
                enum:
                  - A-B
        - type: object
          properties:
            y:
              type: string

Generated code

export type Test = XAB & { y?: string };

All properties are always optional

Hi,
I am wondering why from this schema:

Manufacturer: {
  type: "object",
  properties: {
    id: {
      type: "string",
      format: "uuid"
    },
    created: {
      type: "string",
      format: "date-time"
    },
    modified: {
      type: "string",
      format: "date-time"
    },
    name: {
      type: "string",
      nullable: true
    },
    identificationNumber: {
      type: "string",
      nullable: true
    }
  },
  additionalProperties: false
},

I am getting this:

export interface Manufacturer {
  id?: string;
  created?: string;
  modified?: string;
  name?: string | null;
  identificationNumber?: string | null;
}

instead of this:

export interface Manufacturer {
  id: string;
  created: string;
  modified: string;
  name: string | null; //or name?: string;
  identificationNumber: string | null;  //or identificationNumber?: string;
}

Am I doing something wrong?

enums with spaces throw an error

For example I have:

enum: [resolved, new, in progress]

And I get this error:

☄️  start generating your typescript api
SyntaxError: An enum member name must be followed by a ',', '=', or '}'. (19:6)
  17 |   resolved = "resolved",
  18 |   new = "new",
> 19 |   in progress = "in progress" 
     |      ^
  20 |  }
  21 | 
    at t (/project/node_modules/prettier/parser-typescript.js:1:287)
    at Object.parse (/project/node_modules/prettier/parser-typescript.js:14:2633274)
    at Object.parse (/project/node_modules/prettier/index.js:11370:19)
    at coreFormat (/project/node_modules/prettier/index.js:14784:25)
    at format (/project/node_modules/prettier/index.js:15019:75)
    at formatWithCursor (/project/node_modules/prettier/index.js:15035:12)
    at /project/node_modules/prettier/index.js:51620:12
    at Object.format (/project/node_modules/prettier/index.js:51640:12)
    at /project/node_modules/swagger-typescript-api/src/index.js:89:39 {

This is because it doesn’t add quotes around the enum declaration. That is, instead of outputting:

export enum Enum {
  resolved = "resolved",
  new = "new",
  in progress = "in progress",
}

should be:

export enum Enum {
  resolved = "resolved",
  new = "new",
  "in progress" = "in progress",
}

Output to JS

Is any way to generate swagger.json to JS (instead of TS)?

Broken types for arrays of union types

The latest release update (version 1.8.1) introduced array types using the typical brackets-suffixed notation (${content}[]). However, the generator seems to produce faulty type definitions when the array is to contain a union of types or a union of enum values. That problem has not occured with previous versions using the Array<T> notation. An easy fix respecting the intentions of release 1.8.1 may be to put the content type inside parentheses to which the brackets-suffix can be applied.

Screenshots of the problem

Original Swagger definition:
image

Generated code: 1.8.1 generated type to the left, pre-1.8.1 generated type to the right
image

String enums

Hello,
Is there any way how to easily change how enums are generated?

Shema

DayOfWeek: {
  enum: [
    "Sunday",
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday"
  ],
  type: "string"
},

Instead of this

export enum DayOfWeek {
  Sunday = "Sunday",
  Monday = "Monday",
  Tuesday = "Tuesday",
  Wednesday = "Wednesday",
  Thursday = "Thursday",
  Friday = "Friday",
  Saturday = "Saturday",
}

I would like to have this

export type DayOfWeek = 
  | "Sunday" 
  | "Monday" 
  | "Tuesday" 
  | "Wednesday" 
  | "Thursday" 
  | "Friday" 
  | "Saturday";

Thanks in advance

Generated client is not compatible when typescript has noUnusedParameters set to true

When using this library with my project, that has the tsconfig setting of noUnusedParameters set to true, I get errors with the generated client at safeParseResponse

This is because neither the E generic type is used, nor is the captured e variable in the catch handler (see attached)

image

I resolve this locally by prefixing both variables with an underscore, but it would be nice not to have to re-apply this every time I regenerate the client.

Looking at the client template it appears the E generic type is sometimes used, when the config setting generateResponses is specified (which I'm not currently), but not in all instances, so fix will need to respect that.

But the caught e variable is never used, so that could simply be removed?

Allow baseUrl to be set dynamically

As a developer I'd like to be able to dynamically point the client's baseUrl at a previously unspecified url. This is useful because many CI/CD pipelines dynamically generates urls and it would be useful to dynamically set the url without re-generating the client code.
I could see this done as an environment variable that sets the baseUrl, and some switch in the command to insert the environment variable.
Example:

npx swagger-typescript-api --dynamic-url
public baseUrl: string = process.env.CLIENT_BASE_URL;

Pattern fields in Path Item Object are treated as operations

My example is Swagger 2, I'm not sure if OpenAPI 3.x specs are affected as well (I only work in Swagger still).

Path Items are able to extend the schema using ^x- fields, docs: https://swagger.io/specification/v2/#patterned-fields-1

Example spec

"paths": {
    "/foo/bar": {
        "x-swagger-router-controller": "foo",
        "get": { ... }
    }
}

Generated Client

export class Api<SecurityDataType = any> extends HttpClient<SecurityDataType> {
  example = {
    /**
     * @name xSwaggerRouterControllerFoo
     * @request X SWAGGER ROUTER CONTROLLER:/foo/bar
     */
    xSwaggerRouterControllerFoo: (params?: RequestParams) =>
      this.request<any, any>(`/foo/bar`, "X SWAGGER ROUTER CONTROLLER", params),
...

Generated Namespace

/**
 * @name xSwaggerRouterControllerFoo
 * @request X SWAGGER ROUTER CONTROLLER:/foo/bar
 */
export namespace XSwaggerRouterControllerFoo {
  export type RequestQuery = {};
  export type RequestBody = never;
  export type ResponseBody = any;
}

The expected behavior is that these extensions would be ignored when collating the operations.

It might be as simple as replacing this omit with a pick and a hard-coded list of verbs.

_.omit(requestInfoByMethodsMap, "parameters"),

But given this comment I didn't presume to open a PR myself.
// TODO: refactor that hell

TypeScript 3.7.5 error in the strict mode inside addQueryParams method

private addQueryParams(query: object): string {
    const keys = Object.keys(query);
    return keys.length ? (
      '?' +
      keys.reduce((paramsArray, param) => [
        ...paramsArray,
        param + '=' + encodeURIComponent(query[param])
      ], []).join('&')
    ) : ''
  }

src/templates/client.mustache

(parameter) param: string
No overload matches this call.
Overload 1 of 3, '(callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string', gave the following error.
Type 'string[]' is not assignable to type 'string'.
Overload 2 of 3, '(callbackfn: (previousValue: never[], currentValue: string, currentIndex: number, array: string[]) => never[], initialValue: never[]): never[]', gave the following error.
Type 'string[]' is not assignable to type 'never[]'.
Type 'string' is not assignable to type 'never'.ts(2769)
lib.es5.d.ts(1350, 24): The expected type comes from the return type of this signature.
lib.es5.d.ts(1356, 27): The expected type comes from the return type of this signature.

I suggest following code:

  private addQueryParams(query: {[key:string]:string|number|boolean}): string {
    const keys = Object.keys(query);
    return keys.length === 0 ? ''
      : '?' + keys.map(key => encodeURIComponent(key) + '=' + encodeURIComponent(query[key])).join('&')
  }

Support for dates

Problem

Currently this library seems to ignore format information on data types. Most formats just add some metadata, so they are fine to skip, but format: date-time actually has semantic meaning that could be understood by a typescript client.

Right now, if we have a model definition like this:

"SomeType": {
  "properties": {
    "created": {
      "type": "string",
      "format": "date-time"
    }
  }
}

it generates this kind of model:

interface SomeType {
  created: string;
}

There is no way for the client to know that this string represents a date. It would be really useful if it could generate this model instead:

interface SomeType {
  created: Date;
}

Implementation

I realize that adding custom data transformations is tricky and probably outside the scope of this library. But making only dates work might actually be possible.

Serializing data would be relatively straightforward. JSON serialization would just work. For form serialization there would need to be a special case for Date instances.

The main challenge for implementation seems to make safeParseResponse work, because type information is not available at run-time. I think the only way to make that work is generate a custom parser per API type. Is that outside the scope of this library too?

Further steps

I'd be happy to lend a hand if you think this would be a useful addition to the library. I am aware that templates can be customized, so I might also be able to work around this if necessary.

If an error occurs in a web call then it throws 'Response', it would be nice to be able to customize this behaviour

Converting the http response into the return data or an error takes place in

return fetch(requestUrl, requestOptions).then(async (response) => {
  const data = await this.safeParseResponse<T, E>(response);
  if (!response.ok) throw data;
  return data;
});

If this could be placed into a protected function, then its could be sub classed and overridden.

i.e.

    return fetch(requestUrl, requestOptions).then(async (response) => {
      return await this.handleResponse(response);
    });
  }


  protected async handleResponse<T, E>(response: Response): Promise<HttpResponse<T, E>> {
    const data = await this.safeParseResponse<T, E>(response);
    if (!response.ok) throw data;
    return data;
  }

Note safeParseResponse also needs changing to protected, it may also be a good idea to export HttpResponse

Full Template.

export type RequestParams = Omit<RequestInit, "body" | "method"> & {
  secure?: boolean;
}

{{#hasQueryRoutes}}
export type RequestQueryParamsType = Record<string | number, any>;
{{/hasQueryRoutes}}

interface ApiConfig<{{#apiConfig.generic}}{{name}},{{/apiConfig.generic}}> {
  baseUrl?: string;
  baseApiParams?: RequestParams;
  securityWorker?: (securityData: SecurityDataType) => RequestParams;
}

{{#generateResponses}}
/** Overrided Promise type. Needs for additional typings of `.catch` callback */
type TPromise<ResolveType, RejectType = any> = Omit<Promise<ResolveType>, "then" | "catch"> & {
  then<TResult1 = ResolveType, TResult2 = never>(onfulfilled?: ((value: ResolveType) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: RejectType) => TResult2 | PromiseLike<TResult2>) | undefined | null): TPromise<TResult1 | TResult2, RejectType>;
  catch<TResult = never>(onrejected?: ((reason: RejectType) => TResult | PromiseLike<TResult>) | undefined | null): TPromise<ResolveType | TResult, RejectType>;
}
{{/generateResponses}}

interface HttpResponse<D extends unknown, E extends unknown = unknown> extends Response {
  data: D | null;
  error: E | null;
}

enum BodyType {
  Json,
  {{#hasFormDataRoutes}}
  FormData,
  {{/hasFormDataRoutes}}
}

class HttpClient<{{#apiConfig.generic}}{{name}},{{/apiConfig.generic}}> {
  public baseUrl: string = "{{apiConfig.baseUrl}}";
  private securityData: SecurityDataType = (null as any);
  private securityWorker: null | ApiConfig<{{#apiConfig.generic}}{{name}},{{/apiConfig.generic}}>["securityWorker"] = null;
  
  private baseApiParams: RequestParams = {
    credentials: 'same-origin',
    headers: {
      'Content-Type': 'application/json'
    },
    redirect: 'follow',
    referrerPolicy: 'no-referrer',
  }

  constructor(apiConfig: ApiConfig<{{#apiConfig.generic}}{{name}},{{/apiConfig.generic}}> = {}) {
    Object.assign(this, apiConfig);
  }

  public setSecurityData = (data: SecurityDataType) => {
    this.securityData = data
  }

  {{#hasQueryRoutes}}
  private addQueryParam(query: RequestQueryParamsType, key: string) {
    return encodeURIComponent(key) + "=" + encodeURIComponent(Array.isArray(query[key]) ? query[key].join(",") : query[key])
  }

  protected addQueryParams(rawQuery?: RequestQueryParamsType): string {
    const query = rawQuery || {};
    const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]);
    return keys.length ? `?${keys.map(key =>
      typeof query[key] === "object" && !Array.isArray(query[key]) ?
        this.addQueryParams(query[key] as object).substring(1) :
        this.addQueryParam(query, key)).join("&")
      }` : "";
  }
  {{/hasQueryRoutes}}

  private bodyFormatters: Record<BodyType, (input: any) => any> = {
    [BodyType.Json]: JSON.stringify,
    {{#hasFormDataRoutes}}
    [BodyType.FormData]: (input: any) =>
      Object.keys(input).reduce((data, key) => {
        data.append(key, input[key]);
        return data;
      }, new FormData()),
    {{/hasFormDataRoutes}}
  }

  private mergeRequestOptions(params: RequestParams, securityParams?: RequestParams): RequestParams {
    return {
      ...this.baseApiParams,
      ...params,
      ...(securityParams || {}),
      headers: {
        ...(this.baseApiParams.headers || {}),
        ...(params.headers || {}),
        ...((securityParams && securityParams.headers) || {})
      }
    }
  }
  
  protected safeParseResponse = <T = any, E = any>(response: Response): Promise<HttpResponse<T, E>> => {
    const r = response as HttpResponse<T, E>;
    r.data = null;
    r.error = null;

    return response
      .json()
      .then((data) => {
        if (r.ok) {
          r.data = data;
        } else {
          r.error = data;
        }
        return r;
      })
      .catch((e) => {
        r.error = e;
        return r;
      });
  }
  
  public request = <T = any, E = any>(
    path: string,
    method: string,
    { secure, ...params }: RequestParams = {},
    body?: any,
    bodyType?: BodyType,
    secureByDefault?: boolean,
  ): {{#generateResponses}}TPromise<HttpResponse<T, E>>{{/generateResponses}}{{^generateResponses}}Promise<HttpResponse<T>>{{/generateResponses}} => {
    const requestUrl = `${this.baseUrl}${path}`;
    const secureOptions = (secureByDefault || secure) && this.securityWorker ? this.securityWorker(this.securityData) : {};
    const requestOptions = {
      ...this.mergeRequestOptions(params, secureOptions),
      method,
      body: body ? this.bodyFormatters[bodyType || BodyType.Json](body) : null,
    }

    return fetch(requestUrl, requestOptions).then(async (response) => {
      return await this.handleResponse(response);
    });
  }


  protected async handleResponse<T, E>(response: Response): Promise<HttpResponse<T, E>> {
    const data = await this.safeParseResponse<T, E>(response);
    if (!response.ok) throw data;
    return data;
  }
}

Package not generating the types for requestBodies for Open API 3

Hello Team, following is my swagger, it generates the types for schema whereas it doesn't generates the types for requestbodies. Have a look into this.

{
  "openapi": "3.0.2",
  "info": {
    "title": "Movie",
    "version": "0.0.1",
    "description": "Movie API",
    "contact": {
      "name": "Tech",
      "url": "https://production.com",
      "email": "[email protected]"
    }
  },
  "servers": [
    {
      "url": "http://localhost:3000/api",
      "description": "local_server"
    },
    {
      "url": "https://production.com/api",
      "description": "prod_server"
    }
  ],
  "tags": [
    {
      "name": "movie",
      "description": "Everything about shipping rate"
    }
  ],
  "paths": {
    "/v1/movie/create": {
      "post": {
        "tags": [
          "movie"
        ],
        "description": "Get by its id.",
        "operationId": "V1CreateMovieAction",
        "requestBody": {
          "$ref": "#/components/requestBodies/V1CreateMovieRequestBody"
        },
        "responses": {
          "200": {
            "description": "found and returned successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1CreateMovieResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/movie/update": {
      "post": {
        "tags": [
          "movie"
        ],
        "description": "Get by its id.",
        "operationId": "V1UpdateMovieAction",
        "requestBody": {
          "$ref": "#/components/requestBodies/V1CreateMovieRequestBody"
        },
        "responses": {
          "200": {
            "description": "found and returned successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1CreateMovieResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "V1CreateMovieResponse": {
        "type": "object",
        "properties": {
          "msg": {
            "type": "string"
          }
        }
      }
    },
    "requestBodies": {
      "V1CreateMovieRequestBody": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "data": {
                  "type": "object",
                  "properties": {
                    "director": {
                      "type": "string"
                    },
                    "yearReleased": {
                      "type": "number"
                    },
                    "movieName": {
                      "type": "string"
                    }
                  }
                }
              },
              "example": {
                "data": {
                  "director": "Akshat",
                  "yearReleased": 2020,
                  "movieName": "MRK"
                }
              }
            }
          }
        }
      }
    }
  }
}

Async securityWorker

Hi, Could you change template code so that securityWorker will be async function. I need this so that I can check my JWT token is expired or not and update it if it is expired. I can not do this with current securityWorker.

I suggest you to change current template to below


type ApiConfig<SecurityDataType> = {
  baseUrl?: string;
  baseApiParams?: RequestParams;
  securityWorker?: (securityData: SecurityDataType) => Promise<RequestParams>;
};
  
  public request = async <T = any, E = any>(
    path: string,
    method: string,
    { secure, ...params }: RequestParams = {},
    body?: any,
    bodyType?: BodyType,
    secureByDefault?: boolean,
  ): Promise<T> =>{
    const securityRequestParams = (secureByDefault || secure) && (await this.securityWorker(this.securityData));
    return fetch(`${this.baseUrl}${path}`, {
      // @ts-ignore
      ...this.mergeRequestOptions(params, securityRequestParams),
      method,
      body: body ? this.bodyFormatters[bodyType || BodyType.Json](body) : null,
    }).then(async (response) => {
      const data = await this.safeParseResponse<T, E>(response);
      if (!response.ok) throw data;
      return data;
    });
  }

Multiple types for a property in Swagger 2 are not handled correctly.

The Swagger 2.0 spec didn't explicitly call out how mixed types are handled, like OpenAPI 3.0 does with oneOf.
Instead it falls back to JSON Schema Draft 4, which states:
https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.5.2

5.5.2.1. Valid values
The value of this keyword MUST be either a string or an array. If it
is an array, elements of the array MUST be strings and MUST be
unique.
String values MUST be one of the seven primitive types defined by the
core specification.

However, currently a swagger.json like like the following.

...
"properties": {
  "product_id": {
    "type": ["integer", "string"]
  }
}
...

produces a type like the following

export type SomeRequest = {
  product_id: integerString; // Should be `number | string`
}

Request cancellation support

First, thanks for the awesome library.

Would be really cool if we can extend the default template to support request cancellation...
for example as it's described here: https://react-query.tanstack.com/docs/guides/query-cancellation#using-fetch

what do you think? (in fact it should be easy to change and I can contribute if you give this idea a green light)

p.s I know that there is a possibility to provider a custom template, but would be nice to support it out of the box.

Unexpected token 'export' for >v3

Hi, I tried to understand for a very long time, but I ran out of ideas. After updating to version 3, tests began to fall with this error, in the place where I call the Api class. The project includes a dependency that was generated using swagger-typescript-api, everything was fine on version 2. Any idea why this is happening? Thanks

Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/en/ecmascript-modules for how to enable it.
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    /Users/r/project/specifications/lib/index.js:16
    export class HttpClient {
    ^^^^^^

    SyntaxError: Unexpected token 'export'

axios vulnerability

Hi there!

Just a friendly reminder to upgrade axios due to vulnerabilities :)
image
Best regards,
Mvbraathen

'prettier-plugin-organize-imports' affects a dependent project

Hi. I'm using swagger-typescript-api v4. 0. 4 as a dev dependency for the project.

The project uses 'prettier' with a specific configuration. The swagger-typescript-api sets the prettier plugin 'prettier-plugin-organize-imports' as dependencies. This plugin does not require any configuration and will work directly after installation. I tried disabling this plugin but couldn't. My team doesn't want to use this add-on for prettier.

Is it possible to move "prettier-plugin-organize-imports" to devDependencies/peerDependencies or delete it?
As an option, I suggest using another plugin with the configuration in prettierrc, for example https://github.com/trivago/prettier-plugin-sort-imports.

Or will anybody suggest a workaround for switch-off 'prettier-plugin-organize-imports'?

Underscores in interface names with specific schema definition.

Spring automatically generates schema definitions with generic types (see the example for details).
This causes the typescript interfaces to contain underscores in place of "«" symbols when such a schema is used.

Current result:
Page_TemplateResponseDto_

Expected result:
PageTemplateResponseDto

OA version - 2.0

Example schema:

{
   "Page«TemplateResponseDto»":{
      "type":"object",
      "properties":{
         "content":{
            "type":"array",
            "items":{
               "$ref":"#/definitions/TemplateResponseDto"
            }
         },
         "empty":{
            "type":"boolean"
         },
         "first":{
            "type":"boolean"
         },
         "last":{
            "type":"boolean"
         },
         "number":{
            "type":"integer",
            "format":"int32"
         },
         "numberOfElements":{
            "type":"integer",
            "format":"int32"
         },
         "pageable":{
            "$ref":"#/definitions/Pageable"
         },
         "size":{
            "type":"integer",
            "format":"int32"
         },
         "sort":{
            "$ref":"#/definitions/Sort"
         },
         "totalElements":{
            "type":"integer",
            "format":"int64"
         },
         "totalPages":{
            "type":"integer",
            "format":"int32"
         }
      },
      "title":"Page«TemplateResponseDto»"
   }
}

Custom Templates

Hi!
It'd be cool to be able to provide custom templates directory to this tool in order to tweak templates or just leverage the parser to generate custom models or something like that. It's all only about yet another argument e.g. --templates.

Would it be of your interest if I made a PR with this feature?

always optional type

Hi, i have schema

  EventsList:
    properties:
      rows:
        items:
          $ref: '#/definitions/EventRow'
        type: array
        x-omitempty: false
      items:
        items:
          $ref: '#/definitions/Event'
        type: array
        x-omitempty: false
      total:
        format: int64
        type: integer
        x-omitempty: false
      tableFormat:
        $ref: '#/definitions/TableFormat'
        x-omitempty: false
    type: object

and i am getting

export interface EventsList {
  rows?: EventRow[];
  items?: Event[];
  total?: number;
  tableFormat?: TableFormat;
}

is it somehow possible to get strick type? we also try x-nullable: false and it works, but x-omitempty no.

Name collision for /version endpoint

Another small issue I encountered: It is currently not possible to have a /version endpoint, because of a name collision with the internal version variable. For example:

---
openapi: 3.0.0
info:
  title: SomeAPI
paths:
  "/version":
    get:
      responses:
        '200':
          description: 
          content:
            application/json:
              schema:
                type: string

Relevant parts of generated code:

export class Api<SecurityDataType> {
  public baseUrl = "";
  public title = "SomeAPI";
  public version = "";

  // and later

  version = {
    /**
     * @name versionList
     * @request GET:/version
     */
    versionList: (params?: RequestParams) => this.request<string, any>(`/version`, "GET", params, null),
  };

Error:

Duplicate identifier 'version'.ts(2300)

Nullable not included in type definition

According to this information it should be possible to define a nullable type in OpenAPI 3.0.x like this:

schemas:
  StringNullable:
    type: string
    nullable: true

However this generates the following TypeScript:

export type StringNullable = string;

I was hoping it would create:

export type StringNullable = string | null;

invalid default templates path

The default templates path in index.js is invalid:

Running:

$ node index.js -p ./tests/schemas/v3.0/additional-properties.yaml -o ./tmp -n myApi.ts

results in:

✨ try to get swagger by path "[...]/swagger-typescript-api/tests/schemas/v3.0/additional-properties.yaml"
✨ try to read templates from directory "[...]/swagger-typescript-api/src/templates"
(node:33407) UnhandledPromiseRejectionWarning: Error: ENOENT: no such file or directory, open '[...]/swagger-typescript-api/src/templates/data-contracts.mustache'

I'll submit a PR shortly.

NodeJS support

NodeJS is not support fetch.
Needs to find a better way how to support nodejs usage generated Api module

Response type does not de-reference $refs

openapi: '3.0.0'

info:
  description: 'Description'
  version: 'latest'
  title: 'Title'

paths:
  '/api':
    get:
      operationId: getData
      responses:
        200:
          $ref: '#/components/responses/default'

components:
  responses:
    default:
      description: OK
      content:
        'application/json':
          schema:
            type: object
            properties:
              data: { type: string }

Generates the types:

export namespace api {

  /**
  * @name getData
  * @request GET:/api
  */
  export namespace GetData {
    export type RequestQuery = {};
    export type RequestBody = never;
    export type ResponseBody = any;
  }
}

However if the response type is inlined:

openapi: '3.0.0'

info:
  description: 'Description'
  version: 'latest'
  title: 'Title'

paths:
  '/api':
    get:
      operationId: getData
      responses:
        200:
          description: OK
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data: { type: string }

The correct type is output:

export namespace api {

  /**
  * @name getData
  * @request GET:/api
  */
  export namespace GetData {
    export type RequestQuery = {};
    export type RequestBody = never;
    export type ResponseBody = { data?: string };
  }
}

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.