UNPKG

6.95 kBJavaScriptView Raw
1'use strict';
2const _ = require('lodash');
3const YAML = require('js-yaml');
4const { validateParameters } = require('./deploy/deploy-support-ros');
5const { SERVICE_RESOURCE, iterateResources, iterateFunctions } = require('./definition');
6const transformFunctionInDefinition = (definition = {}, tpl = {}, parameterOverride = {}, useRos = false) => {
7 if (!_.isEmpty(parameterOverride)) {
8 validateParameters(tpl.Parameters, parameterOverride);
9 }
10 const prefixMap = {};
11 const dependsOn = [];
12 const generatePrefix = (prefix, key) => prefix ? `${prefix}.${key}` : key;
13 const refFuncConverter = (value) => {
14 if (!_.isString(value)) {
15 throw new Error('Value of !Ref should be string');
16 }
17 const resourcePath = value.split('/');
18 const resources = tpl.Resources || {};
19 const parameters = tpl.Parameters || {};
20 let isParam = false;
21 if (!_.has(resources, resourcePath)) {
22 if (resourcePath.length === 1 && !_.has(parameters, value)) {
23 throw new Error(`Did not find resource or parameter '${value}'`);
24 }
25 else if (resourcePath.length !== 1) {
26 throw new Error(`Did not find resource '${value}'`);
27 }
28 else {
29 isParam = true;
30 }
31 }
32 if (isParam) {
33 if (useRos) {
34 return `\${${value}}`;
35 }
36 if (parameterOverride[value]) {
37 return parameterOverride[value];
38 }
39 else if (_.has(parameters, [value, 'Default'])) {
40 return _.get(parameters, [value, 'Default']);
41 }
42 throw new Error(`Parameter '${value}' has not been set value`);
43 }
44 const resource = _.get(resources, resourcePath);
45 const resourceType = resource.Type || '';
46 if (resourcePath.length === 1) {
47 if (resourceType === 'Aliyun::Serverless::Service') {
48 dependsOn.push(resourcePath[0]);
49 return useRos ? `\${${resourcePath[0]}.ARN}` : `acs:fc:::services/${resourcePath[0]}`;
50 }
51 }
52 if (resourcePath.length === 2) {
53 if (resourceType === 'Aliyun::Serverless::Function') {
54 dependsOn.push(`${resourcePath[0]}${resourcePath[1]}`);
55 return useRos ? `\${${resourcePath[0]}${resourcePath[1]}.ARN}`
56 : `acs:fc:::services/${resourcePath[0]}/functions/${resourcePath[1]}`;
57 }
58 }
59 throw new Error(`Can not convert resource '${value}' to arn`);
60 };
61 const getAttFuncConverter = (value) => {
62 if (!_.isArray(value) || value.length !== 2) {
63 throw new Error('Value of !GetAtt should be the following form: aaa/bbb/ccc.p1.p2');
64 }
65 const resourcePath = value[0].split('/');
66 const resources = tpl.Resources || {};
67 if (!_.has(resources, resourcePath)) {
68 throw new Error(`Did not find resource '${value}'`);
69 }
70 const resource = _.get(resources, resourcePath);
71 const resourceProperties = resource.Properties || {};
72 if (useRos) {
73 return `\${${resourcePath.join('')}.${value[1]}}`;
74 }
75 if (!_.has(resourceProperties, value[1])) {
76 throw new Error(`Did not find '${value[0]}' resource's property '${value[1]}'`);
77 }
78 return _.get(resourceProperties, value[1]);
79 };
80 const functionConverters = {
81 'Ref': refFuncConverter,
82 'Fn::GetAtt': getAttFuncConverter
83 };
84 const iterateObject = (obj, prefix) => {
85 _.forIn(obj, (value, key, obj) => {
86 if (_.keys(functionConverters).includes(key)) {
87 const convertedValue = functionConverters[key](value);
88 const { obj: o, key: k } = prefixMap[prefix];
89 o[k] = convertedValue;
90 delete prefixMap[prefix];
91 }
92 else if (_.isObjectLike(value)) {
93 if (_.keys(value).length === 1 && _.keys(functionConverters).includes(_.keys(value)[0])) {
94 prefixMap[generatePrefix(prefix, key)] = {
95 obj,
96 key
97 };
98 }
99 iterateObject(value, generatePrefix(prefix, key));
100 }
101 });
102 };
103 iterateObject(definition);
104 return {
105 definition: YAML.dump(definition),
106 dependsOn: dependsOn
107 };
108};
109const transformFlowDefinition = (definition, tpl = {}, parameterOverride = {}) => {
110 if (_.isString(definition)) {
111 return definition;
112 }
113 if (!_.isObject(definition) ||
114 !_.has(definition, 'Fn::Sub') ||
115 !_.isString(_.get(definition, 'Fn::Sub'))) {
116 throw new Error('The flow definition in this format can not be converted');
117 }
118 const resourceMap = generateResourceMap(tpl);
119 const parameterMap = generateParameterMap(tpl, parameterOverride);
120 const replaceMap = {};
121 definition = _.get(definition, 'Fn::Sub');
122 const regex = new RegExp(/\${(.*)}/g);
123 let execRes;
124 while ((execRes = regex.exec(definition))) {
125 const indexKey = execRes[1];
126 if (indexKey.split('.').length > 1) {
127 const [first, ...tail] = indexKey.split('.');
128 replaceMap[execRes[0]] = _.get(resourceMap[first], tail.join('.'), 'NONE');
129 }
130 else {
131 replaceMap[execRes[0]] = parameterMap[indexKey] || 'NONE';
132 }
133 }
134 for (const [src, target] of Object.entries(replaceMap)) {
135 definition = definition.split(src).join(target);
136 }
137 return definition;
138};
139const generateResourceMap = (tpl = {}) => {
140 const resourceMap = new Map();
141 iterateResources(tpl.Resources, SERVICE_RESOURCE, (name, res) => {
142 const properties = res.Properties || {};
143 properties.ARN = `acs:fc:::services/${name}`;
144 properties.ServiceName = name;
145 resourceMap[name] = properties;
146 });
147 iterateFunctions(tpl, (serviceName, serviceRes, functionName, functionRes) => {
148 const properties = functionRes.Properties || {};
149 properties.ARN = `acs:fc:::services/${serviceName}/functions/${functionName}`;
150 properties.ServiceName = serviceName;
151 properties.FunctionName = functionName;
152 resourceMap[`${serviceName}${functionName}`] = properties;
153 });
154 return resourceMap;
155};
156const generateParameterMap = (tpl = {}, parameterOverride = {}) => {
157 const parameterMap = new Map();
158 const parameters = tpl.Parameters || {};
159 for (const [name, def] of Object.entries(parameters)) {
160 if (parameterOverride[name]) {
161 parameterMap[name] = parameterOverride[name];
162 }
163 else if (def.Default) {
164 parameterMap[name] = def.Default;
165 }
166 }
167 return parameterMap;
168};
169module.exports = {
170 transformFunctionInDefinition,
171 transformFlowDefinition
172};