egg-cute-swagger
Version:
swagger for egg
173 lines (140 loc) • 7.17 kB
JavaScript
;
class AbstractField {
constructor (config) {
this.config = config;
}
buildDefinitionRuleSingle(object, source, rules) {
const def = source[object].content;
const path = source[object].path;
rules = rules ?? {};
const properties = Object.assign({}, ...Object.keys(def).map(m => this.buildFieldRule('', Object.assign(def[m], { name: m }), `${path}: ${object}.${m}`, source, rules)));
rules[object] = properties;
return properties;
}
checkTypeRule(/* position, field, path, source, baseTypes */) {
throw new Error('checkRuleType must be implemented.');
}
/**
* 生成字段的Validate Rule格式 (包括Controller参数与Contract类)
* @param {String} position 请求参数位置, body/path/query/headers
* @param {Object} field 请求参数 { name: '', type: '', description: '', required: true }
* @param {Object} path field路径
* @param {Object} source contract对象名称数组
* @return {Object} { name: { type: '', required: true, rule: '', max: '', } }
* @return {Object} { in: '', name: '', type: '', rule: '', required: true } or { name: { type: '', rule: '', required: true } }
*/
buildFieldRule(position, field, path, source, rules) {
const baseRules = ['name', 'type', 'required', 'convertType', 'default', 'max', 'min', 'allowEmpty', 'format', 'trim', 'compare', 'itemType'];
let itemObj = Object.assign({}, ...Object.keys(field).filter(f => baseRules.includes(f)).map(f => { return { [f]: field[f] } }));
itemObj.required = typeof itemObj.required === 'boolean' && itemObj.required ? true: false;
itemObj.required = position === 'path' ? true : itemObj.required;
field.type = field.type ?? '';
const baseTypes = ['boolean', 'integer', 'number', 'string', 'enum', 'id', 'email', 'password', 'url'];
let itemType= field.type.startsWith('array') ? field.type.substring(6, field.type.length - 1) : '';
itemType = !position ? (field.itemType || '') : itemType;
this.checkTypeRule(position, field, path, source, baseTypes);
if (field.hasOwnProperty('enum') && Array.isArray(field.enum)) { // values
field.type = 'enum'
itemObj.values = field.enum;
}
if (field.hasOwnProperty('format') && (field.format === 'date-time'|| field.format === 'date')) {
delete itemObj.format;
field.type = field.format.replace('-', '');
}
itemObj.type = field.type;
// object
if (![...baseTypes, ...['datetime', 'date']].includes(field.type) && !field.type.startsWith('array')) {
const definitions = rules.hasOwnProperty(field.type) ? rules[field.type] : this.buildDefinitionRuleSingle(field.type, source, rules);
itemObj['rule'] = definitions;
itemObj.type = 'object';
}
// array string
if (field.type.startsWith('array') && baseTypes.includes(itemType)) {
itemObj.type = 'array';
itemObj.itemType = itemType;
itemObj.rule = { type: itemType };
}
// array User
if (field.type.startsWith('array') && !baseTypes.includes(itemType)) {
itemObj.type = 'array';
itemObj.itemType = 'object';
itemObj.rule = rules.hasOwnProperty(itemType) ? rules[itemType] : this.buildDefinitionRuleSingle(itemType, source, rules);
}
if(position) {
itemObj.in = position;
}
if(!position) {
const name = itemObj.name;
delete itemObj.name;
itemObj = { [name]: itemObj };
}
return itemObj;
}
checkTypeSwagger(/* position, field, path, source, baseTypes */) {
throw new Error('checkSwaggerType must be implemented.');
}
/**
* 生成字段的Swagger格式 (包括Controller参数与Contract类)
* body与response需要在其他地方再次处理数据, 此方法返回的数据统一是 definitions 格式
* @param {String} position 请求参数位置, body/path/query/headers/response
* @param {Object} field 请求参数 { name: '', type: '', description: '', required: true }
* @param {Object} path field路径
* @param {Object} source contract对象名称数组
* @return {Object} { in: '', name: '', type: '', description: '', required: true } or { name: { type: '', description: '', required: true } }
*/
buildFieldSwagger(position, field, path, source) {
// definitions/response 此方法返回
// { id: { type: 'integer', description: '', example: 0, min: 1, }
// { po: { description: '', '$ref': '#/definitions/pVo', }
// { ids: { type: 'array', description: '', items: { type: 'string' }, }
// { list: { type: 'array', description: '', items: { '$ref': '#/definitions/pVo' }, }
// request query/path
// { in: 'query', name: 'xx', description: '', type: 'string' }
// { in: 'query', name: 'xx', description: '', type: 'array', items: { type: 'string' } }
// { in: 'path', name: 'xx', description: '', type: 'string' }
// { in: 'path', name: 'xx', description: '', type: 'array', items: { type: 'string' } }
// request body 最终需要
// { in: 'body', description: '', schema: { type: 'object', properties: { code: { type: 'number', example: 0 } } } }
// { in: 'body', description: '', schema: { '$ref': '#/definitions/pVo', description: '' } }
// { in: 'body', description: '', schema: { type: 'array', description: '', items: { '$ref': '#/definitions/pVo' } } }
const baseRules = ['name', 'example', 'format', 'enum', 'len', 'min', 'max', 'allowEmpty'];
let itemObj = Object.assign({}, ...Object.keys(field).filter(f => baseRules.includes(f)).map(f => ({ [f]: field[f] })));
itemObj.description = field.desc || '';
itemObj.required = typeof field.required === 'boolean' && field.required ? true: false;
itemObj.required = position === 'path' ? true : itemObj.required;
field.type = field.type ?? '';
field.type = ['enum', 'id', 'email', 'password', 'url'].includes(field.type) ? 'string' : field.type;
itemObj.type = field.type;
const baseTypes = ['boolean', 'integer', 'number', 'string'];
let itemType = field.type.startsWith('array') ? field.type.substring(6, field.type.length - 1) : '';
itemType = !position ? (field.itemType || '') : itemType;
this.checkTypeSwagger(position, field, path, source, baseTypes);
// object
if (![...baseTypes, ...['array']].includes(field.type) && !field.type.startsWith('array')) {
delete itemObj.type;
Object.assign(itemObj, { $ref: `#/definitions/${field.type}` });
}
// array
if (field.type.startsWith('array')) {
itemObj.type = 'array';
itemObj.items = { [baseTypes.includes(itemType) ? 'type' : '$ref']: baseTypes.includes(itemType) ? itemType : `#/definitions/${itemType}` };
}
if (field.type !== 'string') {
delete itemObj.format;
}
if(position) {
itemObj.in = position;
}
if (position === 'query' && field.type.startsWith('array')) {
itemObj.collectionFormat = 'multi';
}
if(!position) {
delete itemObj.required;
const name = itemObj.name;
delete itemObj.name;
itemObj = { [name]: itemObj }
}
return itemObj;
}
}
module.exports = AbstractField;