@lcap/asl
Version:
NetEase Application Specific Language
427 lines (408 loc) • 17.3 kB
text/typescript
import { Schema } from '..';
import { basicTypeMap, getBasicTypeRef } from './basicTypes';
import { dataTypesMap } from './dataTypes';
import { getGenericTypeRef } from './genericTypes';
import { cloneDeep } from 'lodash';
/**
* 会改变原来的结构,最好自己做深拷贝
* @param schema
* @param root
* @example RefType - 用于前端界面的类型描述
* {
* asset: {
* type: 'object',
* propertyList: [
* { name: 'id', $ref: '#/basicType/Long' },
* { name: 'name', $ref: '#/basicType/String' },
* ],
* },
* assetType: {
* type: 'enum',
* enumItemList: [
* { value: 'COMPUTER', label: '台式机' },
* { value: 'TABLET', label: '平板电脑' },
* { value: 'MOBILE', label: '移动电话' },
* },
* },
* assets: {
* isArray: true,
* $ref: '#/xxxx/entity/asset'
* },
* test: { $ref: '#/basicType/Date' }
* }
*/
export function convert2RefType(schema: Schema): Schema {
if (!schema)
return schema;
if (schema.type === 'object') {
if (schema.properties) {
schema.propertyList = Object.keys(schema.properties).map((name) => {
const property = schema.properties[name];
property.name = name;
return property;
});
delete schema.properties;
}
schema.propertyList = !schema.propertyList ? [] : schema.propertyList.map((property) => convert2RefType(property));
} else if (schema.type === 'enum') {
if (schema.enumItems) {
schema.enumItemList = Object.keys(schema.enumItems).map((value) => ({
level: 'enumItem',
label: schema.enumItems[value],
value,
}));
}
delete schema.enumItems;
} else if (schema.type === 'array') {
schema.isArray = true;
// 转成泛型
const listSchema = dataTypesMap['#/genericTypes/List'];
if (listSchema) {
const genericSchema = {
type: 'genericType',
typeKey: '#/genericTypes/List',
typeInstantiation: {
typeName: 'List',
typeParams: (listSchema.typeParams || []).map((param: any) => {
const tempParam = Object.assign({}, param);
tempParam.typeParamValue = convert2RefType(schema.items);
tempParam.type = 'typeParam';
return tempParam;
}),
},
};
delete schema.items;
Object.assign(schema, genericSchema);
}
} else if (schema.type === 'genericType') {
if (schema.typeInstantiation) {
schema.typeKey = getGenericTypeRef(schema.typeInstantiation.typeName);
schema.typeInstantiation.typeParams.forEach((typeParam) => {
typeParam.type = 'typeParam';
convert2RefType(typeParam.typeParamValue);
});
}
} else if (schema.type) {
schema.typeKey = getBasicTypeRef(schema.type, schema.format);
} else if (schema.$ref) {
if (schema.$ref.startsWith('#/basicTypes')) { //兼容错误缓存情况
schema.typeKey = schema.$ref;
Object.assign(schema, dataTypesMap[schema.$ref]);
delete schema.$ref;
} else {
schema.typeKey = schema.$ref;
delete schema.type;
delete schema.format;
}
}
return schema;
}
/**
* 会改变原来的结构,最好自己做深拷贝
* @param schema
* @param root
* @example SchemaType - 非标准的 JSON Schema 结构
* {
* asset: {
* type: 'object',
* propertyList: [
* { type: 'int', format: 'long' },
* { type: 'string' },
* ],
* },
* assetType: {
* type: 'enum',
* enumItemList: [
* { value: 'COMPUTER', label: '台式机' },
* { value: 'TABLET', label: '平板电脑' },
* { value: 'MOBILE', label: '移动电话' },
* },
* },
* assets: {
* isArray: true,
* items: {
* $ref: '#/xxxx/entity/asset'
* }
* },
* test: { type: 'string', format: 'date' }
* }
*/
export function convert2SchemaType(schema: Schema, options: { enum?: string } = {}) {
if (!schema)
return schema;
if (schema.type === 'object') {
schema.propertyList && schema.propertyList.forEach((property) => convert2SchemaType(property));
} else if (schema.type === 'enum') {
//
} else if (schema.isArray) { // 去除
//
} else if (schema.$ref) { // 兼容老版本传$ref
if (!schema.$ref.startsWith('#/basicTypes/')) {
// 防止切换 type 时的污染
delete schema.type;
delete schema.format;
Object.assign(schema, { typeKey: schema.$ref });
} else {
Object.assign(schema, { typeKey: schema.$ref });
Object.assign(schema, basicTypeMap[schema.$ref]);
delete schema.$ref;
}
} else if (schema.typeKey) { // 从s-datatype-select中选择类型后只传typeKey
if (schema.typeKey.startsWith('#/genericTypes')) {
if (!schema.typeInstantiation)
return schema;
schema = {
type: 'genericType',
typeKey: `#/genericTypes/${schema.typeInstantiation.typeName}`,
typeInstantiation: {
typeName: schema.typeInstantiation.typeName,
typeParams: schema.typeInstantiation.typeParams.map((typeParam: any) => {
delete typeParam.tempColor;
delete typeParam.tempText;
typeParam.typeParamValue = convert2SchemaType(typeParam.typeParamValue);
typeParam.type = 'typeParam';
return typeParam;
}),
},
};
} else if (!schema.typeKey.startsWith('#/basicTypes/')) { // { $ref: '', typeKey: ''}
// 防止切换 type 时的污染
schema = { $ref: schema.typeKey, typeKey: schema.typeKey };
} else {
schema = Object.assign({ typeKey: schema.typeKey }, basicTypeMap[schema.typeKey]);
}
}
return schema;
}
/**
* 根据 Ref Schema 生成子节点
* @param schema
* @param code
* @param dataTypesMap
*/
export function genSchemaChildren(
schema: Schema, code: string = '', usedSchemaRefs: { [name: string]: boolean } = {},
): Array<any> {
const typeKey = schema.typeKey;
if (typeKey && usedSchemaRefs[typeKey])
return;
// 理论上可以继续往下选,但这里先断掉!
const next = dataTypesMap[typeKey];
usedSchemaRefs = Object.assign({}, usedSchemaRefs);
if (typeKey && !(typeKey.startsWith('#/basicTypes/') || typeKey.startsWith('#/genericTypes/')))
usedSchemaRefs[typeKey] = true;
if (schema.type === 'genericType') {
const children: Array<any> = [];
const genericClass = dataTypesMap[schema.typeKey];
if (genericClass) {
const propertyList = genericClass.propertyList;
propertyList.forEach((property: any) => {
let propertySchema = cloneDeep(property);
if (property.type === 'genericParam' && schema.typeInstantiation) {
const param = schema.typeInstantiation.typeParams.find((typeParam: any) => typeParam.typeParamName === property.typeParamName);
if (param) {
propertySchema = Object.assign({ name: property.name }, param.typeParamValue);
}
} else if (propertySchema.type === 'genericType' && schema.typeInstantiation) {
if (propertySchema.typeInstantiation) {
propertySchema.typeInstantiation.typeParams.forEach((typeParam: any) => {
const param = schema.typeInstantiation.typeParams.find((typeParamTemp: any) => typeParamTemp.typeParamName === typeParam.typeParamName);
if (param) {
typeParam.typeParamValue = Object.assign({}, param.typeParamValue);
}
});
}
}
const item: any = {
level: 'property',
name: propertySchema.name,
schema: propertySchema,
code: property.id ? `${code}.ID_${property.id}` : `${code}.${property.name}`,
};
if (schema.typeName === '#/genericTypes/List') {
item.arrayLength = true;
}
item.children = genRefSchemaChildren(item.schema, item.code, usedSchemaRefs);
item.isLeaf = !item.children;
children.push(item);
});
}
return children;
} else if (schema.type === 'object') {
const children: Array<any> = [];
schema.propertyList && schema.propertyList.forEach((property: Schema) => {
const item: any = {
level: 'property',
name: property.name,
schema: property,
code: code + '.' + (property.id ? 'ID_' + property.id : property.name),
};
item.children = genSchemaChildren(item.schema, item.code, usedSchemaRefs);
item.isLeaf = !item.children;
children.push(item);
});
return children;
} else if (schema.isArray) {
//
} else if (typeKey && typeKey.startsWith('#/basicTypes')) {
return undefined;
} else if (next) {
return genSchemaChildren(next, code, usedSchemaRefs);
}
}
/**
* 根据 Ref Schema 生成子节点
* @param schema
* @param code
*/
export function genRefSchemaChildren(
schema: Schema, code: string,
usedSchemaRefs: { [name: string]: boolean } = {},
): Array<any> {
if (!schema)
return;
const typeKey = schema.typeKey;
if (typeKey && usedSchemaRefs[typeKey])
return;
// 理论上可以继续往下选,但这里先断掉!
const next = dataTypesMap[typeKey];
usedSchemaRefs = Object.assign({}, usedSchemaRefs);
if (typeKey && !(typeKey.startsWith('#/basicTypes/') || typeKey.startsWith('#/genericTypes/')))
usedSchemaRefs[typeKey] = true;
if (schema.type === 'genericType') {
const children: Array<any> = [];
const genericClass = dataTypesMap[schema.typeKey];
if (genericClass) {
const propertyList = genericClass.propertyList;
propertyList.forEach((property: any) => {
let propertySchema = cloneDeep(property);
if (property.type === 'genericParam' && schema.typeInstantiation) {
const param = schema.typeInstantiation.typeParams.find((typeParam: any) => typeParam.typeParamName === property.typeParamName);
if (param) {
propertySchema = Object.assign({ name: property.name }, param.typeParamValue);
}
} else if (propertySchema.type === 'genericType' && schema.typeInstantiation) {
if (propertySchema.typeInstantiation) {
propertySchema.typeInstantiation.typeParams.forEach((typeParam: any) => {
const param = schema.typeInstantiation.typeParams.find((typeParamTemp: any) => typeParamTemp.typeParamName === typeParam.typeParamName);
if (param) {
typeParam.typeParamValue = Object.assign({}, param.typeParamValue);
}
});
}
}
const item: any = {
level: 'refProperty',
schema: propertySchema,
code: property.id ? `${code}.ID_${property.id}` : `${code}.${property.name}`,
};
if (schema.typeKey === '#/genericTypes/List') {
item.arrayLength = true;
}
item.children = genRefSchemaChildren(item.schema, item.code, usedSchemaRefs);
item.isLeaf = !item.children;
children.push(item);
});
}
return children;
} else if (schema.type === 'object') {
const children: Array<any> = [];
schema.propertyList && schema.propertyList.forEach((property: Schema) => {
const item: any = {
level: 'refProperty',
schema: property,
code: property.id ? `${code}.ID_${property.id}` : `${code}.${property.name}`,
};
item.children = genRefSchemaChildren(item.schema, item.code, usedSchemaRefs);
item.isLeaf = !item.children;
children.push(item);
});
return children;
} else if (schema.isArray) {
//
} else if (typeKey && typeKey.startsWith('#/basicTypes')) {
return undefined;
} else if (next) {
return genRefSchemaChildren(next, code, usedSchemaRefs);
}
}
/**
* 根据 Ref Schema 生成子节点
* @param schema
* @param code
*/
export function genFullRefSchemaChildren(schema: Schema, code: string, usedSchemaRefs: { [name: string]: boolean } = {}): Array<any> {
if (!schema)
return;
const typeKey = schema.typeKey;
if (typeKey && usedSchemaRefs[typeKey])
return;
// 理论上可以继续往下选,但这里先断掉!
const next = dataTypesMap[typeKey];
usedSchemaRefs = Object.assign({}, usedSchemaRefs);
if (typeKey && !(typeKey.startsWith('#/basicTypes/') || typeKey.startsWith('#/genericTypes/')))
usedSchemaRefs[typeKey] = true;
if (schema.type === 'genericType') {
let children: Array<any> = [];
if (schema.typeKey === '#/genericTypes/List') {
children = genRefSchemaChildren(schema.typeInstantiation.typeParams[0].typeParamValue, code, usedSchemaRefs);
} else {
const genericClass = dataTypesMap[schema.typeKey];
if (genericClass) {
const propertyList = genericClass.propertyList;
propertyList.forEach((property: any) => {
let propertySchema = cloneDeep(property);
if (property.type === 'genericParam' && schema.typeInstantiation) {
const param = schema.typeInstantiation.typeParams.find((typeParam: any) => typeParam.typeParamName === property.typeParamName);
if (param) {
propertySchema = Object.assign({ name: property.name }, param.typeParamValue);
}
} else if (propertySchema.type === 'genericType' && schema.typeInstantiation) {
if (propertySchema.typeInstantiation) {
propertySchema.typeInstantiation.typeParams.forEach((typeParam: any) => {
const param = schema.typeInstantiation.typeParams.find((typeParamTemp: any) => typeParamTemp.typeParamName === typeParam.typeParamName);
if (param) {
typeParam.typeParamValue = Object.assign({}, param.typeParamValue);
}
});
}
}
const item: any = {
level: 'refProperty',
schema: propertySchema,
code: property.id ? `${code}.ID_${property.id}` : `${code}.${property.name}`,
};
item.children = genRefSchemaChildren(item.schema, item.code, usedSchemaRefs);
item.isLeaf = !item.children;
children.push(item);
});
}
}
return children;
} else if (schema.type === 'object') {
const children: Array<any> = [];
schema.propertyList && schema.propertyList.forEach((property: Schema) => {
const item: any = {
level: 'refProperty',
schema: property,
code: `${code}.ID_${property.id}`,
};
item.children = genFullRefSchemaChildren(item.schema, item.code, usedSchemaRefs);
item.isLeaf = !item.children;
children.push(item);
});
return children;
} else if (schema.isArray) {
//
} else if (typeKey && typeKey.startsWith('#/basicTypes')) {
return undefined;
} else if (next) {
return genFullRefSchemaChildren(next, code, usedSchemaRefs);
}
}
/**
* 统一在 lcap-pc-template 中处理
*/
export function genInitData(schema: Schema, relationship = 'None', usedSchemaRefs: { [name: string]: boolean } = {}): any {
throw new Error('统一在 lcap-pc-template 中处理!');
}