UNPKG

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