UNPKG

12.3 kBJavaScriptView Raw
1'use strict';
2
3const debug = require('debug')('fun:common');
4
5const _ = require('lodash');
6const { green, red } = require('colors');
7
8const { promptForFunctionSelection } = require('./init/prompt');
9
10const SERVICE_RESOURCE = 'Aliyun::Serverless::Service';
11const FUNCTION_RESOURCE = 'Aliyun::Serverless::Function';
12const FLOW_RESOURCE = 'Aliyun::Serverless::Flow';
13
14/* eslint-disable */
15function iterateResources(resources, resType, callback) {
16 if (!resources) {
17 return;
18 }
19 for (const [name, res] of Object.entries(resources)) {
20 if (res.Type === resType) {
21 callback(name, res);
22 }
23 }
24}
25/* eslint-enable */
26
27function findResourceByName(resources, resourceName) {
28 for (const [name, res] of Object.entries(resources)) {
29 if (name === resourceName) {
30 return {
31 resourceName,
32 resourceRes: res
33 };
34 }
35 }
36 return;
37}
38
39function findServices(resources) {
40 const services = [];
41
42 iterateResources(resources, SERVICE_RESOURCE, (serviceName, serviceRes) => {
43 services.push({
44 serviceName,
45 serviceRes
46 });
47 });
48
49 return services;
50}
51
52function iterateFunctions(tplContent, callback) {
53 if (tplContent.Resources) {
54 const resources = tplContent.Resources;
55
56 iterateResources(resources, SERVICE_RESOURCE, (serviceName, serviceRes) => {
57 iterateResources(serviceRes, FUNCTION_RESOURCE, (functionName, functionRes) => {
58 callback(
59 serviceName,
60 serviceRes,
61 functionName,
62 functionRes
63 );
64 });
65 });
66 }
67}
68
69function findFunctions(serviceRes) {
70
71 const functions = [];
72
73 iterateResources(serviceRes, FUNCTION_RESOURCE, (functionName, functionRes) => {
74 functions.push({
75 functionName,
76 functionRes
77 });
78 });
79
80 return functions;
81}
82
83function findHttpTriggersInFunction(functionRes) {
84 const triggers = [];
85
86 if (functionRes.Events) {
87 iterateResources(functionRes.Events, 'HTTP', (triggerName, triggerRes) => {
88 triggers.push({
89 triggerName,
90 triggerRes
91 });
92 });
93 }
94
95 return triggers;
96}
97
98function findFunctionByServiceAndFunctionName(resources, serviceName, functionName) {
99 debug('begin search serviceName and functionName');
100
101 let serviceRes = resources[serviceName];
102
103 if (!serviceRes || !serviceName || serviceRes.Type !== SERVICE_RESOURCE) {
104
105 throw new Error(`could not found service: ${serviceName}`);
106 }
107
108 let functionRes = serviceRes[functionName];
109
110 if (functionRes && functionRes.Type !== FUNCTION_RESOURCE) {
111 functionRes = null;
112 }
113
114 return {
115 serviceName,
116 serviceRes,
117 functionName,
118 functionRes
119 };
120}
121
122function findFunctionInService(funcName, serviceRes) {
123
124 debug('find function ' + funcName + ' definition in service: ' + JSON.stringify(serviceRes));
125
126 for (let { functionName, functionRes } of findFunctions(serviceRes)) {
127 debug(`functionName is ${functionName}, compare with ${functionName}`);
128 if (functionName === funcName) {
129 debug(`found function ${functionName}, functionRes is ${functionRes}`);
130
131 return functionRes;
132 }
133 }
134
135 return null;
136}
137
138function findFunctionByFunctionName(resources, functionName) {
139 // iterator all services and functions
140 for (let { serviceName, serviceRes } of findServices(resources)) {
141 debug('servicename: ' + serviceName);
142 const functionRes = findFunctionInService(functionName, serviceRes);
143
144 if (functionRes) {
145 return {
146 serviceName,
147 serviceRes,
148 functionName,
149 functionRes
150 };
151 }
152 }
153
154 return {};
155}
156
157function parseDomainRoutePath(domainRoutePath) {
158 let domainName = null;
159 let routePath = null;
160
161 if (!domainRoutePath) { return []; }
162
163 const index = domainRoutePath.indexOf('/');
164 if (index < 0) {
165 domainName = domainRoutePath;
166 } else {
167 domainName = domainRoutePath.substring(0, index);
168 routePath = domainRoutePath.substring(index);
169 }
170 return [domainName, routePath];
171}
172
173function parseFunctionPath(funcPath) {
174 let serviceName = null;
175 let functionName = null;
176
177 if (!funcPath) { return []; }
178
179 const index = funcPath.indexOf('/');
180 if (index < 0) {
181 functionName = funcPath;
182 } else {
183 serviceName = funcPath.substring(0, index);
184 functionName = funcPath.substring(index + 1);
185 }
186 debug(`invoke service: ${serviceName}`);
187 debug(`invoke function: ${functionName}`);
188 return [serviceName, functionName];
189}
190
191/**
192 * funcPath : functionName or serviceName/functionName
193 */
194function findFunctionInTpl(funcPath, tpl) {
195 const [serviceName, functionName] = parseFunctionPath(funcPath);
196 return doFindFunctionInTpl(serviceName, functionName, tpl);
197}
198
199// return first if only provide functionName
200function doFindFunctionInTpl(serviceName, functionName, tpl) {
201
202 const resources = tpl.Resources;
203
204 if (serviceName) {
205 // invokeName is serviceName/functionName
206 return findFunctionByServiceAndFunctionName(resources, serviceName, functionName);
207 }
208 // invokeName is functionName
209 return findFunctionByFunctionName(resources, functionName);
210}
211
212function findFunctionsInTpl(tpl, filter) {
213 const functions = [];
214
215 const resources = tpl.Resources;
216
217 for (let { serviceName, serviceRes } of findServices(resources)) {
218 for (let { functionName, functionRes } of findFunctions(serviceRes)) {
219
220 if (filter && !filter(functionName, functionRes)) { continue; }
221
222 functions.push({
223 serviceName,
224 serviceRes,
225 functionName,
226 functionRes
227 });
228 }
229 }
230
231 return functions;
232}
233
234function findNasConfigInService(serviceRes) {
235 if (!serviceRes) { return null; }
236
237 const serviceProps = serviceRes.Properties;
238
239 if (!serviceProps) { return null; }
240
241 return serviceProps.NasConfig;
242}
243
244function findHttpTriggersInTpl(tpl) {
245 const resources = tpl.Resources;
246
247 const httpTriggers = [];
248
249 for (let { serviceName, serviceRes } of findServices(resources)) {
250 for (let { functionName, functionRes } of findFunctions(serviceRes)) {
251 for (let { triggerName, triggerRes } of findHttpTriggersInFunction(functionRes)) {
252 httpTriggers.push({
253 serviceName,
254 serviceRes,
255 functionName,
256 functionRes,
257 triggerName,
258 triggerRes
259 });
260 }
261 }
262 }
263
264 return httpTriggers;
265}
266
267function findFirstFunctionName(tpl) {
268 const resources = tpl.Resources;
269
270 var firstInvokeName;
271
272 for (let { serviceName, serviceRes } of findServices(resources)) {
273 for (let { functionName } of findFunctions(serviceRes)) {
274 firstInvokeName = serviceName + '/' + functionName;
275 break;
276 }
277 }
278
279 if (!firstInvokeName) {
280 throw new Error(red(`Missing function definition in template.yml`));
281 }
282 return firstInvokeName;
283}
284
285// find the first service in resoueces
286function findServiceByServiceName (resources, name) {
287
288 for (let { serviceName, serviceRes } of findServices(resources)) {
289
290 if (serviceName === name) {
291 return {
292 serviceName,
293 serviceRes
294 };
295 }
296 }
297 return {};
298}
299
300function findAllFunctionsByFunctionName(resources, functionName) {
301
302 let functions = [];
303
304 for (let { serviceName, serviceRes } of findServices(resources)) {
305 debug('servicename: ' + serviceName);
306 const functionRes = findFunctionInService(functionName, serviceRes);
307
308 if (functionRes) {
309
310 functions.push({
311 serviceName,
312 serviceRes,
313 functionName,
314 functionRes
315 });
316 }
317 }
318 return functions;
319}
320
321async function matchingResourceBySourceName(resources, sourceName) {
322
323 const resourceObj = findResourceByName(resources, sourceName);
324
325 if (!_.isEmpty(resourceObj)) {
326 return resourceObj;
327 }
328
329 const functions = findAllFunctionsByFunctionName(resources, sourceName);
330
331 if (functions.length === 0) {
332
333 throw new Error(`could not found sourceName: ${sourceName}`);
334 } else if (functions.length > 1) {
335
336 const { serviceName, functionName } = await promptForFunctionSelection(functions);
337
338 const selectionFunction = functions.find(funcObj => {
339
340 return funcObj.serviceName === serviceName && funcObj.functionName === functionName;
341 });
342 // delete unmatch functions under a serviceRes
343 const serviceRes = deleteUnmatchFunctionsUnderServiceRes(selectionFunction);
344
345 return {
346 resourceName: selectionFunction.serviceName,
347 resourceRes: serviceRes
348 };
349 }
350
351 const serviceName = _.first(functions).serviceName;
352 let serviceRes = _.first(functions).serviceRes;
353
354 serviceRes = deleteUnmatchFunctionsUnderServiceRes({
355 serviceName,
356 serviceRes,
357 functionName: sourceName
358 });
359
360 return {
361 resourceName: serviceName,
362 resourceRes: serviceRes
363 };
364}
365
366// delete unmatch functions under a serviceRes
367function deleteUnmatchFunctionsUnderServiceRes({
368 serviceName,
369 serviceRes,
370 functionName
371}) {
372
373 const functionNamesInService = findFunctions(serviceRes).map(funRes => {
374 return funRes.functionName;
375 });
376
377 if (!_.includes(functionNamesInService, functionName)) {
378
379 throw new Error(`could not found service/function:` + green(`${serviceName}`) + `/` + red(`${functionName}`));
380 }
381
382 for (let functions of findFunctions(serviceRes)) {
383
384 if (functions.functionName !== functionName) {
385
386 serviceRes = _.omit(serviceRes, functions.functionName);
387 }
388 }
389
390 return serviceRes;
391}
392
393function findServiceByCertainServiceAndFunctionName(resources, certainServiceName, certainFunctionName) {
394
395 for (let { serviceName, serviceRes } of findServices(resources)) {
396
397 if (serviceName === certainServiceName) {
398
399 serviceRes = deleteUnmatchFunctionsUnderServiceRes({
400 serviceName,
401 serviceRes,
402 functionName: certainFunctionName
403 });
404
405 return {
406 serviceName,
407 serviceRes
408 };
409 }
410 }
411
412 throw new Error(`could not found service: ${certainServiceName}`);
413}
414
415
416function ensureNasParams(nasConfig) {
417
418 const propsRequired = ['Auto', 'UserId', 'GroupId'];
419
420 const notExistParams = propsRequired.filter(paramter => {
421 return !nasConfig.hasOwnProperty(paramter);
422 });
423
424 if (!_.isEmpty(notExistParams)) {
425 console.error(red(''));
426 throw new Error(red(`Missing '${notExistParams.join(', ')}' in NasConfig.`));
427 }
428 if (!_.isEmpty(nasConfig.MountPoints)) {
429 console.error(red(''));
430 throw new Error(red(`Additional properties: 'MountPoints' in NasConfig.`));
431 }
432}
433
434function isNasAutoConfig(nasConfig) {
435
436 if (nasConfig === 'Auto') {
437 return true;
438 }
439
440 if ((nasConfig || {}).Auto) {
441 ensureNasParams(nasConfig);
442 return true;
443 }
444
445 return false;
446}
447
448function getUserIdAndGroupId(nasConfig) {
449 if (_.isEmpty(nasConfig)) { return {}; }
450
451 if (nasConfig === 'Auto') {
452 return {
453 userId: 10003,
454 groupId: 10003
455 };
456 }
457 return {
458 userId: nasConfig.UserId,
459 groupId: nasConfig.GroupId
460 };
461}
462
463function isVpcAutoConfig(vpcConfig) {
464 if (vpcConfig === 'Auto') { return true; }
465 return false;
466}
467
468// except Auto
469function onlyOneNASExists(nasConfig) {
470 const isNasAuto = isNasAutoConfig(nasConfig);
471
472 if (_.isEmpty(nasConfig || isNasAuto)) {
473 return false;
474 }
475 const mountPoints = nasConfig.MountPoints || [];
476 return mountPoints.length === 1;
477}
478
479function validateNasAndVpcConfig(resources) {
480 if (_.isEmpty(resources)) { return; }
481
482 for (const [name, resource] of Object.entries(resources)) {
483 if (resource.Type === SERVICE_RESOURCE) {
484 const serviceprop = (resource.Properties || {});
485 const vpcConfig = serviceprop.VpcConfig;
486 const nasConfig = serviceprop.NasConfig;
487
488 if (isNasAutoConfig(nasConfig) && !_.isEmpty(vpcConfig) && !isVpcAutoConfig(vpcConfig)) {
489 throw new Error(`\nVpcConfig is not supported by 'NasConfig:Auto' in service: ${name}`);
490 }
491 }
492 }
493}
494
495module.exports = {
496 findFunctionInTpl, findHttpTriggersInTpl,
497 findFunctionsInTpl, findNasConfigInService, iterateResources,
498 findHttpTriggersInFunction, findServices, findResourceByName,
499 findFunctions, findFirstFunctionName, matchingResourceBySourceName,
500 findServiceByCertainServiceAndFunctionName, deleteUnmatchFunctionsUnderServiceRes,
501 isNasAutoConfig, isVpcAutoConfig, parseFunctionPath, iterateFunctions, parseDomainRoutePath,
502 onlyOneNASExists, findServiceByServiceName, findFunctionByServiceAndFunctionName, getUserIdAndGroupId,
503 SERVICE_RESOURCE, FUNCTION_RESOURCE, FLOW_RESOURCE, validateNasAndVpcConfig
504};
\No newline at end of file