cyclosproject / ng-swagger-gen

A Swagger 2.0 codegen for Angular
MIT License
220 stars 100 forks source link
angular api-client code-generator swagger typescript

ng-swagger-gen: A Swagger 2.0 code generator for Angular

This project is a NPM module that generates model interfaces and web service clients from a Swagger 2.0 specification. The generated classes follow the principles of Angular.

NOTICE: This project isn't actively maintained anymore. Since 2019 our development focus has shifted to ng-openapi-gen, and the Cyclos project, which maintains both projects has long ago moved to OpenAPI 3. As such, the maintainance of ng-swagger-gen is basically done by the community, via pull requests. However, as ng-swagger-gen has no automated tests, accepting PR's for new features is also challenging, because the original maintainers have no practical way to test if anything else is broken. As such, only important bug fixes will be accepted.

Angular / RxJS version compatibility:

OpenAPI 3

If you are interested in generating a client for services described using OpenAPI 3, take a look on ng-openapi-gen.

Major version upgrade notices

Design principles

Here are a few notes:

Requirements

The generator itself has very few requirements, basically json-schema-ref-parser, argparse and mustache.

However, starting with the version 1.0.0, the generated code requires both Angular 6.0+ and RxJS 6.0+. These versions are expressed as peerDependencies, so make sure you don't have unmet peer dependencies. If you are stuck on previous versions of Angular / RxJS, you can use ng-swagger-gen version as ~0.11.0, which supports Angular 4.3, and RxJS 5.5.

How to use it

In your project, run:

cd <your_angular_app_dir>
npm install ng-swagger-gen --save-dev
node_modules/.bin/ng-swagger-gen -i <path_to_swagger_json> [-o output_dir]

Where:

Please, run the ng-swagger-gen with the --help argument to view all available command line arguments.

Generated folder structure

The folder src/app/api (or your custom folder) will contain the following structure:

project_root
+- src
   +- app
      +- api
         +- models
         |  +- model1.ts
         |  +- model1.example.json
         |  +- ...
         |  +- modeln.ts
         |  +- modeln.example.json
         +- services
         |  +- tag1.service.ts
         |  +- ...
         |  +- tagn.service.ts
         +- api.module.ts
         +- api-configuration.ts
         +- base-service.ts
         +- models.ts
         +- services.ts

The files are:

Using a configuration file

On regular usage it is recommended to use a configuration file instead of passing command-line arguments to ng-swagger-gen. The default configuration file name is ng-swagger-gen.json, and should be placed on the root folder of your NodeJS project. Besides allowing to omit the command-line arguments, using a the configuration file allows a greater degree of control over the generation.

An accompanying JSON schema is also available, so the configuration file can be validated, and IDEs can autocomplete the file. If you have installed and saved the ng-swagger-gen module in your node project, you can use a local copy of the JSON schema on ./node_modules/ng-swagger-gen/ng-swagger-gen-schema.json. It is also possible to use the online version at https://github.com/cyclosproject/ng-swagger-gen/blob/master/ng-swagger-gen-schema.json.

It is also possible to specify the configuration file to use. This is useful when multiple APIs are generated. To specify a configuration file, use the argument --config or its short form, -c, like this:

ng-swagger-gen --config custom-config.json

When working with multiple APIs, it is advised to set a different prefix for each one. This impacts the generated global files, such as ApiModule and ApiConfiguration. The default prefix is Api, leading to those names. But, if the specified prefix in the configuration file is, for example, Customers, the generated files will be CustomersModule and CustomersConfiguration. The prefix support has been added in version 1.3.

Generating the configuration file

To generate a configuration file, run the following in the root folder of your project;

ng-swagger-gen --gen-config [-i path_to_swagger_json] [-o output_dir]

This will generate the ng-swagger-gen.json file in the current directory with the property defaults, plus the input Swagger JSON path (or URL) and the output directory that were specified together. Both are optional, and the file is generated anyway.

Configuration file reference

The supported properties in the JSON file are:

Configuration file example

The following is an example of a configuration file which will choose a few tags to generate, and chose not to generate the ApiModule class:

{
  "$schema": "./node_modules/ng-swagger-gen/ng-swagger-gen-schema.json",
  "swagger": "my-swagger.json",
  "includeTags": [
    "Blogs",
    "Comments",
    "Users"
  ],
  "apiModule": false
}

This will generate only the services for the chosen tags, and also skip the generation of any interfaces for models which are not used by any of the generated services.

Setting up a node script

Regardless If your Angular project was generated or is managed by Angular CLI, or you have started your project with some other seed (for example, using webpack directly), you can setup a script to make sure the generated API classes are consistent with the swagger descriptor.

To do so, create the ng-swagger-gen.json configuration file and add the following scripts to your package.json:

{
  "scripts": {
    "start": "ng-swagger-gen && ng serve",
    "build": "ng-swagger-gen && ng build -prod"
  }
}

This way whenever you run npm start or npm run build, the API classes will be generated before actually serving / building your application.

Also, if you use several configuration files, you can specify multiple times the call to ng-swagger-gen, like:

{
  "scripts": {
    "start": "ng-swagger-gen -c api1.json && ng-swagger-gen -c api2.json && ng serve",
    "build": "ng-swagger-gen -c api1.json && ng-swagger-gen -c api2.json && ng build -prod"
  }
}

Specifying the root URL / web service endpoint

The easiest way to specify a custom root URL (web service endpoint URL) is to use forRoot method of ApiModule and set the rootUrl property from there.

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    ApiModule.forRoot({rootUrl: 'https://some-root-url.com'}),
  ],
  bootstrap: [
    AppComponent
  ]
})
export class AppModule { }

Alternatively, you can inject the ApiConfiguration instance in some service or component, such as the AppComponent and set the rootUrl property there.

Passing request headers / customizing the request

To pass request headers, such as authorization or API keys, as well as having a centralized error handling, a standard HttpInterceptor should be used. It is basically an @Injectable that is called before each request, and can customize both requests and responses.

Here is an example:

@Injectable()
export class ApiInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // Apply the headers
    req = req.clone({
      setHeaders: {
        'ApiToken': '234567890'
      }
    });

    // Also handle errors globally
    return next.handle(req).pipe(
      tap(x => x, err => {
        // Handle this err
        console.error(`Error performing request, status code = ${err.status}`);
      })
    );
  }
}

Then, both the HttpInterceptor implementation and the injection token HTTP_INTERCEPTORS pointing to it must be provided in your application module, like this:

import { NgModule, Provider, forwardRef } from '@angular/core';
import { HTTP_INTERCEPTORS } from '@angular/common/http';

import { ApiInterceptor } from './api.interceptor';

export const API_INTERCEPTOR_PROVIDER: Provider = {
  provide: HTTP_INTERCEPTORS,
  useExisting: forwardRef(() => ApiInterceptor),
  multi: true
};

@NgModule({
  providers: [
    ApiInterceptor,
    API_INTERCEPTOR_PROVIDER
  ]
})
export class AppModule {}

Finer control over specific requests can also be achieved, such as:

To do so, just create another shared @Injectable(), for example, called ApiRequestConfiguration, which has state for such special cases. Then inject it on both the HttpInterceptor and in the client code that makes requests. Here is an example for such class for controlling the authentication:

import { Injectable } from '@angular/core';
import { HttpRequest } from '@angular/common/http';

/**
 * Configuration for the performed HTTP requests
 */
@Injectable()
export class ApiRequestConfiguration {
  private nextAuthHeader: string;
  private nextAuthValue: string;

  /** Set to basic authentication */
  basic(user: string, password: string): void {
    this.nextAuthHeader = 'Authorization';
    this.nextAuthValue = 'Basic ' + btoa(user + ':' + password);
  }

  /** Set to session key */
  nextAsSession(sessionKey: string): void {
    this.nextAuthHeader = 'Session';
    this.nextAuthValue = sessionKey;
  }

  /** Clear any authentication headers (to be called after logout) */
  clear(): void {
    this.nextAuthHeader = null;
    this.nextAuthValue = null;
  }

  /** Apply the current authorization headers to the given request */
  apply(req: HttpRequest<any>): HttpRequest<any> {
    const headers = {};
    if (this.nextAuthHeader) {
      headers[this.nextAuthHeader] = this.nextAuthValue;
    }
    // Apply the headers to the request
    return req.clone({
      setHeaders: headers
    });
  }
}

Then change the ApiInterceptor class to call the apply method. And, of course, add ApiRequestConfiguration to your module providers and inject it on your components or services.

Swagger extensions

The swagger specification doesn't allow referencing an enumeration to be used as an operation parameter. Hence, ng-swagger-gen supports the vendor extension x-type in operations, whose value could either be a model name representing an enumeration or Array<EnumName> or List<EnumName> (both are equivalents) to use an array of models.

Who uses this project

This project was developed by the Cyclos development team, and, in fact, the Cyclos REST API is the primary test case for generated classes. However, since Cyclos 4.12, the project has changed the API descriptor to OpenAPI 3. As such, a new generator was developed: ng-openapi-gen.

That doesn't mean that the generator works only for the Cyclos API. For instance, the following commands will generate an API client for Swagger's PetStore example, assuming Angular CLI is installed:

ng new petstore
cd petstore
npm install --save-dev ng-swagger-gen
node_modules/.bin/ng-swagger-gen -i http://petstore.swagger.io/v2/swagger.json