@kumologica/sdk 3.0.27-beta3 → 3.0.27-beta5
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.
|
@@ -309,27 +309,12 @@ class TestSuiteController {
|
|
|
309
309
|
return flowFileJson.filter((node) => node.type === 'TestCase' || node.type === 'HTTPTestCase');
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
if (!testCases || (testCases && testCases.length === 0)) {
|
|
319
|
-
logError(`No testcases found on flow file: ${flowFileAbsPath}`);
|
|
320
|
-
process.exit(1);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// If defined, filter the testcase that you want to test
|
|
324
|
-
if (testcase) {
|
|
325
|
-
testCases = testCases.filter(tc => tc.name.toLowerCase() === testcase.toLowerCase());
|
|
326
|
-
if (testCases && testCases.length === 0){
|
|
327
|
-
logError(`TestCase: "${testcase}" cannot be found`);
|
|
328
|
-
process.exit(1);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
|
|
312
|
+
/**
|
|
313
|
+
*
|
|
314
|
+
* @param {*} testcasesSelected - array of {id: nodeId, name: nodeName}
|
|
315
|
+
* @returns
|
|
316
|
+
*/
|
|
317
|
+
async runTestSuite(testcasesSelected) {
|
|
333
318
|
// === Starting the testSuite ===
|
|
334
319
|
this._appServer.events.emit('runtime-event', {
|
|
335
320
|
id: runtimeEvents.TEST_TESTSUITE_START,
|
|
@@ -337,7 +322,7 @@ class TestSuiteController {
|
|
|
337
322
|
});
|
|
338
323
|
|
|
339
324
|
// === Iterate over all the testCases ===
|
|
340
|
-
await this.runTestCases(
|
|
325
|
+
await this.runTestCases(testcasesSelected);
|
|
341
326
|
await this.waitForResults();
|
|
342
327
|
const errorsNum = this._testReporter.getFailedTestCases().length;
|
|
343
328
|
|
package/cli/commands/test.js
CHANGED
|
@@ -3,6 +3,7 @@ const { Select } = require('enquirer');
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const { logError } = require('./utils');
|
|
5
5
|
const path = require('path');
|
|
6
|
+
const wcmatch = require('wildcard-match');
|
|
6
7
|
|
|
7
8
|
const { AppServer } = require('@kumologica/runtime');
|
|
8
9
|
const { TestSuiteController } = require('./test-utils/TestSuiteController');
|
|
@@ -11,7 +12,7 @@ const { codegen } = require('@kumologica/builder');
|
|
|
11
12
|
const log = console.log;
|
|
12
13
|
const APP_SERVER_PORT = 1990;
|
|
13
14
|
|
|
14
|
-
async function runTest(flowFilePath,
|
|
15
|
+
async function runTest(flowFilePath, testcaseSelected, iterative) {
|
|
15
16
|
log(`\n> Starting runtime on port ${APP_SERVER_PORT}...`);
|
|
16
17
|
|
|
17
18
|
let appServer = new AppServer({
|
|
@@ -28,24 +29,31 @@ async function runTest(flowFilePath, testcase, iterative) {
|
|
|
28
29
|
await appServer.start();
|
|
29
30
|
log(`> FlowFile to be tested: ${chalk.bold(path.resolve(flowFilePath))} \n`);
|
|
30
31
|
let testSuiteController = new TestSuiteController(appServer);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
//
|
|
36
|
-
|
|
32
|
+
|
|
33
|
+
// If testcase is null, default to universal wildcard
|
|
34
|
+
testcaseSelected = testcaseSelected || "**";
|
|
35
|
+
|
|
36
|
+
// Find out all testcases available on the flow
|
|
37
|
+
let testcasesAvailable = testSuiteController.findTestCasesFromFlow(flowFilePath);
|
|
38
|
+
if (!testcasesAvailable || (testcasesAvailable && testcasesAvailable.length === 0)) {
|
|
39
|
+
logError(`No testcases found on flow file: ${flowFileAbsPath}`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
};
|
|
42
|
+
let testcaseAvailableNames = testcasesAvailable.map(tc=>tc.name);
|
|
43
|
+
|
|
44
|
+
// Capture the testcase from user on iterative mode
|
|
37
45
|
if (iterative) {
|
|
38
46
|
const prompt = new Select({
|
|
39
47
|
name: 'testcase',
|
|
40
48
|
message: 'What testcase do you want to run?',
|
|
41
|
-
choices:
|
|
49
|
+
choices: [...testcaseAvailableNames, 'Run all TestCases...']
|
|
42
50
|
});
|
|
43
51
|
await prompt.run()
|
|
44
52
|
.then(tc => {
|
|
45
53
|
if (tc === 'Run all TestCases...'){
|
|
46
|
-
|
|
54
|
+
testcaseSelected = "**";
|
|
47
55
|
}else {
|
|
48
|
-
|
|
56
|
+
testcaseSelected = tc;
|
|
49
57
|
}
|
|
50
58
|
})
|
|
51
59
|
.catch(err => {
|
|
@@ -54,15 +62,31 @@ async function runTest(flowFilePath, testcase, iterative) {
|
|
|
54
62
|
});
|
|
55
63
|
}
|
|
56
64
|
|
|
65
|
+
// Filter all testcases to be part of the test suite
|
|
66
|
+
const isMatch = wcmatch(testcaseSelected);
|
|
67
|
+
let testCasesSelected = [];
|
|
68
|
+
testcasesAvailable.forEach(async tc => {
|
|
69
|
+
if (isMatch(tc.name)){
|
|
70
|
+
testCasesSelected.push({ name: tc.name, id: tc.id });
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Execute the testcasesIds if any, otherwise throw an error
|
|
75
|
+
if (testCasesSelected.length === 0){
|
|
76
|
+
logError(`No matched testcases found`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
} else {
|
|
79
|
+
const errors = await testSuiteController.runTestSuite(testCasesSelected);
|
|
80
|
+
process.exit(errors > 0);
|
|
81
|
+
}
|
|
57
82
|
|
|
58
|
-
const errors = await testSuiteController.runTestSuite(testcase); // no need to wait for response
|
|
59
|
-
process.exit(errors > 0);
|
|
60
83
|
} catch (err) {
|
|
61
84
|
log(
|
|
62
85
|
chalk.red(
|
|
63
86
|
`Unexpected error occurred while starting server due to <${err.message}>`
|
|
64
87
|
)
|
|
65
88
|
);
|
|
89
|
+
console.log(err);
|
|
66
90
|
process.exit(1);
|
|
67
91
|
}
|
|
68
92
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"productName": "Kumologica Designer",
|
|
4
4
|
"copyright": "Copyright 2020 Kumologica Pty Ltd, All Rights Reserved.",
|
|
5
5
|
"author": "Kumologica Pty Ltd <contact@kumologica.com>",
|
|
6
|
-
"version": "3.0.27-
|
|
6
|
+
"version": "3.0.27-beta5",
|
|
7
7
|
"description": "Kumologica Designer, harnessing Serverless for your cloud integration needs",
|
|
8
8
|
"main": "src/app/main.js",
|
|
9
9
|
"files": [
|
|
@@ -65,9 +65,9 @@
|
|
|
65
65
|
"license": "Proprietary",
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@electron/remote": "^2.0.8",
|
|
68
|
-
"@kumologica/builder": "3.0.27-
|
|
69
|
-
"@kumologica/devkit": "3.0.27-
|
|
70
|
-
"@kumologica/runtime": "3.0.27-
|
|
68
|
+
"@kumologica/builder": "3.0.27-beta5",
|
|
69
|
+
"@kumologica/devkit": "3.0.27-beta5",
|
|
70
|
+
"@kumologica/runtime": "3.0.27-beta5",
|
|
71
71
|
"adm-zip": "0.4.13",
|
|
72
72
|
"ajv": "8.10.0",
|
|
73
73
|
"aws-sdk": "2.513.0",
|
|
@@ -117,6 +117,7 @@
|
|
|
117
117
|
"tcp-port-used": "1.0.2",
|
|
118
118
|
"util": "0.12.1",
|
|
119
119
|
"when": "3.7.8",
|
|
120
|
+
"wildcard-match": "^5.1.2",
|
|
120
121
|
"ws": "7.1.1",
|
|
121
122
|
"xterm": "4.1.0",
|
|
122
123
|
"xterm-addon-fit": "0.2.1",
|
package/src/app/lib/aws/cf.js
CHANGED
|
@@ -196,8 +196,7 @@ class AWSCFTemplate {
|
|
|
196
196
|
|
|
197
197
|
} else if (nodes[i].type === 'Lambda') {
|
|
198
198
|
//arn:aws:lambda:ap-southeast-2:174450237637:function:kumologica-deployments-flow-lambda
|
|
199
|
-
|
|
200
|
-
let lambdaArn = this.handleValue(lambda, nodes[i].LambdaArn);
|
|
199
|
+
let lambdaArn = this.handleValue(lambda, nodes[i].LambdaArn);
|
|
201
200
|
let lambdaParts = lambdaArn.split(":");
|
|
202
201
|
let lambdaName = '*';
|
|
203
202
|
|
|
@@ -220,10 +219,23 @@ class AWSCFTemplate {
|
|
|
220
219
|
this.handlePolicy(lambdaRole, `KLSES`, `ses:${nodes[i].operation}`, '*');
|
|
221
220
|
|
|
222
221
|
} else if (nodes[i].type === 'SSM') {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
222
|
+
let key;
|
|
223
|
+
if (nodes[i].operation === "GetParametersByPath") {
|
|
224
|
+
key = this.handleValue(lambda, nodes[i].Path);
|
|
225
|
+
} else {
|
|
226
|
+
key = this.handleValue(lambda, nodes[i].Key);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (key.startsWith('/')) {
|
|
230
|
+
key = key.substring(1);
|
|
231
|
+
}
|
|
232
|
+
const ssmArn = {'Fn::Sub': 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/' + key};
|
|
233
|
+
this.handlePolicy(lambdaRole, `KLSSM${new Date().valueOf()}`, `ssm:${nodes[i].operation}`, ssmArn);
|
|
226
234
|
|
|
235
|
+
if (nodes[i].operation === "GetParameter" && nodes[i].Key.startsWith("/aws/reference/secretsmanager/")) {
|
|
236
|
+
const smArn = {'Fn::Sub': 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:*'};
|
|
237
|
+
this.handlePolicy(lambdaRole, `KLSM${new Date().valueOf()}`, "secretsmanager:GetSecretValue", smArn);
|
|
238
|
+
}
|
|
227
239
|
} else if (nodes[i].type === 'S3') {
|
|
228
240
|
let bucketName = this.handleValue(lambda, nodes[i].Bucket);
|
|
229
241
|
const bucketArn = `arn:aws:s3:::${bucketName}/*`;
|
|
@@ -260,6 +272,7 @@ class AWSCFTemplate {
|
|
|
260
272
|
if (!params.role) {
|
|
261
273
|
template.Resources['LambdaRole'] = lambdaRole;
|
|
262
274
|
}
|
|
275
|
+
|
|
263
276
|
return template;
|
|
264
277
|
}
|
|
265
278
|
|
|
@@ -279,7 +292,6 @@ class AWSCFTemplate {
|
|
|
279
292
|
// to determine the value
|
|
280
293
|
handleValue(lambda, key) {
|
|
281
294
|
let value;
|
|
282
|
-
console.log(`key= ${key}`);
|
|
283
295
|
|
|
284
296
|
if (!key) {
|
|
285
297
|
return key;
|
|
@@ -287,7 +299,6 @@ class AWSCFTemplate {
|
|
|
287
299
|
let keyValue = key.replace(/\s+/g, '');
|
|
288
300
|
|
|
289
301
|
if (keyValue.startsWith("env.")) {
|
|
290
|
-
console.log(`keyValue= ${keyValue}`);
|
|
291
302
|
keyValue = keyValue.replace("env.", "");
|
|
292
303
|
value = lambda.Properties.Environment.Variables[keyValue];
|
|
293
304
|
if (!value) {
|
|
@@ -316,7 +327,6 @@ class AWSCFTemplate {
|
|
|
316
327
|
// - updates existing resource policy (if found) with new permissions
|
|
317
328
|
//
|
|
318
329
|
handlePolicy(lambdaRole, policyName, operation, id) {
|
|
319
|
-
console.log(`policyName: ${policyName}`);
|
|
320
330
|
policyName = policyName.replace('/', '-').replace('*', 'all');
|
|
321
331
|
let policy = jp.query(lambdaRole.Properties, `Policies[?(@.PolicyName=='${policyName}')]`);
|
|
322
332
|
|
|
@@ -373,7 +383,7 @@ class AWSCFTemplate {
|
|
|
373
383
|
throw new Error(`Missing Trigger Parameter: SQS url`);
|
|
374
384
|
}
|
|
375
385
|
return {
|
|
376
|
-
PolicyName: '
|
|
386
|
+
PolicyName: 'KLSQSPolicy'+process.hrtime(),
|
|
377
387
|
PolicyDocument: {
|
|
378
388
|
Version: '2012-10-17',
|
|
379
389
|
Statement: [
|
package/src/app/lib/aws/index.js
CHANGED
|
@@ -241,7 +241,7 @@ class AWSDeployer {
|
|
|
241
241
|
);
|
|
242
242
|
|
|
243
243
|
flowListeners = this.processFlow(nodes);
|
|
244
|
-
|
|
244
|
+
|
|
245
245
|
const stackDetails = await this.executeStack(
|
|
246
246
|
settings,
|
|
247
247
|
params,
|
|
@@ -278,7 +278,7 @@ class AWSDeployer {
|
|
|
278
278
|
response = await this.runTriggers(params, settings, lambdaArn);
|
|
279
279
|
if (response) {
|
|
280
280
|
const url = `https://${response.apiId}.execute-api.${AWS.config.region}.amazonaws.com/${response.stage}`;
|
|
281
|
-
this.printSignature(url, flowListeners);
|
|
281
|
+
this.printSignature(url, flowListeners, params);
|
|
282
282
|
}
|
|
283
283
|
} catch (error) {
|
|
284
284
|
this.log(`${this.chalk('redBright', 'Trigger creation failed.')}`);
|
|
@@ -288,8 +288,9 @@ class AWSDeployer {
|
|
|
288
288
|
return response;
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
printSignature(url, s) {
|
|
291
|
+
printSignature(url, s, p) {
|
|
292
292
|
this.log(' ')
|
|
293
|
+
|
|
293
294
|
if (s.api && s.api.length > 0) {
|
|
294
295
|
this.log('API Gateway:');
|
|
295
296
|
s.api.forEach(a => this.log(` ${(' ' + a.verb.toUpperCase()).slice(-6)} ${url}${a.url}`), this);
|
|
@@ -326,10 +327,13 @@ class AWSDeployer {
|
|
|
326
327
|
this.log('');
|
|
327
328
|
}
|
|
328
329
|
|
|
329
|
-
if (
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
+
}
|
|
333
337
|
}
|
|
334
338
|
|
|
335
339
|
if (s.alexa && s.alexa.length > 0) {
|
|
@@ -588,6 +592,7 @@ class AWSDeployer {
|
|
|
588
592
|
if (!params.events[i].stream) {
|
|
589
593
|
throw Error(`Missing Trigger Parameter: SQS Queue Url.`);
|
|
590
594
|
}
|
|
595
|
+
params.events[i].url = params.events[i].stream;
|
|
591
596
|
params.events[i].stream = await this.sqs.getQueueArn(
|
|
592
597
|
params.events[i].stream
|
|
593
598
|
);
|
package/src/app/main.js
CHANGED
|
@@ -314,7 +314,7 @@ ipcMain.on('restart-requested', async (event, msg) => {
|
|
|
314
314
|
// reload the nodes available in the palette
|
|
315
315
|
try {
|
|
316
316
|
await requestAsync({
|
|
317
|
-
uri: `http://127.0.0.1:${
|
|
317
|
+
uri: `http://127.0.0.1:${RUNTIME_PORT}/nodes-reload`,
|
|
318
318
|
method: 'POST',
|
|
319
319
|
json: true,
|
|
320
320
|
});
|
|
@@ -330,7 +330,7 @@ ipcMain.on('modal-node-library:restart-requested', async (event, msg) => {
|
|
|
330
330
|
// reload the nodes available in the palette
|
|
331
331
|
try {
|
|
332
332
|
await requestAsync({
|
|
333
|
-
uri: `http://127.0.0.1:${
|
|
333
|
+
uri: `http://127.0.0.1:${RUNTIME_PORT}/nodes-reload`,
|
|
334
334
|
method: 'POST',
|
|
335
335
|
json: true,
|
|
336
336
|
});
|