@lcap/asl
Version:
NetEase Application Specific Language
436 lines (411 loc) • 12.6 kB
text/typescript
import { immutable, circular, excludedInJSON, action } from '../decorators';
import {
config, utils, LEVEL_ENUM, Vertex, MicroService, Logic,
Service, ActionOptions, isBasicType,
} from '..';
import { interfaceService } from '../../service/data';
import { findUsageService } from '../../service/common';
/**
* 接口类
*/
export class Interface extends Vertex {
/**
* 概念类型
*/
public readonly level: LEVEL_ENUM = LEVEL_ENUM.interface;
/**
* Id
*/
public readonly id: string = undefined;
/**
* InterfaceKey
*/
public readonly key: string = undefined;
/**
* 名称
*/
public readonly name: string = undefined;
/**
* 协议
*/
public readonly protocol: string = undefined;
/**
* 地址
*/
public readonly host: string = undefined;
/**
* 端口
*/
public readonly port: string = undefined;
/**
* 路径
*/
public readonly path: string = undefined;
/**
* 方法
*/
public readonly method: string = undefined;
/**
* 描述
*/
public readonly description: string = undefined;
/**
* 请求数据
*/
public exportedInterface: Interface = undefined;
/**
* 逻辑 Id
*/
public readonly logicId: string = undefined;
/**
* 逻辑
* @TODO 使用 excludedInJSON 有点问题,不同 class 相同字段会串
*/
public logic: Logic = undefined;
/**
* Service 类型
*/
public readonly serviceType:
'micro' | 'entity' | 'process' | 'processComponent' | 'external' | 'export' = undefined;
/**
* Service Id
*/
public readonly serviceId: string = undefined;
/**
* Service Name
*/
public readonly category: string = undefined;
/**
* 所属实体 Id
*/
public entityId: string = undefined;
/**
* Service 名称
*/
public readonly serviceName: string = undefined;
/**
* Service
*/
public readonly service: MicroService = undefined;
/**
* 参数
*/
public parameters: string = undefined;
/**
* 返回值
*/
public responses: string = undefined;
/**
* 请求数据
*/
public requestBody: string = undefined;
/**
* 树组件的子节点字段
*/
public readonly moreChildrenFields: Array<string> = ['logic.params', 'logic.returns', 'logic.variables'];
/**
* 周边存在的名称
*/
public existingNames: Array<string> = [];
/**
* @param source 需要合并的部分参数
*/
constructor(source?: Partial<Interface>) {
super();
source && this.assign(source);
}
/**
* 按当前 id 加载接口数据
* @requires this.id
*/
async load() {
const result = await interfaceService.loadDetail({
path: {
id: this.id,
},
});
this.assign(result);
return this;
}
/**
* 添加接口
*/
async create(none?: void, actionOptions?: ActionOptions, then?: () => Promise<any>) {
config.defaultApp?.emit('saving');
if (!this.logicId) {
this.logic.assign({ name: this.name });
await this.logic.create();
this.assign({
path: this.path || '/api/' + this.name,
logicId: this.logic.id,
});
}
const body = this.toPlainJSON();
body.logicId = this.logicId || this.logic.id;
utils.logger.debug('添加接口', body);
const result: Interface = await interfaceService.create({
headers: {
appId: config.defaultApp?.id,
operationAction: actionOptions?.actionName || 'Interface.create',
operationDesc: actionOptions?.actionDesc || `添加接口"${this.name}"`,
},
body,
});
this.pick(result, ['id']);
this.assign({
key: `#/${this.service.id}/${this.id}`,
});
await then?.();
this.service.emit('interfacesChange');
this.service.emit('vertexIdToNameChange', this.id, this.name);
await config.defaultApp?.history.load();
config.defaultApp?.emit('saved');
return this;
}
/**
* 删除接口
*/
async delete(none?: void, actionOptions?: ActionOptions) {
config.defaultApp?.emit('saving');
if (this.logic)
await this.logic.delete();
if (this.id) {
await interfaceService.delete({
headers: {
appId: config.defaultApp?.id,
operationAction: actionOptions?.actionName || 'Interface.delete',
operationDesc: actionOptions?.actionDesc || `删除接口"${this.name}"`,
},
path: {
id: this.id,
},
});
const index = this.service.interfaces.indexOf(this);
~index && this.service.interfaces.splice(index, 1);
this.service.globalLogic.removeInterface(this);
this.destroy();
this.service.syncStructures(); // 处理 CallQueryComponent 嵌套的情形
this.service.emit('interfacesChange');
await config.defaultApp?.history.load();
config.defaultApp?.emit('saved');
} else {
const index = this.service.interfaces.indexOf(this);
~index && this.service.interfaces.splice(index, 1);
this.service.globalLogic.removeInterface(this);
}
}
/**
* 更新接口
*/
async update(none?: void, actionOptions?: ActionOptions, then?: () => Promise<any>) {
config.defaultApp?.emit('saving');
const body = this.toPlainJSON();
await interfaceService.update({
headers: {
appId: config.defaultApp?.id,
operationAction: actionOptions?.actionName || 'Interface.update',
operationDesc: actionOptions?.actionDesc || `修改接口"${this.name}"`,
},
body,
});
await then?.();
await config.defaultApp?.history.load();
config.defaultApp?.emit('saved');
return this;
}
/**
* 设置接口名称
*/
async setName(name: string) {
const originalData = {
name: this.name,
path: this.path,
};
this.assign({ name, path: '/api/' + name });
try {
await this.update(undefined, {
actionDesc: '设置接口名称',
});
this.service.emit('interfacesChange');
this.service.emit('vertexIdToNameChange', this.id, this.name);
} catch (err) {
this.assign(originalData);
throw err;
}
}
/**
* 设置接口描述
*/
async setDescription(description: string) {
this.assign({ description });
await this.update(undefined, {
actionDesc: '设置接口描述',
});
this.service.emit('interfacesChange');
}
/**
* 设置接口路径
*/
async setPath(path: string) {
this.assign({ path });
await this.update(undefined, {
actionDesc: '设置接口路径',
});
this.service.emit('interfacesChange');
}
/**
* 设置接口方法
*/
async setMethod(method: string) {
this.assign({ method });
await this.update(undefined, {
actionDesc: '设置接口方法',
});
this.service.emit('interfacesChange');
}
/**
* 设置接口参数和返回值类型
*/
async setParamAndReturn() {
if (this.serviceType === 'micro' && this.exportedInterface) {
const logic = this.logic;
const exportedInterface = this.exportedInterface;
let bodyParameters: Array<utils.ParamStruts> = [];
if (exportedInterface.requestBody) {
const body = JSON.parse(exportedInterface.requestBody);
const schema = body?.content['application/json']?.schema;
const name = body?.description?.name || 'content';
const required = body?.required;
bodyParameters = [{
name,
schema,
in: 'body',
required,
defaultValue: '',
}];
}
const params: Array<utils.ParamStruts> = Object.values(JSON.parse(exportedInterface.parameters || '{}')).map((p: utils.ParamStruts) => ({
name: p.name,
schema: p.schema,
in: p.in,
required: true,
defaultValue: '',
}));
const parameters = params.concat(bodyParameters).filter((p) => !!p);
const finnalParameter: Array<utils.ParamStruts> = [];
if (logic) {
logic.params.forEach((p) => {
const finded = parameters.find((param) => param.name === p.name);
const isBasic = isBasicType(p.schema);
if (finded) {
finnalParameter.push({
name: p.name,
schema: p.schema,
in: isBasic ? 'query' : 'body',
required: finded.required,
defaultValue: finded.defaultValue,
});
} else {
finnalParameter.push({
name: p.name,
schema: p.schema,
in: isBasic ? 'query' : 'body',
required: !isBasic,
defaultValue: '',
});
}
});
}
let finnalResponses = '';
if (logic.returns.length > 0) {
const p = logic.returns[0];
const payload = {
description: {
name: p.name,
},
content: {
'application/json': {
schema: p.schema,
},
},
};
finnalResponses = JSON.stringify({
200: payload,
});
}
const {
parameters: finnalP,
requestBody: finnalB,
}: utils.resolvedParameter = utils.resolveParameter(finnalParameter);
exportedInterface.parameters = finnalP;
exportedInterface.requestBody = finnalB;
exportedInterface.responses = finnalResponses;
await exportedInterface.update();
}
this.service.emit('interfacesChange');
}
/**
* 查找引用
*/
async findUsage() {
let id = this.id;
if (['entity', 'micro', 'process', 'processComponent'].includes(this.serviceType)) {
id = this.logicId;
}
const result = await findUsageService.findUsage({
query: {
id,
},
});
return result;
}
/**
* 从后端 JSON 生成规范的 Interface 对象
*/
public static from(source: any, service: Service) {
const item = new Interface(source);
item.assign({
service,
expanded: false,
key: `#/${service.id}/${item.id}`,
});
item.logic && item.assign({ logic: Logic.from(item.logic, item) });
return item;
}
}
export default Interface;