JavaScript 中基于 swagger-decorator 的自动实体类构建与 Swagger 接口文档天生

JavaScript 中基于 swagger-decorator 的自动实体类构建与 Swagger 接口文档天生是笔者关于开源项目 swagger-decorator 的形貌,关于不恶感运用注解的项目中运用 swagger-decorator 增加适宜的实体类或许接口类注解,从而完成支撑嵌套地实体类校验与天生、Sequelize 等 ORM 模子天生、基于 Swagger 的接口文档天生等等功用。如果有对 JavaScript 语法运用尚存不明的可以参考 JavaScript 进修与实践材料索引或许当代 JavaScript 开辟:语法基本与实践技能系列文章。

swagger-decorator: 一处注解,多处运用

swagger-decorator 的初志是为了简化 JavaScript 运用开辟,笔者在编写 JavaScript 运用(Web 前端 & Node.js)时发明我们常常须要反复地建立实体类、增加解释或许举行范例校验,swagger-decorator 愿望可以让开辟者一处注解、多处运用。须要强调的是,在笔者多年的 Java 运用开辟中也感受到,过量过分的注解反而会大大减弱代码的可读性,因而笔者也发起应该在适宜的时刻舒心肠运用
swagger-decorator,而不是舍本逐末,一味地寻求注解覆蓋率。swagger-decorator 已可以用于实体类天生与校验、Sequelize ORM 实体类天生、面向 Koa 的路由注解与 Swagger 文档自动天生。我们可以运用 yarn 或许 npm 装置 swagger-decorator 依靠,须要注重的是,由于我们在开辟中还会用到注解语法,因而还须要增加 babel-plugin-transform-decorators-legacy 插件以举行语法兼容转化。


# 运用 npm 装置依靠

$ npm install swagger-decorator -S

$

# 运用 yarn 装置依靠

$ yarn add swagger-decorator

$ yarn add babel-plugin-transform-decorators-legacy -D

# 导入须要的东西函数
import { 
    wrappingKoaRouter,
    entityProperty,
    ...
} from "swagger-decorator";

实体类注解

swagger-decorator 的中心 API 等于关于实体类的注解,该注解不会转变实体类的任何属性表现,只是会将注解限制的属性特征纪录在内置的 innerEntityObject 单例中以供后用。属性注解 entityProperty 的要领申明以下:

/**
 * Description 建立某个属性的形貌
 * @param type 基本范例 self - 示意为本身
 * @param description 形貌
 * @param required 是不是为必要参数
 * @param defaultValue 默许值
 * @param pattern
 * @param primaryKey 是不是为主键
 * @returns {Function}
 */
export function entityProperty({
  // 天生接口文档须要的参数
  type = "string",
  description = "",
  required = false,
  defaultValue = undefined,

  // 举行校验所须要的参数
  pattern = undefined,

  // 举行数据库衔接须要的参数
  primaryKey = false
}) {}

简朴的用户实体类注解以下,这里的数据范例 type 支撑 Swagger 默许的字符花样的范例形貌,也支撑直接运用 JavaScript 类名或许 JavaScript 数组。

// @flow

import { entityProperty } from "../../src/entity/decorator";
import UserProperty from "./UserProperty";
/**
 * Description 用户实体类
 */
export default class User {
  // 编号
  @entityProperty({
    type: "integer",
    description: "user id, auto-generated",
    required: true
  })
  id: string = 0;

  // 姓名
  @entityProperty({
    type: "string",
    description: "user name, 3~12 characters",
    required: false
  })
  name: string = "name";

  // 邮箱
  @entityProperty({
    type: "string",
    description: "user email",
    pattern: "email",
    required: false
  })
  email: string = "email";

  // 属性
  @entityProperty({
    type: UserProperty,
    description: "user property",
    required: false
  })
  property: UserProperty = new UserProperty();
}

export default class UserProperty {
  // 朋侪列表
  @entityProperty({
    type: ["number"],
    description: "user friends, which is user ids",
    required: false
  })
  friends: [number];
}

Swagger 内置数据范例定义:

Common NametypeformatComments
integerintegerint32signed 32 bits
longintegerint64signed 64 bits
floatnumberfloat
doublenumberdouble
stringstring
bytestringbytebase64 encoded characters
binarystringbinaryany sequence of octets
booleanboolean
datestringdateAs defined by full-dateRFC3339
dateTimestringdate-timeAs defined by date-timeRFC3339
passwordstringpasswordUsed to hint UIs the input needs to be obscured.

实例天生与校验

实体类定义终了以后,我们起首可以运用 instantiate 函数为实体类天生实例;差别于直接运用 new 关键字建立,instantiate 可以依据指定属性的数据范例或许花样举行校验,同时可以迭代天生嵌套地子对象。

/**
 * Description 从实体类中天生对象,而且举行数据校验;注重,这里会举行递归天生,即对实体类对象一样举行天生
 * @param EntityClass 实体类
 * @param data 数据对象
 * @param ignore 是不是疏忽校验
 * @param strict 是不是疏忽非预定义类属性
 * @throws 当校验失利,会抛出非常
 */
export function instantiate(
  EntityClass: Function,
  data: {
    [string]: any
  },
  { ignore = false, strict = true }: { ignore: boolean, strict: boolean } = {}
): Object {}

这里为了轻易形貌运用 Jest 测试用例申明差别的运用场景:

describe("测试实体类实例化函数", () => {
  test("测试 User 类实例化校验", () => {
    expect(() => {
      instantiate(User, {
        name: "name"
      }).toThrowError(/validate fail!/);
    });

    let user = instantiate(User, {
      id: 0,
      name: "name",
      email: "a@q.com"
    });

    // 推断是不是为 User 实例
    expect(user).toBeInstanceOf(User);
  });

  test("测试 ignore 参数可以许可疏忽校验", () => {
    instantiate(
      User,
      {
        name: "name"
      },
      {
        ignore: true
      }
    );
  });

  test("测试 strict 参数可以掌握是不是疏忽分外参数", () => {
    let user = instantiate(
      User,
      {
        name: "name",
        external: "external"
      },
      {
        ignore: true,
        strict: true
      }
    );

    expect(user).not.toHaveProperty("external", "external");

    user = instantiate(
      User,
      {
        name: "name",
        external: "external"
      },
      {
        ignore: true,
        strict: false
      }
    );

    expect(user).toHaveProperty("external", "external");
  });
});

describe("测试嵌套实例天生", () => {
  test("测试可以递归天生嵌套实体类", () => {
    let user = instantiate(User, {
      id: 0,
      property: {
        friends: [0]
      }
    });

    expect(user.property).toBeInstanceOf(UserProperty);
  });
});

Sequelize 模子天生

Sequelize 是 Node.js 运用中经常使用的 ORM 框架,swagger-decorator 供应了 generateSequelizeModel 函数以轻易从实体类中运用现有的信息天生 Sequelize 对象模子;generateSequelizeModel 的第一个参数输入实体类,第二个参数输入须要覆写的模子属性,第三个参数设置分外属性,比如是不是须要将驼峰定名转化为下划线定名等等。

const originUserSequelizeModel = generateSequelizeModel(
  User,
  {
    _id: {
      primaryKey: true
    }
  },
  {
    mappingCamelCaseToUnderScore: true
  }
);

const UserSequelizeModel = sequelize.define(
  "b_user",
  originUserSequelizeModel,
  {
    timestamps: false,
    underscored: true,
    freezeTableName: true
  }
);

UserSequelizeModel.findAll({
  attributes: { exclude: [] }
}).then(users => {
  console.log(users);
});

从 Flow 范例声明中自动天生注解

笔者习气运用 Flow 作为 JavaScript 的静态范例检测东西,因而笔者增加了 flowToDecorator 函数以自动地从 Flow 声明的类文件中提掏出范例信息;内部道理参考当代 JavaScript 开辟:语法基本与实践技能 一书中的 JavaScript 语法树与代码转化章节。该函数的运用体式格局为:

// @flow

import { flowToDecorator } from '../../../../src/transform/entity/flow/flow';

test('测试从 Flow 中提掏出数据范例而且转化为 Swagger 接口类', () => {
  flowToDecorator('./TestEntity.js', './TestEntity.transformed.js').then(
    codeStr => {
      console.log(codeStr);
    },
    err => {
      console.error(err);
    }
  );
});

这里对应的 TestEntity 为:

// @flow

import AnotherEntity from "./AnotherEntity";

class Entity {
  // Comment
  stringProperty: string = 0;

  classProperty: Entity = null;

  rawProperty;

  @entityProperty({
    type: "string",
    description: "this is property description",
    required: true
  })
  decoratedProperty;
}

转化后的实体类为:

// @flow

import { entityProperty } from 'swagger-decorator';

import AnotherEntity from './AnotherEntity';

class Entity {
  // Comment
  @entityProperty({
    type: 'string',
    required: false,
    description: 'Comment'
  })
  stringProperty: string = 0;

  @entityProperty({
    type: Entity,
    required: false
  })
  classProperty: Entity = null;

  @entityProperty({
    type: 'string',
    required: false
  })
  rawProperty;

  @entityProperty({
    type: 'string',
    description: 'this is property description',
    required: true
  })
  decoratedProperty;
}

接口注解与 Swagger 文档天生

关于 Swagger 文档范例可以参考 OpenAPI Specification ,而关于 swagger-decorator 的实际运用可以参考本项目标运用示例或许 基于 Koa2 的 Node.js 运用模板

封装路由对象

import { wrappingKoaRouter } from "swagger-decorator";

...

const Router = require("koa-router");

const router = new Router();

wrappingKoaRouter(router, "localhost:8080", "/api", {
  title: "Node Server Boilerplate",
  version: "0.0.1",
  description: "Koa2, koa-router,Webpack"
});

// define default route
router.get("/", async function(ctx, next) {
  ctx.body = { msg: "Node Server Boilerplate" };
});

// use scan to auto add method in class
router.scan(UserController);

定义接口类

export default class UserController extends UserControllerDoc {
  @apiRequestMapping("get", "/users")
  @apiDescription("get all users list")
  static async getUsers(ctx, next): [User] {
    ctx.body = [new User()];
  }

  @apiRequestMapping("get", "/user/:id")
  @apiDescription("get user object by id, only access self or friends")
  static async getUserByID(ctx, next): User {
    ctx.body = new User();
  }

  @apiRequestMapping("post", "/user")
  @apiDescription("create new user")
  static async postUser(): number {
    ctx.body = {
      statusCode: 200
    };
  }
}

在 UserController 中是担任详细的营业完成,为了防止过量的注解文档关于代码可读性的滋扰,笔者发起是将除了途径与形貌以外的信息安排到父类中声明;swagger-decorator 会自动从某个接口类的直接父类中提掏出同名要领的形貌文档。


export default class UserControllerDoc {
  @apiResponse(200, "get users successfully", [User])
  static async getUsers(ctx, next): [User] {}

  @pathParameter({
    name: "id",
    description: "user id",
    type: "integer",
    defaultValue: 1
  })
  @queryParameter({
    name: "tags",
    description: "user tags, for filtering users",
    required: false,
    type: "array",
    items: ["string"]
  })
  @apiResponse(200, "get user successfully", User)
  static async getUserByID(ctx, next): User {}

  @bodyParameter({
    name: "user",
    description: "the new user object, must include user name",
    required: true,
    schema: User
  })
  @apiResponse(200, "create new user successfully", {
    statusCode: 200
  })
  static async postUser(): number {}
}

运转运用

  • run your application and open swagger docs (PS. swagger-decorator contains Swagger UI):

/swagger

《JavaScript 中基于 swagger-decorator 的自动实体类构建与 Swagger 接口文档天生》

/swagger/api.json

《JavaScript 中基于 swagger-decorator 的自动实体类构建与 Swagger 接口文档天生》

    原文作者:王下邀月熊_Chevalier
    原文地址: https://segmentfault.com/a/1190000010226949
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞