packages/ros-cdk-codegen/lib/update-spec.ts (188 lines of code) (raw):
const rosClient = require('@alicloud/ros-2019-09-10');
import * as fs from 'fs';
import * as path from 'path';
// client init
function initClient(endpoint: string, accessKeyId: string, accessKeySecret: string) {
let client = new rosClient({
endpoint: endpoint ? endpoint : 'https://ros.aliyuncs.com',
accessKeyId: accessKeyId,
accessKeySecret: accessKeySecret,
});
return client
}
function getPropertyTypeName(type: string, propertyName: string, path: Array<string>, propertyTypes: any): string {
let shortName = propertyName;
let fullName = `${type}.${shortName}`;
if (!(fullName in propertyTypes)) {
return shortName;
}
let path2 = [...path];
path2.reverse();
let middle = '';
for (const token of path2) {
middle = token + middle;
shortName = `${middle}${propertyName}`
fullName = `${type}.${shortName}`;
if (!(fullName in propertyTypes)) {
return shortName;
}
}
let i = 1;
while (true) {
let shortName2 = `${shortName}${i}`
fullName = `${type}.${shortName2}`;
if (!(fullName in propertyTypes)) {
return shortName2;
}
i++;
}
}
export function specGenerator() {
let spec: any = {};
// ALIYUN开头的资源集合
let onlyResourceTypes: Set<string> = new Set();
// ALIYUN与DATASOURCE开头的资源集合
let fullResourceTypes: Set<string> = new Set();
let codeMappingTypesArray = new Array();
let resourceTypes: any = {};
let propertyTypes: any = {};
let codeMappingFile = fs.readFileSync(path.join(__dirname, '/../../@alicloud/ros-cdk-spec/spec/code_mapping.json')).toString();
let codeMappingFileContent = JSON.parse(codeMappingFile);
let specificationOriginFile = fs.readFileSync(path.join(__dirname, '/../../@alicloud/ros-cdk-spec/spec/specification_origin.json')).toString();
let specificationOriginFileContent = JSON.parse(specificationOriginFile);
let fieldMappingFile = fs.readFileSync(path.join(__dirname, '/../../@alicloud/ros-cdk-spec/spec/field_mapping.json')).toString();
const fieldMappingContent = JSON.parse(fieldMappingFile);
for (let [type] of Object.entries(specificationOriginFileContent['ResourceTypes'])) {
let category: string[] = type.split('::');
// e.g. ALIYUN::ECS::VPC
if (category[0].startsWith('ALIYUN')) {
onlyResourceTypes.add(category[0] + '::' + category[1]);
}
fullResourceTypes.add(category[0] + '::' + category[1]);
let typeDetail = specificationOriginFileContent['ResourceTypes'][type];
// handle properties
// string, integer, number, boolean -> PrimitiveType
// list, map -> Type + PrimitiveType / ItemType
// move 'schema' to propertyTypes
let queue: { [key: string]: any } = {};
queue[type] = [typeDetail.Properties, []];
while (Object.keys(queue).length > 0) {
let queueNext: { [key: string]: any } = {};
let resourceNames = Object.keys(queue);
resourceNames.sort()
for (let resourceName of resourceNames) {
let properties = queue[resourceName][0];
let path = queue[resourceName][1];
if (properties === null || properties === undefined) continue;
let propertyNames = Object.keys(properties);
propertyNames.sort()
for (let propertyName of propertyNames) {
let Property = properties[propertyName];
Property.PrimitiveName = propertyName;
// replace all property including "." with ""
let newPropertyName = propertyName.replace(new RegExp('\\.', 'gm'), '');
// Replace the language-reserved field with the specified field
if (newPropertyName in fieldMappingContent) {
newPropertyName = fieldMappingContent[newPropertyName];
}
if (newPropertyName !== propertyName) {
properties[newPropertyName] = Property;
delete properties[propertyName];
propertyName = newPropertyName;
}
let propertyType = Property.Type;
let Schema = Property.Schema;
delete Property.Schema;
delete Property.Type;
switch (propertyType) {
case 'string':
case 'integer':
case 'number':
case 'boolean':
Property.PrimitiveType = initialToUpperCase(propertyType);
break;
case 'list':
Property.Type = 'List';
if (Schema) {
if (Schema['*']['Schema']) {
Property.ItemType = getPropertyTypeName(type, propertyName, path, propertyTypes);
let subTypeName = `${type}.${Property.ItemType}`
Schema = Schema['*']['Schema'];
queueNext[subTypeName] = [Schema, [...path, propertyName]];
propertyTypes[subTypeName] = {
Properties: Schema,
};
} else if (Schema['*']['Type'] === 'map') {
Property.PrimitiveItemType = 'AnyDict';
} else {
Property.PrimitiveItemType = initialToUpperCase(Schema['*']['Type']);
}
} else {
Property.PrimitiveItemType = 'Any';
}
break;
case 'map':
if (Schema) {
Property.Type = getPropertyTypeName(type, propertyName, path, propertyTypes);
let subTypeName = `${type}.${Property.Type}`;
queueNext[subTypeName] = [Schema, [...path, propertyName]];
propertyTypes[subTypeName] = {
Properties: Schema,
};
} else {
Property.Type = 'Map';
// target: {[key: string]: any}
// now: {[key: string]: string}
// Property.PrimitiveType = initialToUpperCase('any');
Property.PrimitiveItemType = 'Any';
}
break;
}
}
}
queue = queueNext;
}
resourceTypes[`${type}`] = {
Description: typeDetail.Description ? typeDetail.Description : '',
Documentation: typeDetail.Documentation ? typeDetail.Documentation : '',
Attributes: typeDetail.Attributes ? typeDetail.Attributes : {},
Properties: typeDetail.Properties ? typeDetail.Properties : {},
};
}
for (let [rosCode] of Object.entries(codeMappingFileContent['CodeMapping'])) {
codeMappingTypesArray.push('ALIYUN::' + rosCode)
}
let onlyTypesArray = Array.from(onlyResourceTypes)
let fullTypesArray = Array.from(fullResourceTypes)
let diffArr = getArrDifference(onlyTypesArray, codeMappingTypesArray)
if(diffArr != null && diffArr != ""){
throw new Error(`code_mapping file diff with types file "${diffArr}", please add information with code_mapping file.`);
}
fs.writeFileSync(path.join(__dirname, '/../../@alicloud/ros-cdk-spec/spec/types.json'), JSON.stringify(fullTypesArray, null, '\t'));
spec['PropertyTypes'] = propertyTypes;
spec['ResourceTypes'] = resourceTypes;
fs.writeFileSync(path.join(__dirname, '/../../@alicloud/ros-cdk-spec/spec/specification.json'), JSON.stringify(spec, null, '\t'));
}
export async function specOriginGenerator(endpoint: string, accessKeyId: string, accessKeySecret: string) {
let spec: any = {};
let resourceTypes: any = {};
let client = initClient(endpoint, accessKeyId, accessKeySecret)
let allResourceTypes = await client.listResourceTypes({EntityType: 'All'})
for (let type of allResourceTypes.ResourceTypes) {
// e.g. ALIYUN::ECS::VPC & DATASOURCE::VPC::FlowLogs
if (type.startsWith('DATASOURCE') || type.startsWith('ALIYUN')) {
let typeDetail = await client.getResourceType({ResourceType: type});
let properties = typeDetail.Properties ? typeDetail.Properties : {};
resourceTypes[`${typeDetail.ResourceType}`] = {
Description: typeDetail.Description ? typeDetail.Description : '',
Documentation: `https://www.alibabacloud.com/help/ros/developer-reference/${type.toLowerCase().replace(/::/g, '-')}`,
Attributes: typeDetail.Attributes ? typeDetail.Attributes : {},
Properties: properties,
};
await sleep(1000);
}
}
spec['ResourceTypes'] = resourceTypes;
fs.writeFileSync(path.join(__dirname, '/../../@alicloud/ros-cdk-spec/spec/specification_origin.json'), JSON.stringify(spec, null, '\t'));
}
const sleep = function (ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
};
function initialToUpperCase(str: string): string {
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
function getArrDifference(arr1: any, arr2: any) {
let newArr = [];
let arr3 = arr1.concat(arr2);
function isContain(value: any) {
return arr1.indexOf(value) == -1 || arr2.indexOf(value) == -1;
}
newArr = arr3.filter(isContain);
return newArr;
}