核心文件
wrapper.ts
- 用于依據(jù) decorator 函數(shù)執(zhí)行后得到的數(shù)據(jù)進(jìn)行 router 的自動(dòng)化構(gòu)建零酪,同時(shí)根據(jù) middlewares 函數(shù)的結(jié)果在 router 構(gòu)建時(shí)添加相對(duì)應(yīng)的中間件。
- 基于 swaggerObject 構(gòu)建 swagger 文檔
swaggerObject.ts
swagger 文檔可以使用 json 描述窘茁, swaggerObject 暴露出一個(gè) SwaggerObject 的實(shí)例,提供添加對(duì)應(yīng)的 swagger 字段的方法供 decorator 使用,并且暴露出 data 字段表示整個(gè) swagger 對(duì)象,用于構(gòu)建 swagger json 文檔
decorators.ts
定義使用的 decorator稠炬,如 @request,@summary 等咪啡。由于 swagger 文檔的構(gòu)建中首启,對(duì)個(gè)字段會(huì)有相似的結(jié)構(gòu),如 summary 與 description 的格式相同撤摸,所以為了使得 decorator 函數(shù)更加簡(jiǎn)潔毅桃,使用curry化的方式優(yōu)化代碼結(jié)構(gòu)。如下
const _desc = (type: string, text: string | any[]) => (target: any, name: string, descriptor: PropertyDescriptor) => {
descriptor.value[type] = text;
swaggerObject.add(target, name, { [type]: text });
return descriptor;
};
const desc = _.curry(_desc);
const description = desc('description');
const summary = desc('summary');
const tags = desc('tags');
這些 decorator 會(huì)需要處理兩件事准夷,一是對(duì)于 swaggerObject 要添加相關(guān)字段內(nèi)容钥飞,二是對(duì)于 router 構(gòu)建要添加相關(guān)字段內(nèi)容,如 @request 需要添加 path衫嵌,method 兩個(gè)字段到綁定的方法中读宙,使得 wapper.js 中可以根據(jù)這些信息構(gòu)建 router
validate/index.ts
validate/check.ts
這兩個(gè)文件用于對(duì) decorator 定義的參數(shù)進(jìn)行校驗(yàn)。關(guān)鍵難點(diǎn)在于對(duì)于 swagger 中 array 和 object 嵌套結(jié)構(gòu)的校驗(yàn)楔绞,利用遞歸的方式實(shí)現(xiàn)结闸。參考代碼如下,在 cObject 中調(diào)用 check 校驗(yàn)嵌套數(shù)據(jù)酒朵。
const check = (input: any, expect: Expect) => {
const cond = _.cond([
[_.equals('string'), () => cString(input, expect)],
[_.equals('boolean'), () => cBool(input, expect)],
[_.equals('number'), () => cNum(input, expect)],
[_.equals('object'), () => cObject(input, expect)],
[_.equals('array'), () => cArray(input, expect)],
[_.T, () => ({ is: true, val: input })] // 其他類(lèi)型不做校驗(yàn)桦锄,直接返回原數(shù)據(jù)
]);
return cond(expect.type);
};
// /**
// * 對(duì) Object 做檢驗(yàn), 支持嵌套數(shù)據(jù)
// {
// aaaa: 'hh',
// bbbb: 'qq',
// }
// { // expect:
// type: 'object',
// properties: {
// aaaa: { type: 'string', example: 'http://www.baidu.com', required: true },
// bbbb: { type: 'string', example: 'Bob' }
// c: { type: 'object', properties: {ii: {type: 'string'}, jj: {type: 'number'}} }
// }
// }
// */
const cObject = (input: any, expect: Expect = {}) => {
if (!cRequired(input, expect).is) return { is: false };
const res = { is: true, val: input };
if (!is.object(input)) return { is: false };
if (!expect.properties) return res;
for (const key of Object.keys(expect.properties)) {
// ignore empty key if not required
if (!expect.properties[key].required && input[key] === undefined) {
continue; // eslint-disable-line
}
const { is, val } = check(input[key], expect.properties[key]);
if (!is) {
console.log('error object properties:', key); // TODO need to update error debug info
res.is = false;
break;
}
input[key] = is ? val : input[key];
}
return res;
};