swagger-decorator

0.0.22 • Public • Published

中文版本 | English Version

基于 swagger-decorator 的自动实体类构建与 Swagger 接口文档生成,对于不反感使用注解的项目中利用 swagger-decorator 添加合适的实体类或者接口类注解,从而实现支持嵌套地实体类校验与生成、Sequelize 等 ORM 模型生成、基于 Swagger 的接口文档生成等等功能。

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";

实体类注解

/**
 * 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
}) {}
// @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];
}
 

数据类型支持:

Common Name type format Comments
integer integer int32 signed 32 bits
long integer int64 signed 64 bits
float number float
double number double
string string
byte string byte base64 encoded characters
binary string binary any sequence of octets
boolean boolean
date string date As defined by full-date - RFC3339
dateTime string date-time As defined by date-time - RFC3339
password string password Used to hint UIs the input needs to be obscured.

实例生成与校验

/**
 * 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 {}
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 模型生成

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 类型声明中自动生成注解

接口注解与 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
    };
  }
}
 
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

/swagger/api.json

About

RoadMap

  • 修复实体类自动生成中可能存在的错误
  • 复合类型推导
  • 接口数据自动校验

Package Sidebar

Install

npm i swagger-decorator

Weekly Downloads

25

Version

0.0.22

License

ISC

Last publish

Collaborators

  • wxyyxc1992