@kumologica/sdk 3.2.0-beta2 → 3.2.0-beta20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,28 +1,19 @@
1
1
  const fs = require('fs-extra');
2
2
  const path = require('path');
3
3
  const AWS = require('aws-sdk');
4
- const AdmZip = require('adm-zip');
5
- const util = require('util');
6
- const exec = util.promisify(require('child_process').exec);
7
- const rimraf = require('rimraf');
8
- const { codegen } = require('@kumologica/builder');
9
- const AWSCFTemplate = require('./cf');
10
4
  const CAAWSCloudWatch = require('./ca-cloudwatch-api');
11
- const CAAWSIot = require('./ca-iot-api');
12
5
  const CAS3 = require('./ca-s3-api');
13
6
  const CASNS = require('./ca-sns-api');
14
7
  const CASQS = require('./ca-sqs-api');
15
8
  const CAAWSApiGateway = require('./ca-apigw-api');
16
9
  const CADynamoDb = require('./ca-dynamodb-api');
17
- const CAElb = require('./ca-elb-api');
18
10
  const CAEvents = require('./ca-events-api');
19
- const CAAlexa = require('./ca-alexa-api');
20
- const CACodeCommit = require('./ca-codecommit-api');
21
11
  const AWSProfile = require('./aws-profile');
22
- const KLIAM = require('./kl-iam-api');
23
12
  const { openFileOnEditor } = require('../utils/editor');
13
+ const { exp } = require('@kumologica/builder');
14
+ const { build } = require('@kumologica/builder');
24
15
 
25
- /**
16
+ /**
26
17
  * sample events:
27
18
  * https://docs.aws.amazon.com/lambda/latest/dg/lambda-services.html
28
19
  */
@@ -45,7 +36,6 @@ class AWSDeployer {
45
36
 
46
37
  this.cw = false;
47
38
 
48
- this.iot = new CAAWSIot(this.log.bind(this));
49
39
  this.apiGateway = new CAAWSApiGateway(this.log.bind(this));
50
40
  this.dynamoDb = new CADynamoDb(this.log.bind(this));
51
41
  this.cf = new AWS.CloudFormation();
@@ -53,15 +43,10 @@ class AWSDeployer {
53
43
  this.sns = new CASNS(this.log.bind(this));
54
44
  this.sqs = new CASQS(this.log.bind(this));
55
45
  this.lambdaApi = new AWS.Lambda();
56
- this.cfTemplate = new AWSCFTemplate();
57
46
  this.cwLogs = new CAAWSCloudWatch(this.logCloud.bind(this));
58
47
  this.cwevents = new AWS.CloudWatchEvents();
59
48
  this.cognito = new AWS.CognitoIdentityServiceProvider();
60
- this.elb = new CAElb(this.log.bind(this));
61
49
  this.events = new CAEvents(this.log.bind(this));
62
- this.alexa = new CAAlexa(this.log.bind(this));
63
- this.codecommit = new CACodeCommit(this.log.bind(this));
64
- this.iam = new KLIAM(this.log.bind(this));
65
50
  }
66
51
 
67
52
  chalk(c, t) {
@@ -107,23 +92,9 @@ class AWSDeployer {
107
92
  }
108
93
 
109
94
  prepare(projectDir, flowFileName, originaLambdaName) {
110
- const deployDir = projectDir + '/deploy';
111
95
 
96
+ const deployDir = path.join(projectDir, "build");
112
97
  const flowName = flowFileName.replace('.json', '');
113
-
114
- // setup deployment directory
115
- fs.ensureDirSync(deployDir);
116
- fs.emptyDirSync(deployDir);
117
- fs.ensureDirSync(deployDir + '/node_modules');
118
-
119
- // copy project files
120
- codegen.generateProjectCode(
121
- deployDir,
122
- flowFileName
123
- );
124
-
125
- this.processPackageJson(projectDir, deployDir);
126
-
127
98
  const zipFileName = `${flowName}/lambda${Date.now()}.zip`;
128
99
  const lambdaName = this.sanitizeLambdaName(originaLambdaName);
129
100
 
@@ -141,34 +112,6 @@ class AWSDeployer {
141
112
  };
142
113
  }
143
114
 
144
- processPackageJson(projectDir, deployDir) {
145
-
146
- const pckg = this.loadJsonFile(path.join(projectDir, 'package.json'));
147
-
148
- // ensure dependencies contain runtime
149
- if (!pckg.dependencies["@kumologica/runtime"]) {
150
- pckg.dependencies["@kumologica/runtime"] = `^${pckg.version.substr(0, 1)}`;
151
- this.createFile(deployDir, 'package.json', JSON.stringify(pckg));
152
-
153
- } else {
154
- fs.copySync(
155
- path.join(projectDir, 'package.json'),
156
- path.join(deployDir, 'package.json')
157
- );
158
- }
159
-
160
- if (pckg.files) {
161
- pckg.files.forEach(f => {
162
-
163
- if (!["node_modules/**/*", "lambda.js" ].includes(f)) {
164
- fs.copySync(
165
- path.join(projectDir, f),
166
- path.join(deployDir, f)
167
- );
168
- }
169
- });
170
- }
171
- }
172
115
 
173
116
  /**
174
117
  *
@@ -234,7 +177,8 @@ class AWSDeployer {
234
177
  settings.deploymentBucketName = await this.prepareS3Bucket(
235
178
  settings.flowName
236
179
  );
237
- await this.buildLambda(settings);
180
+
181
+ await this.buildLambda(settings, projectInfo);
238
182
 
239
183
  const nodes = this.loadJsonFile(
240
184
  path.join(projectInfo.projectDir, projectInfo.projectFlowName)
@@ -246,17 +190,38 @@ class AWSDeployer {
246
190
  settings,
247
191
  params,
248
192
  deploymentStart,
249
- nodes
193
+ nodes,
194
+ projectInfo
250
195
  );
196
+ /*
197
+ "Outputs": [
198
+ {
199
+ "OutputKey": "LambdaArn",
200
+ "OutputValue": "arn:aws:lambda:ap-southeast-2:174842903734:function:sockets-flow",
201
+ "Description": "The Arn of the kumologica flow lambda."
202
+ },
203
+ {
204
+ "OutputKey": "RootResourceId",
205
+ "OutputValue": "qb9cfkwah9",
206
+ "Description": "The root resource id of rest api gateway created in this script."
207
+ },
208
+ {
209
+ "OutputKey": "RestApiId",
210
+ "OutputValue": "h1eihbg3gb",
211
+ "Description": "The id of rest api gateway created in this script."
212
+ }
213
+ */
214
+
251
215
  lambdaArn = stackDetails.Stacks[0].Outputs[0].OutputValue;
252
216
 
217
+ this.log(JSON.stringify(stackDetails));
253
218
  this.log(`${this.chalk('greenBright', 'Deployment successful.')}`);
254
219
  this.log(
255
220
  ` ${this.chalk('#F5DEB3', 'StackId:')} ${this.chalk('whiteBright',
256
221
  stackDetails.Stacks[0].StackId
257
222
  )}`,
258
223
  false
259
- );
224
+ );
260
225
  this.log(` ${this.chalk('#F5DEB3', 'LambdaArn:')} ${this.chalk('whiteBright',lambdaArn)}`, false);
261
226
  this.log('', false);
262
227
 
@@ -265,95 +230,87 @@ class AWSDeployer {
265
230
  this.cwLogs.startCWLogs(settings.functionName, AWS.config.region);
266
231
  this.cw = true;
267
232
  }*/
268
- } catch (error) {
269
- this.log(`${this.chalk('redBright', 'Deployment failed.')}`);
270
- this.log(` ${this.chalk('redBright', error)}`);
271
- } finally {
272
- //this.s3.deleteS3Bucket(deploymentBucketName);
273
- }
274
233
 
275
- let response = {};
276
- if (lambdaArn) {
277
- try {
278
- response = await this.runTriggers(params, settings, lambdaArn);
279
- if (response) {
280
- const url = `https://${response.apiId}.execute-api.${AWS.config.region}.amazonaws.com/${response.stage}`;
281
- this.printSignature(url, flowListeners, params);
282
- }
283
- } catch (error) {
284
- this.log(`${this.chalk('redBright', 'Trigger creation failed.')}`);
285
- this.log(` ${this.chalk('redBright', error)}`);
286
- }
287
- }
288
- return response;
234
+ } catch (error) {
235
+ this.log(`${this.chalk('redBright', 'Deployment failed.')}`);
236
+ this.log(` ${this.chalk('redBright', error)}`);
237
+ } finally {
238
+ //this.s3.deleteS3Bucket(deploymentBucketName);
239
+ }
240
+
241
+ if (lambdaArn) {
242
+ // response = await this.runTriggers(params, settings, lambdaArn);
243
+ const url = `https://${response.apiId}.execute-api.${AWS.config.region}.amazonaws.com/${response.stage}`;
244
+ this.printSignature(url, flowListeners, params);
289
245
  }
246
+ }
290
247
 
291
- printSignature(url, s, p) {
292
- this.log(' ')
293
-
294
- if (s.api && s.api.length > 0) {
295
- this.log('API Gateway:');
296
- s.api.forEach(a => this.log(` ${(' ' + a.verb.toUpperCase()).slice(-6)} ${url}${a.url}`), this);
297
- this.log('');
298
- }
248
+ printSignature(url, s, p) {
249
+ this.log(' ')
299
250
 
300
- if (s.dynamodb && s.dynamodb.length > 0) {
301
- this.log('DynamoDB Streams:');
302
- s.dynamodb.forEach(a => this.log(` ${a.startingPosition} ${a.stream}`), this);
303
- this.log('');
304
- }
251
+ if (s.api && s.api.length > 0) {
252
+ this.log('API Gateway:');
253
+ s.api.forEach(a => this.log(` ${(' ' + a.verb.toUpperCase()).slice(-6)} ${url}${a.url}`), this);
254
+ this.log('');
255
+ }
305
256
 
306
- if (s.s3 && s.s3.length > 0) {
307
- this.log('S3:');
308
- s.s3.forEach(a => this.log(` ${a.eventType} ${a.bucket}`), this);
309
- this.log('');
310
- }
311
-
312
- if (s.sns && s.sns.length > 0) {
313
- this.log('SNS Topics:');
314
- s.sns.forEach(a => this.log(` ${a.topic}`), this);
315
- this.log('');
316
- }
317
-
318
- if (s.iot && s.iot.length > 0) {
319
- this.log('IOT:');
320
- s.iot.forEach(a => this.log(` ${a.rule} ${a.query}`), this);
321
- this.log('');
322
- }
257
+ if (s.dynamodb && s.dynamodb.length > 0) {
258
+ this.log('DynamoDB Streams:');
259
+ s.dynamodb.forEach(a => this.log(` ${a.startingPosition} ${a.stream}`), this);
260
+ this.log('');
261
+ }
323
262
 
324
- if (s.alb && s.alb.length > 0) {
325
- this.log('Application Load Balancer:');
326
- s.alb.forEach(a => this.log(` ${a.alb} ${a.listener}`), this);
327
- this.log('');
328
- }
263
+ if (s.s3 && s.s3.length > 0) {
264
+ this.log('S3:');
265
+ s.s3.forEach(a => this.log(` ${a.eventType} ${a.bucket}`), this);
266
+ this.log('');
267
+ }
329
268
 
330
- if (p.events && p.events.length > 0) {
331
- const sqs = p.events.filter(i => i.source == 'sqs');
332
- if (sqs && sqs.length > 0) {
333
- this.log('SQS URL:');
334
- sqs.forEach(a => this.log(` ${a.url}`), this);
335
- this.log('');
336
- }
337
- }
269
+ if (s.sns && s.sns.length > 0) {
270
+ this.log('SNS Topics:');
271
+ s.sns.forEach(a => this.log(` ${a.topic}`), this);
272
+ this.log('');
273
+ }
338
274
 
339
- if (s.alexa && s.alexa.length > 0) {
340
- this.log('Alexa Skills:');
341
- s.alexa.forEach(a => this.log(` ${a.skillId}`), this);
342
- this.log('');
343
- }
275
+ if (s.iot && s.iot.length > 0) {
276
+ this.log('IOT:');
277
+ s.iot.forEach(a => this.log(` ${a.rule} ${a.query}`), this);
278
+ this.log('');
279
+ }
344
280
 
345
- if (s.kinesis && s.kinesis.length > 0) {
346
- this.log('Kinesis:');
347
- s.kinesis.forEach(a => this.log(` ${a.startingPosition} ${a.stream}`), this);
348
- this.log('');
349
- }
281
+ if (s.alb && s.alb.length > 0) {
282
+ this.log('Application Load Balancer:');
283
+ s.alb.forEach(a => this.log(` ${a.alb} ${a.listener}`), this);
284
+ this.log('');
285
+ }
350
286
 
351
- if (s.cwevents && s.cwevents.length > 0) {
352
- this.log('Cloud Watch Events Rules:');
353
- s.cwevents.forEach(a => this.log(` ${a.rule}`), this);
354
- this.log('');
355
- }
287
+ if (p.events && p.events.length > 0) {
288
+ const sqs = p.events.filter(i => i.source == 'sqs');
289
+ if (sqs && sqs.length > 0) {
290
+ this.log('SQS URL:');
291
+ sqs.forEach(a => this.log(` ${a.url}`), this);
292
+ this.log('');
356
293
  }
294
+ }
295
+
296
+ if (s.alexa && s.alexa.length > 0) {
297
+ this.log('Alexa Skills:');
298
+ s.alexa.forEach(a => this.log(` ${a.skillId}`), this);
299
+ this.log('');
300
+ }
301
+
302
+ if (s.kinesis && s.kinesis.length > 0) {
303
+ this.log('Kinesis:');
304
+ s.kinesis.forEach(a => this.log(` ${a.startingPosition} ${a.stream}`), this);
305
+ this.log('');
306
+ }
307
+
308
+ if (s.cwevents && s.cwevents.length > 0) {
309
+ this.log('Cloud Watch Events Rules:');
310
+ s.cwevents.forEach(a => this.log(` ${a.rule}`), this);
311
+ this.log('');
312
+ }
313
+ }
357
314
 
358
315
  processFlow(flow) {
359
316
 
@@ -421,6 +378,105 @@ class AWSDeployer {
421
378
  };
422
379
  }
423
380
 
381
+ mapParams(params) {
382
+ let vpcConf;
383
+ if (params.vpcConfig && params.vpcConfig.sn && params.vpcConfig.sn.length > 0
384
+ && params.vpcConfig.sg && params.vpcConfig.sg.length > 0) {
385
+ let sn = params.vpcConfig.sn.split(",").map(s => s.trim());
386
+ let sg = params.vpcConfig.sg.split(",").map(s => s.trim());
387
+
388
+ vpcConf = JSON.stringify({
389
+ SubnetIds: sn,
390
+ SecurityGroupIds: sg
391
+ });
392
+ }
393
+
394
+ let tags;
395
+ if ( params.tags && params.tags.length > 0) {
396
+ tags = {};
397
+ params.tags.forEach(t => tags[t.key] = t.value)
398
+ tags = JSON.stringify(tags);
399
+ }
400
+
401
+ let env;
402
+ if ( params.environment && params.environment.length > 0) {
403
+ env = {
404
+ Variables: {}
405
+ };
406
+ params.environment.forEach(e => env.Variables[e.key] = e.value)
407
+ env = JSON.stringify(env);
408
+ }
409
+
410
+ let tracing;
411
+ if (params.xRay) {
412
+ tracing = '{"Mode":"Active"}';
413
+ }
414
+ // KL_TRIGGERS: '[{"api": {"apiId": "CHANGE_IT_TO_API_GATEWAY_ID", "parentId": "CHANGE_IT_TO_PARENT_ID", "stage": "test", "resource": "accounts"}}, {"event": {"expression": "cron(0 1 * * ? *)", "reference": "1am", "name": "CliBuildDemoEvent1am"}}, {"event": {"expression": "rate(1 minute)", "reference": "5min", "name": "CliBuildDemoEvent5min"}}]'
415
+
416
+ // "events":[{"source":"api","api":"qyhtzl0n39","stage":"test","parentId":"xfdg32","resource":"sample","authorizerId":"fdr3"}]
417
+ // "events":[{"source":"api","api":"qyhtzl0n39","stage":"test","parentId":"xfdg32","resource":"sample","authorizerId":"fdr3"},
418
+ //{"source":"dynamodb","stream":"arn:aws:dynamodb:ap-southeast-2:174842903734:table/contact_us/stream/2021-04-25T15:02:15.028","batchSize":"23","batchWindow":"3","startingPosition":"LATEST"},
419
+ //{"source":"sqs","stream":"https://sqs.ap-southeast-2.amazonaws.com/174842903734/devq2","batchSize":"32"},
420
+ //{"source":"kinesis","stream":"arn:aws:dynamodb:ap-southeast-2:174842903734:table/contact_us/stream/2021-04-25T15:02:15.028","batchSize":"34","batchWindow":"33","startingPosition":"LATEST"},
421
+ // {"source":"cwevents","rule":"arn:aws:events:ap-southeast-2:174842903734:rule/Test"},
422
+ //{"source":"sns","topic":"arn:aws:sns:ap-southeast-2:174842903734:t1"},
423
+ //{"source":"s3","bucket":"kumoexcelstore","eventType":"s3:ObjectCreated:*","prefix":"cc","suffix":"fd"}],"vpcConfig":{"sg":"","sn":""}}
424
+ ///Users/wojtek/Development/kumologica/sdk/kumologica/packages/sdk/src/app/lib/github/index.js:114
425
+ let triggers;
426
+ if (params.events) {
427
+ triggers = [];
428
+ params.events.forEach(e => {
429
+ switch(e.source) {
430
+ case "api": triggers.push({"api": {"apiId": e.api, "stage": e.stage, "parentId": e.parentId, "resource": e.resource, "authorizerId": e.authorizerId}});
431
+ break;
432
+ case "websocket": triggers.push({"websocket": {"apiId": e.apiId, "stage": e.stage}});
433
+ break;
434
+ case "sns": triggers.push({"sns": {"topicArn": e.topic}});
435
+ break;
436
+ case "sqs": triggers.push({"sqs": {"queueArn": e.queueArn, "batchSize": e.batchSize}});
437
+ break;
438
+ case "dynamodb": triggers.push({"dynamodb": {"streamArn": e.stream, "startingPosition": e.startingPosition, "batchSize": e.batchSize, "batchWindow": e.batchWindow}});
439
+ break;
440
+ case "kinesis": triggers.push({"kinesis": {"streamArn": e.stream, "startingPosition": e.startingPosition, "batchSize": e.batchSize, "batchWindow": e.batchWindow}});
441
+ break;
442
+ case "s3": triggers.push({"s3": {"bucket": e.bucket, "eventType": e.eventType, "prefix": e.prefix, "suffix": e.suffix}});
443
+ break;
444
+ case "cwevents": triggers.push({"event": {"expression": e.expression, "reference": e.reference, "name": e.name, "state": e.state}});
445
+ break;
446
+ }
447
+ })
448
+ triggers = JSON.stringify(triggers);
449
+ };
450
+
451
+ let args = {
452
+ "zip-file-name": params.zipFileName,
453
+ "bucket-name": params.bucketName,
454
+ "lambda-name": params.functionName,
455
+ "memory-size": params.memory,
456
+ "description": params.description,
457
+ "environment": env,
458
+ "tags": tags,
459
+ "timeout": params.timeout,
460
+ "triggers": triggers,
461
+ "vpc-config": vpcConf,
462
+ "role-arn": params.role,
463
+ "tracing-config": tracing,
464
+ "reserved-concurrency": params.reservedConcurrency
465
+ // not present
466
+ // "role-name": params.roleName,
467
+ // "layers": params.layers,
468
+ // "policy": params.policy,
469
+ // "strict-mode": params.strictMode,
470
+ // "log-retention-days": params.logRetentionDays,
471
+ // "file-system-configs": params.fileSystemConfigs,
472
+ // "dead-letter-config": params.deadLetterConfig,
473
+ // "runtime": params.runtime,
474
+ // "architectures": params.architectures,
475
+ // "kms-key-arn": params.kmsKeyArn,
476
+ }
477
+ return args;
478
+ }
479
+
424
480
  async generateScript(projectInfo, params, profile) {
425
481
  if (!profile) {
426
482
  throw new Error(
@@ -440,34 +496,23 @@ class AWSDeployer {
440
496
  this.log(` ${this.chalk('#F5DEB3', 'AWS region:')} ${this.chalk('whiteBright', AWS.config.region)}`, false);
441
497
 
442
498
  const settings = this.prepare(projectInfo.projectDir, projectInfo.projectFlowName, params.functionName, params.description);
443
- const nodes = await this.prepareNodes(this.loadJsonFile(path.join(projectInfo.projectDir, projectInfo.projectFlowName)));
444
499
 
445
500
  settings.deploymentBucketName = await this.prepareS3Bucket(
446
501
  settings.flowName
447
502
  );
448
503
 
449
- const lambdaCfTemplate = JSON.stringify(
450
- this.cfTemplate.createCfTemplate(
451
- params,
452
- settings,
453
- nodes,
454
- AWS.config.region
455
- ),
456
- null,
457
- 2
458
- );
459
-
460
- this.createFile(
461
- settings.deployDir,
462
- `cf-${projectInfo.projectFlowName}`,
463
- lambdaCfTemplate
464
- );
504
+ // NEW
505
+ const args = this.mapParams(params);
506
+ args.region = AWS.config.region;
507
+ args["bucket-name"] = settings.deploymentBucketName;
508
+ args["project-directory"] = projectInfo.projectDir;
509
+ args["flow-file-name"] = projectInfo.projectFlowName;
510
+ args["zip-file-name"] = settings.zipFileName;
511
+
512
+ console.log(`args: ${JSON.stringify(args)}`);
513
+ const scriptFileName = exp("cloudformation", "aws", args);
514
+ this.log(`Cloudformation script has been created: ${scriptFileName}`);
465
515
 
466
- const scriptFileName = path.join(
467
- settings.deployDir,
468
- `cf-${projectInfo.projectFlowName}`
469
- );
470
- this.log(`Cloud formation script created: ${scriptFileName}`);
471
516
  openFileOnEditor(scriptFileName);
472
517
  } catch (Error) {
473
518
  this.log(`${this.chalk('redBright', 'Script creation failed.')}`);
@@ -476,49 +521,16 @@ class AWSDeployer {
476
521
  }
477
522
  }
478
523
 
479
-
480
524
  /*
481
525
  * Builds lambda and zips all sources.
482
- */
483
- async buildLambda(settings) {
484
- this.log('Building lambda...');
485
-
486
- const npmCmd = `npm install --production --prefix "${settings.deployDir}"`;
487
- const response = await exec(npmCmd, { cwd: settings.deployDir });
488
-
489
- if (response) {
490
- this.log('', false);
491
- const lines = response.stdout.split('\n');
492
- for (var i = 0; i < lines.length; i++) {
493
- if (lines[i]) {
494
- this.log(lines[i], false);
495
- }
496
- }
497
- this.log('', false);
498
- }
499
-
500
- this.log('Excluding optional dependencies...');
501
-
502
- // for npm 8 on windows is creating symlink that causes recursions and zip to fail
503
- const splitdir = path.dirname(settings.deployDir).split(path.sep);
504
- const linkname = splitdir[splitdir.length-1];
505
- try {
506
- fs.unlinkSync(path.join(settings.deployDir, 'node_modules', linkname));
507
- } catch (e) {
508
- // ignore ENOENT
509
- if (e.code !== "ENOENT") {
510
- throw e;
511
- }
512
- }
513
-
514
- // Remove aws-sdk library as it is provided by aws nodejs runtime.
515
- // Reducing the resulting lambda zip file by more than 70%
516
- rimraf.sync(path.join(settings.deployDir, 'node_modules', 'aws-sdk'));
517
-
518
- this.log('Zipping lambda...');
519
- const zip = new AdmZip();
520
- zip.addLocalFolder(settings.deployDir);
521
- zip.writeZip(path.join(settings.deployDir, settings.zipFileName));
526
+ */
527
+ async buildLambda(settings, projectInfo) {
528
+ let args = {};
529
+ args["project-directory"] = projectInfo.projectDir;
530
+ args["flow-file-name"] = projectInfo.projectFlowName;
531
+ args["zip-file-name"] = settings.zipFileName;
532
+
533
+ await build("aws", args);
522
534
  }
523
535
 
524
536
  /*
@@ -526,7 +538,7 @@ class AWSDeployer {
526
538
  * and executes create or update stack.
527
539
  * Returns stack id
528
540
  */
529
- async executeStack(settings, params, deploymentStartTimestamp, nodes) {
541
+ async executeStack(settings, params, deploymentStartTimestamp, nodes, projectInfo) {
530
542
  this.log(`Uploading zip file to bucket...`);
531
543
  var body = fs.createReadStream(
532
544
  path.join(settings.deployDir, settings.zipFileName)
@@ -535,19 +547,21 @@ class AWSDeployer {
535
547
  await this.s3.upload({
536
548
  Bucket: settings.deploymentBucketName,
537
549
  Key: settings.zipFileName,
538
- Body: body,
550
+ Body: body
539
551
  });
540
552
 
541
- params = await this.prepareParams(params);
542
- nodes = await this.prepareNodes(nodes);
543
-
544
- const lambdaCfTemplate = this.cfTemplate.createCfTemplate(
545
- params,
546
- settings,
547
- nodes,
548
- AWS.config.region
549
- );
550
-
553
+ const args = this.mapParams(params);
554
+ args.region = AWS.config.region;
555
+ args["bucket-name"] = settings.deploymentBucketName;
556
+ args["project-directory"] = projectInfo.projectDir;
557
+ args["flow-file-name"] = projectInfo.projectFlowName;
558
+ args["zip-file-name"] = settings.zipFileName;
559
+ args.skipPrepare = "true"; // no need for cloudformation prepare
560
+
561
+ const scriptFileName = exp("cloudformation", "aws", args);
562
+
563
+ const lambdaCfTemplate = this.loadJsonFile(scriptFileName);
564
+
551
565
  const stackParams = {
552
566
  StackName: settings.stackName,
553
567
  Capabilities: ['CAPABILITY_NAMED_IAM'],
@@ -563,7 +577,9 @@ class AWSDeployer {
563
577
  await this.cf
564
578
  .waitFor('stackCreateComplete', { StackName: stackParams.StackName })
565
579
  .promise();
580
+
566
581
  } catch (error) {
582
+
567
583
  if (error.code == 'AlreadyExistsException') {
568
584
  stack = await this.cf.updateStack(stackParams).promise();
569
585
  this.log('Waiting for stack update complete...');
@@ -575,6 +591,7 @@ class AWSDeployer {
575
591
  })
576
592
  .promise();
577
593
  } catch (error) {
594
+
578
595
  await this.describeStackEvents(
579
596
  stackParams.StackName,
580
597
  deploymentStartTimestamp
@@ -593,137 +610,6 @@ class AWSDeployer {
593
610
  return this.describeStack(stackParams.StackName);
594
611
  }
595
612
 
596
- /**
597
- * Function links additional details for parameters that are not needed
598
- * in ui but are required by cloud formation
599
- *
600
- * @param {*} params
601
- */
602
- async prepareParams(params) {
603
- for (var i = 0; i < params.events.length; i++) {
604
- if (params.events[i].source === 'sqs') {
605
- if (!params.events[i].stream) {
606
- throw Error(`Missing Trigger Parameter: SQS Queue Url.`);
607
- }
608
- params.events[i].url = params.events[i].stream;
609
- params.events[i].stream = await this.sqs.getQueueArn(
610
- params.events[i].stream
611
- );
612
- } else if (params.events[i].source === 'codecommit') {
613
- if (!params.events[i].repository) {
614
- throw Error(`Missing Trigger Parameter: CodeCommit Repository.`);
615
- }
616
- const repository = await this.codecommit.getRepository(
617
- params.events[i].repository
618
- );
619
- params.events[i].repositoryArn = repository.Arn;
620
- params.events[i].account = repository.accountId;
621
- }
622
- }
623
- return params;
624
- }
625
-
626
- async prepareNodes(nodes) {
627
- for (var i = 0; i < nodes.length; i++) {
628
- if (nodes[i].type === 'SQS') {
629
- if (!nodes[i].QueueUrl) {
630
- throw Error(`Missing Node Parameter: SQS Queue Url.`);
631
- }
632
- nodes[i].QueueArn = await this.sqs.getQueueArn(nodes[i].QueueUrl);
633
- }
634
- }
635
- return nodes;
636
- }
637
-
638
- async runTriggers(params, settings, lambdaArn) {
639
- let response = {};
640
-
641
- if (params.events) {
642
- this.log('Adding flow triggers ...');
643
-
644
- for (var i = 0; i < params.events.length; i++) {
645
- if (params.events[i].source === 's3') {
646
- await this.addS3Event(
647
- params.events[i],
648
- settings.functionName,
649
- lambdaArn
650
- );
651
- } else if (params.events[i].source === 'sns') {
652
- await this.addSNSEvent(
653
- params.events[i],
654
- settings.functionName,
655
- lambdaArn
656
- );
657
- } else if (params.events[i].source === 'cwevents') {
658
- await this.addCWEvent(
659
- params.events[i],
660
- settings.functionName,
661
- lambdaArn
662
- );
663
- } else if (params.events[i].source === 'cwlogs') {
664
- await this.addCWLogsEvent(
665
- params.events[i],
666
- settings.functionName,
667
- lambdaArn
668
- );
669
- } else if (params.events[i].source === 'cognito') {
670
- await this.addCognitoEvent(
671
- params.events[i],
672
- settings.functionName,
673
- lambdaArn
674
- );
675
- } else if (params.events[i].source === 'alexa') {
676
- await this.addAlexaEvent(
677
- params.events[i],
678
- settings.functionName,
679
- lambdaArn
680
- );
681
- } else if (params.events[i].source === 'codecommit') {
682
- await this.addCodeCommitEvent(
683
- params.events[i],
684
- settings.functionName,
685
- lambdaArn
686
- );
687
- } else if (params.events[i].source === 'iot') {
688
- var rule = await this.iot.addTrigger(
689
- params.events[i],
690
- settings.functionName,
691
- lambdaArn
692
- );
693
- if (rule) {
694
- await this.addLambdaPermission(
695
- 'iot.amazonaws.com',
696
- settings.functionName,
697
- null,
698
- rule.ruleArn
699
- );
700
- }
701
- } else if (params.events[i].source === 'api') {
702
- const newApiId = await this.apiGateway.addTrigger(
703
- params.events[i],
704
- settings.functionName,
705
- lambdaArn
706
- );
707
-
708
- response.apiId = newApiId? newApiId: params.events[i].api;
709
- response.stage = params.events[i].stage;
710
-
711
- //arn:aws:lambda:ap-southeast-2:640233474616:function:kumologica-aws-inbound-test-flow-lambda
712
- const lambdaArnParts = lambdaArn.split(':');
713
- const apiArn = `arn:aws:execute-api:${lambdaArnParts[3]}:${lambdaArnParts[4]}:${params.events[i].api}/*/*/{proxy+}`;
714
- await this.addLambdaPermission(
715
- 'apigateway.amazonaws.com',
716
- settings.functionName,
717
- null,
718
- apiArn
719
- );
720
- }
721
- }
722
- this.log(`${this.chalk('greenBright', 'All triggers added successfully:')}`);
723
- }
724
- return response;
725
- }
726
-
727
613
  async describeStack(stackName) {
728
614
  return this.cf.describeStacks({ StackName: stackName }).promise();
729
615
  }
@@ -733,10 +619,17 @@ class AWSDeployer {
733
619
  */
734
620
  async describeStackEvents(stackName, deploymentStartTimestamp) {
735
621
  this.log('Calling describe stack events...');
622
+ let events;
736
623
 
737
- const events = await this.cf
624
+ try {
625
+ events = await this.cf
738
626
  .describeStackEvents({ StackName: stackName })
739
627
  .promise();
628
+ } catch (error) {
629
+ //this.log(`de: ${error}`);
630
+ // may throw stack does not exists for the first ever call
631
+ }
632
+
740
633
  if (!events || events == undefined) {
741
634
  return;
742
635
  }
@@ -780,157 +673,6 @@ class AWSDeployer {
780
673
  fs.outputFileSync(path.join(baseDir, fileName), content, 'utf-8');
781
674
  }
782
675
 
783
- async addLambdaPermission(
784
- principal,
785
- lambdaName,
786
- sourceAccount,
787
- sourceArn,
788
- eventSourceToken
789
- ) {
790
- var request = {
791
- Action: 'lambda:InvokeFunction',
792
- FunctionName: lambdaName,
793
- Principal: principal,
794
- SourceAccount: sourceAccount,
795
- SourceArn: sourceArn,
796
- EventSourceToken: eventSourceToken,
797
- StatementId: `${lambdaName}-${Date.now()}`,
798
- };
799
-
800
- // wait for response, the subsequent calls require permissions to be present.
801
- await this.lambdaApi.addPermission(request).promise();
802
- }
803
-
804
- async addS3Event(event, lambdaName, lambdaArn) {
805
- if (!event.bucket) {
806
- throw Error(`Missing Trigger Parameter: Bucket Name.`);
807
- }
808
-
809
- this.log(
810
- `Setting lambda execution permissions for bucket ${event.bucket} ...`
811
- );
812
- await this.addLambdaPermission(
813
- 's3.amazonaws.com',
814
- lambdaName,
815
- event.sourceAccount,
816
- `arn:aws:s3:::${event.bucket}`
817
- );
818
-
819
- await this.s3.bucketNotificationConfiguration(
820
- lambdaArn,
821
- event.bucket,
822
- event.eventType,
823
- event.prefix,
824
- event.suffix
825
- );
826
- }
827
-
828
- async addAlexaEvent(event, lambdaName, lambdaArn) {
829
- this.log(`Setting configuration for alexa skill id ${event.skillID} ...`);
830
- await this.addLambdaPermission(
831
- 'alexa-appkit.amazon.com',
832
- lambdaName,
833
- null,
834
- null,
835
- event.skillID
836
- );
837
- }
838
-
839
- async addCodeCommitEvent(event, lambdaName, lambdaArn) {
840
- this.log(
841
- `Setting configuration for code commit repository ${event.repository} ...`
842
- );
843
-
844
- await this.addLambdaPermission(
845
- 'codecommit.amazon.com',
846
- lambdaName,
847
- event.account,
848
- event.repositoryArn
849
- );
850
-
851
- this.log('Adding permissions for lambda to call codecommit.');
852
- await this.codecommit.addLambdaPermissions(event, lambdaArn);
853
- }
854
-
855
- async addSNSEvent(event, lambdaName, lambdaArn) {
856
- await this.addLambdaPermission(
857
- 'sns.amazonaws.com',
858
- lambdaName,
859
- null,
860
- event.topic
861
- );
862
-
863
- this.log(`Setting notification configuration for topic ${event.topic} ...`);
864
- await this.sns.subscribe(event.topic, lambdaArn);
865
- }
866
-
867
- async addCWEvent(event, lambdaName, lambdaArn) {
868
- await this.addLambdaPermission(
869
- 'events.amazonaws.com',
870
- lambdaName,
871
- null,
872
- event.rule
873
- );
874
-
875
- const ruleName = event.rule.split('rule/').pop();
876
-
877
- this.log(
878
- `Setting notification configuration for cloudwatch event ${ruleName} ...`
879
- );
880
-
881
- var params = {
882
- Rule: ruleName,
883
- Targets: [
884
- {
885
- Arn: lambdaArn,
886
- Id: `${lambdaName}`,
887
- },
888
- ],
889
- };
890
-
891
- await this.cwevents.putTargets(params).promise();
892
- }
893
-
894
- /**
895
- * https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CloudWatchLogs.html#putSubscriptionFilter-property
896
- *
897
- * @param {*} event
898
- * @param {*} lambdaName
899
- * @param {*} lambdaArn
900
- */
901
- async addCWLogsEvent(event, lambdaName, lambdaArn) {
902
- //await this.addLambdaPermission("events.amazonaws.com",
903
- // lambdaName, null, event.rule);
904
-
905
- /*this.log(`Setting notification configuration for cloudwatch event ${ruleName} ...`);
906
- var params = {
907
- destinationArn: 'STRING_VALUE', // required
908
- filterName: 'STRING_VALUE', // required
909
- filterPattern: 'STRING_VALUE', // required
910
- logGroupName: 'STRING_VALUE', // required
911
- distribution: Random | ByLogStream,
912
- roleArn: 'STRING_VALUE'
913
- };*/
914
- await this.cwLogs.putSubscriptionFilter(params).promise();
915
- }
916
-
917
- async addCognitoEvent(event, lambdaName, lambdaArn) {
918
- await this.addLambdaPermission(
919
- 'cognito-idp.amazonaws.com',
920
- lambdaName,
921
- null,
922
- event.identityPool
923
- );
924
-
925
- const ruleName = event.rule.split('rule/').pop();
926
-
927
- // TODO: add cognito implementation
928
- //this.log(`Setting notification configuration for cloudwatch event ${ruleName} ...`);
929
- var params = {};
930
-
931
- await this.cognito.updateUserPool(params).promise();
932
- }
933
-
934
676
  async listServices(type, profile) {
935
677
  if (!profile) {
936
678
  throw new Error(
@@ -969,12 +711,6 @@ class AWSDeployer {
969
711
  case 'iot':
970
712
  res = await this.iot.listTopicRules();
971
713
  break;
972
- case 'alexa':
973
- res = await this.alexa.listSkills();
974
- break;
975
- case 'codecommit':
976
- res = await this.codecommit.listRepositories();
977
- break;
978
714
  case 'iamroles':
979
715
  res = await this.iam.listRoles();
980
716
  break;