npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

zm-swagger-to-ts

v0.0.7

Published

Given a swagger.json file, generates a number of TypeScript files which can be used as models and model-driven forms in Angular 2 (and above)

Downloads

2

Readme

Swagger TypeScript code generator

Node module to generate TypeScript code for Angular (2 and above) based on Webapi meta data in Swagger v2 format.

Use it in combination with the sibling package angular-swagger-form-field to implement reactive forms.

See angular-swagger-form-field-sample for a sample how to use the generated classes.

Setup

Download the module with npm:

npm install --save-dev zm-swagger-to-ts

Usage in NodeJS

Create a simple .js file and run it using node path/to/file.js

You can then run this from npm by adding the node line from above as a task in your package.json

const { generateTSFiles } = require("zm-swagger-to-ts");

const config = {
  file: __dirname + "\\swagger.json"
};

generateTSFiles(
  config.file, // This can be either a file containing the Swagger json or the Swagger object itself
  {
    modelFolder: "./path/to/models",
    enumTSFile: "./path/to/models/enums.ts"
    // + optionally more configuration
  }
);

Usage in Gulp

gulp.config

Create a gulp.config file with the settings you want:

  • generateClasses (default: true) - If this flag is set to false, the generator will output only interfaces and enums. It will not output classes and validators.
"use strict";

module.exports = config();

function config() {
  var root = "./src/";
  var srcAppFolder = root + "app/";
  var folders = {
    // root
    root: root,
    // sources
    srcWebapiFolder: srcAppFolder + "models/webapi/",
    srcLanguagesFolder: root + "assets/i18n/",
    // swagger
    swaggerFolder: root + "swagger/"
  };
  var files = {
    swaggerJson: "swagger.json"
    //swaggerJson: 'swagger-zib.json',
    //swaggerJson: 'swagger-zib-v2.json',
  };

  var swagger = {
    url: "http://petstore.swagger.io/v2/swagger.json",
    //url: 'http://127.0.0.1/ZIB.WebApi.v2/swagger/docs/v1',
    swaggerFile: folders.swaggerFolder + files.swaggerJson,
    swaggerFolder: folders.swaggerFolder,
    swaggerTSGeneratorOptions: {
      modelFolder: folders.srcWebapiFolder,
      enumTSFile: folders.srcWebapiFolder + "enums.ts",
      enumI18NHtmlFile: folders.enumI18NHtmlFolder + "enum-i18n.component.html",
      enumLanguageFiles: [
        folders.srcLanguagesFolder + "nl.json",
        folders.srcLanguagesFolder + "en.json"
      ],
      generateClasses: true,
      modelModuleName: "webapi.models",
      enumModuleName: "webapi.enums",
      enumRef: "./enums",
      subTypePropertyName: "typeSelector",
      namespacePrefixesToRemove: [],
      typeNameSuffixesToRemove: [],
      typesToFilter: [
        "ModelAndView", // Springfox artifact
        "View" // Springfox artifact
      ]
    }
  };

  var config = {
    root: root,
    files: files,
    swagger: swagger
  };
  return config;
}

gulpfile.js

Create a gulpfile.js:

/*global __dirname */
"use strict";

var gulp = require("gulp");

var $ = require("gulp-load-plugins")({ lazy: true });
var args = require("yargs").argv;
var swaggerTSGenerator = require("zm-swagger-to-ts");
var request = require("request");
var source = require("vinyl-source-stream");

var config = require("./gulp.config");

//--------------- gulp tasks ---------------

gulp.task("default", ["show-help"]); // Set default gulp tasks
gulp.task("show-help", $.taskListing);

gulp.task("gen", ["gen-webapi"]);
gulp.task("gen-webapi", ["gen-webapi-download-swagger"], genWebapi);
gulp.task("gen-webapi-download-swagger", genWebapiDownloadSwagger);

//--------------- generator tasks ---------------

function genWebapi(done) {
  swaggerTSGenerator.generateTSFiles(
    config.swagger.swaggerFile,
    config.swagger.swaggerTSGeneratorOptions
  );
  done();
}
function genWebapiDownloadSwagger(done) {
  process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0"; // Ignore 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' authorization error

  return request
    .get({
      url: config.swagger.url,
      headers: {
        "User-Agent": "request",
        "content-type": "application/json"
      }
    })
    .pipe(customPlumber("Error gen-webapi-autorest"))
    .pipe(source(config.files.swaggerJson))
    .pipe($.streamify($.jsbeautifier(/*{ mode: 'VERIFY_AND_WRITE' }*/)))
    .pipe(gulp.dest(config.folders.swaggerFolder));
}

function customPlumber(errTitle) {
  return $.plumber({
    errorHandler: $.notify.onError({
      // Customizing error title
      title: errTitle || "Error running Gulp",
      message: "Error: <%= error.message %>",
      sound: "Glass"
    })
  });
}

function log(msg) {
  $.util.log($.util.colors.yellow(msg));
}

Execute the gulp task(s)

Download the swagger file and generate the code:

gulp gen

Generated files

validators.ts

The generated validators.ts is fixed (its always generated regardless of the Swagger). It contains some extra validators to implement validation rules for the Swagger which are not part of the standard Angular validators:

maxValueValidator;
minValueValidator;

base-model.ts

The generated base-model.ts is fixed (its always generated regardless of the Swagger). It contains the base class for all generated models. The next members can be used in your own software:

    /**
    * use $formGroup in angular's formBuilder to make a model driven (reactive) form.
    *   this.form = this.formBuilder.group({
    *       pet: this.pet.$formGroup,
    *   });
    */
    $formGroup: FormGroup;

    /**
    * add one or more additional validators to the control
    * @param key Name of the control (is the same as the name of the attached model property)
    * @param validators Validator(s) to add to the control
    */
    addValidatorToControl(controlName: string, validators: ValidatorFn | ValidatorFn[]) {
        ...
    }
}

sub-type-factory.ts

This class is used in the generated models to instantiate subTypes. It uses the swaggerTSGeneratorOptions.subTypePropertyName from the generator config.

The generated file looks like this:

/**
 * This file is generated by the SwaggerTSGenerator.
 * Do not edit.
*/
/* tslint:disable */

import { Company } from './company.model';
...

export class SubTypeFactory {
    /**
     * create subType based on the value if the typeSelector property
     */
    static createSubTypeInstance(value: any, useFormGroupValuesToModel = false): object {
        switch (value.typeSelector) {
            case 'Company':
                return new Company(value, useFormGroupValuesToModel);
            ...
            default:
                throw new Error(`${value.typeSelector} not supported here`);
        }
    }
}

*.model.ts

For each definition in the Swagger an Interface and a Class are generated. The class contains the $FormGroup property to be used in the Angular FormBuilder to make a model driven form. The controls in the FormGroup contain the validators which implement the validation rules from the Swagger defnition.

Properties of an enum type are generated referencing this type which are generated in the next section.

This is an example of a generated TypeScript file with one model (definition) from the Swagger file:

/**
 * This file is generated by the SwaggerTSGenerator.
 * Do not edit.
 */
/* tslint:disable */
import {
  Validators,
  FormControl,
  FormGroup,
  FormArray,
  ValidatorFn
} from "@angular/forms";
import {
  minValueValidator,
  maxValueValidator,
  enumValidator
} from "./validators";
import { BaseModel } from "./base-model";
import { SubTypeFactory } from "./sub-type-factory";

import { type } from "./enums";
import { gender } from "./enums";
import { Address } from "./address.model";
import { Veterinarian } from "./veterinarian.model";
import { Tag } from "./tag.model";
import { NullableOrEmpty } from "./nullable-or-empty.model";

export interface IPet {
  name: string;
  age?: number;
  dob?: Date;
  type: type;
  gender?: gender;
  address?: Address;
  vet?: Veterinarian;
  tags?: Array<Tag>;
  isFavorate?: boolean;
  testDate?: NullableOrEmpty<Date>;
  primitiveArray?: Array<string>;
}

export class Pet extends BaseModel implements IPet {
  name: string;
  age: number;
  dob: Date;
  type: type;
  gender: gender;
  address: Address;
  vet: Veterinarian;
  tags: Array<Tag>;
  isFavorate: boolean;
  testDate: NullableOrEmpty<Date>;
  primitiveArray: Array<string>;

  /**
   * constructor
   * @param values Can be used to set a webapi response or formValues to this newly constructed model
   * @useFormGroupValuesToModel if true use formValues
   */
  constructor(values?: any, useFormGroupValuesToModel = false) {
    super();
    this.address = new Address();
    this.vet = new Veterinarian();
    this.tags = new Array<Tag>();
    this.testDate = new NullableOrEmpty<Date>();
    this.primitiveArray = new Array<string>();
    if (values) {
      this.setValues(values, useFormGroupValuesToModel);
    }
  }

  /**
   * set the values.
   * @param values Can be used to set a webapi response to this newly constructed model
   */
  setValues(values: any, useFormGroupValuesToModel = false): void {
    if (values) {
      const rawValues = this.getValuesToUse(values, useFormGroupValuesToModel);
      this.name = rawValues.name;
      this.age = rawValues.age;
      this.dob = rawValues.dob;
      this.type = rawValues.type;
      this.gender = rawValues.gender;
      this.address.setValues(rawValues.address, useFormGroupValuesToModel);
      this.vet.setValues(rawValues.vet, useFormGroupValuesToModel);
      this.fillModelArray<Tag>(
        this,
        "tags",
        rawValues.tags,
        Tag,
        useFormGroupValuesToModel,
        SubTypeFactory.createSubTypeInstance,
        Tag
      );
      this.isFavorate = rawValues.isFavorate;
      this.testDate.setValues(rawValues.testDate, useFormGroupValuesToModel);
      this.fillModelArray<string>(
        this,
        "primitiveArray",
        rawValues.primitiveArray,
        useFormGroupValuesToModel,
        SubTypeFactory.createSubTypeInstance,
        string
      );
      // set values in model properties for added formControls
      super.setValuesInAddedPropertiesOfAttachedFormControls(
        values,
        useFormGroupValuesToModel
      );
    }
  }

  protected getFormGroup(): FormGroup {
    if (!this._formGroup) {
      this._formGroup = new FormGroup({
        name: new FormControl(this.name, [
          Validators.required,
          Validators.minLength(4),
          Validators.pattern("^[a-zA-Z0-9- ]+$")
        ]),
        age: new FormControl(this.age),
        dob: new FormControl(this.dob),
        type: new FormControl(this.type, [Validators.required]),
        gender: new FormControl(this.gender),
        address: this.address.$formGroup,
        vet: this.vet.$formGroup,
        tags: new FormArray([]),
        isFavorate: new FormControl(this.isFavorate),
        testDate: this.testDate.$formGroup,
        primitiveArray: new FormArray([])
      });

      // generate FormArray control elements
      this.fillFormArray<Tag>("tags", this.tags, Tag);
      this.fillFormArray<string>("primitiveArray", this.primitiveArray);
    }
    return this._formGroup;
  }

  /**
   * set the FormGroup values to the model values.
   */
  setFormGroupValues() {
    this.$formGroup.controls["name"].setValue(this.name);
    this.$formGroup.controls["age"].setValue(this.age);
    this.$formGroup.controls["dob"].setValue(this.dob);
    this.$formGroup.controls["type"].setValue(this.type);
    this.$formGroup.controls["type"].setValue(this.type);
    this.$formGroup.controls["gender"].setValue(this.gender);
    this.address.setFormGroupValues();
    this.vet.setFormGroupValues();
    this.fillFormArray<Tag>("tags", this.tags, Tag);
    this.$formGroup.controls["isFavorate"].setValue(this.isFavorate);
    this.testDate.setFormGroupValues();
    this.fillFormArray<string>("primitiveArray", this.primitiveArray, string);
    this.$formGroup.controls["minimumPremiumPercentage"].setValue(
      this.minimumPremiumPercentage
    );
    // set formValues in added formControls
    super.setFormGroupValuesInAddedFormControls();
  }
}

Custom models

For custom models you can use the following data in your HBS template

interface TemplateData {
  generateClasses: boolean;
  hasComplexType: boolean;
  validatorFileName: string;
  baseModelFileName: string;
  moduleName: string;
  enumModuleName: string;
  enumRef: string;
  subTypePropertyName: string;
  type: Type;
}

// Where Type is the following interface
interface Type {
  fileName: string;
  typeName: string;
  namespace: string;
  fullNamespace: string;
  fullTypeName: string;
  isSubType: boolean;
  baseType: Type;
  baseImportFile: string;
  path: string;
  pathToRoot: string;
  properties: TypeProperty[];
}

interface TypeProperty {
  name: string;
  typeName: string;
  namespace: string;
  description: string;
  hasValidation: boolean;
  isComplexType: boolean;
  isImportType: boolean;
  isUniqueImportType: boolean;
  importType: string;
  importFile: string;
  isEnum: boolean;
  isUniqueImportEnumType: boolean;
  importEnumType: string;
  isArray: boolean;
  isArrayComplexType: boolean;
  arrayTypeName: string;
  validators: {
    validation: {
      required: boolean;
      minimum: number;
      maximum: number;
      enum: string;
      minLength: number;
      maxLength: number;
      pattern: string;
    };
    validatorArray: string[];
  };
  enum: string[];
}

enums.ts

This is een excerpt from a generated TypeScript file with enums.

// THIS IS GENERATED CODE. DO NOT CHANGE MANUALLY!
/* tslint:disable */

// generate enum based on strings instead of numbers
// (see https://blog.rsuter.com/how-to-implement-an-enum-with-string-values-in-typescript/)
export enum type {
  cat = <any>"cat",
  dog = <any>"dog",
  bird = <any>"bird",
  whale = <any>"whale"
}

export enum gender {
  unknown = <any>"unknown",
  male = <any>"male",
  female = <any>"female"
}

export enum hairColor {
  red = <any>"red",
  blond = <any>"blond",
  brown = <any>"brown",
  black = <any>"black",
  white = <any>"white",
  gray = <any>"gray"
}

/**
 * bundle of all enums for databinding to options, radio-buttons etc.
 * usage in component:
 *   import { AllEnums, minValueValidator, maxValueValidator } from '../../models/webapi';
 *
 *   @Component({
 *       ...
 *   })
 *   export class xxxComponent implements OnInit {
 *       allEnums = AllEnums;
 *       ...
 *       ngOnInit() {
 *           this.allEnums = AllEnums.instance;
 *       }
 *   }
 */
export class AllEnums {
  private static _instance: AllEnums = new AllEnums();
  constructor() {
    if (AllEnums._instance) {
      throw new Error(
        "Error: Instantiation failed: Use AllEnums.instance instead of new"
      );
    }
    AllEnums._instance = this;
  }
  static get instance(): AllEnums {
    return AllEnums._instance;
  }

  type = type;
  gender = gender;
  hairColor = hairColor;
}

Normally enums are numbers based in TypeScript. In out Webapi's whe use stringbased Enums. The thick with cat = <any>"cat" is used to make the TypeScript enums string based.

enum-i18n.component.html

When the I18N features of the angular-cli are used in your application, a view can be gerenarted containing a translatable entry for each enum value. This is triggered by the presence of the enumLanguageFiles property in the options in the gulp.config file.

<!--
 * This file is generated by the SwaggerTSGenerator.
 * Do not edit.
-->

<!-- ngx-translate-data for enums -->
<div class="hidden">
    <!-- Role -->
    <span i18n="type.cat|ngx-translate">Cat</span>
    <span i18n="type.dog|ngx-translate">Dog</span>
    <span i18n="type.brid|ngx-translate">Bird</span>
    <span i18n="type.whale|ngx-translate">Whale</span>
    ...
</div>

Using the xliffmerge tool, this file can be used to generate the enum language .json files for ngx-translate.

Make sure this view is part of lazy loaded Angular component (it does not have to be loaded, it will only be used by the angular-cli i18n extraction tool).

enums language files

As an alternative for the enum-i18n.component.html file from the section above, enum language files can be generated. Translation must be done by hand. Each new enum value is added to the given enumLanguageFile(s). Enum values already present are left intact.

{
  "type": "-------ENUM-TYPE-------",
  "cat": "kat",
  "dog": "hond",
  "bird": "vogel",
  "whale": "whale",
  "gender": "-------ENUM-TYPE-------",
  "unknown": "onbekend",
  "male": "man",
  "female": "vrouw",
  "hairColor": "-------ENUM-TYPE-------",
  "red": "rood",
  "blond": "blond",
  "brown": "bruin",
  "black": "zwart",
  "white": "wit",
  "gray": "grijs"
}

index.ts

This barrel file contains references to all generated files.