@kumologica/sdk 3.6.1 → 3.6.2-beta1
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.
- package/cli/commands/create-commands/OpenApiBuilder.js +608 -0
- package/cli/commands/create-commands/index.js +3 -1
- package/cli/commands/create-commands/openapi.js +61 -38
- package/cli/commands/create-commands/signup.js +425 -0
- package/cli/commands/create.js +7 -1
- package/cli/commands/run.js +14 -13
- package/package.json +6 -5
- package/src/app/main-process/modal-home.js +1 -0
- package/src/app/main-process/modal-welcome.js +2 -0
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
class OpenApiBuilder {
|
|
2
|
+
constructor(api) {
|
|
3
|
+
this.api = api;
|
|
4
|
+
this.shiftx = 100;
|
|
5
|
+
this.shifty = 80;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
S4() {
|
|
9
|
+
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
getId() {
|
|
13
|
+
return this.S4() + "." + this.S4();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getEventListenerEnd(id, tabId, x, y, status, response, notes, headers) {
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
"id": id,
|
|
20
|
+
"type": "EventListener-End",
|
|
21
|
+
"z": tabId,
|
|
22
|
+
"name": status,
|
|
23
|
+
"statusCode": status === "default" ? "200" : status,
|
|
24
|
+
"responseType": "http",
|
|
25
|
+
"headers": headers,
|
|
26
|
+
"payload": response || "msg.payload",
|
|
27
|
+
"eventPayload": "",
|
|
28
|
+
"eventType": "success",
|
|
29
|
+
"x": x,
|
|
30
|
+
"y": y,
|
|
31
|
+
"wires": [],
|
|
32
|
+
"caname": "eventlistenerend",
|
|
33
|
+
"category": "general",
|
|
34
|
+
"info": notes || ""
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getLog(id, nextId, tabId, x, y, logName, logMessage, logType) {
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
"id": id,
|
|
42
|
+
"type": "Logger",
|
|
43
|
+
"z": tabId,
|
|
44
|
+
"name": logName || "Log",
|
|
45
|
+
"level": logType || "INFO",
|
|
46
|
+
"message": logMessage,
|
|
47
|
+
"x": x,
|
|
48
|
+
"y": y,
|
|
49
|
+
"wires": [[nextId]],
|
|
50
|
+
"caname": "logger",
|
|
51
|
+
"category": "logging"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
getSchema(id, nextId, tabId, x, y, schema, notes) {
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
"id": id,
|
|
59
|
+
"type": "json-schema-validator",
|
|
60
|
+
"z": tabId,
|
|
61
|
+
"name": "Validate Request",
|
|
62
|
+
"Property": "msg.payload",
|
|
63
|
+
"query": schema,
|
|
64
|
+
"x": x,
|
|
65
|
+
"y": y,
|
|
66
|
+
"wires": [
|
|
67
|
+
[nextId]
|
|
68
|
+
],
|
|
69
|
+
"caname": "JSONSchemaVal",
|
|
70
|
+
"category": "validation",
|
|
71
|
+
"info": notes || ""
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
getError(id, nextId, tabId, x, y, errorCode, notes) {
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
"id": id,
|
|
79
|
+
"type": "catch",
|
|
80
|
+
"z": tabId,
|
|
81
|
+
"name": "Catch " + (errorCode || "Error"),
|
|
82
|
+
"scope": [],
|
|
83
|
+
"uncaught": false,
|
|
84
|
+
"x": x,
|
|
85
|
+
"y": y,
|
|
86
|
+
"wires": [[nextId]],
|
|
87
|
+
"caname": "catch",
|
|
88
|
+
"category": "exception",
|
|
89
|
+
"info": notes || ""
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
getLogicCall(id, nextId, tabId, x, y, logicInId) {
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
"id": id,
|
|
98
|
+
"type": "Subflow",
|
|
99
|
+
"z": tabId,
|
|
100
|
+
"untilproptype": "num",
|
|
101
|
+
"proptype": "msg",
|
|
102
|
+
"name": "Business Logic Call",
|
|
103
|
+
"prop": "loop",
|
|
104
|
+
"untilprop": 0,
|
|
105
|
+
"until": "gt",
|
|
106
|
+
"loop": "none",
|
|
107
|
+
"scope": "global",
|
|
108
|
+
"perf": false,
|
|
109
|
+
"seq": false,
|
|
110
|
+
"selectedTargetNode": logicInId,
|
|
111
|
+
"targetNode": logicInId,
|
|
112
|
+
"initialLoad": "false",
|
|
113
|
+
"x": x,
|
|
114
|
+
"y": y,
|
|
115
|
+
"wires": [[nextId]],
|
|
116
|
+
"caname": "subflow",
|
|
117
|
+
"category": "routing"
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
getLogicSubflow(id, tabId, y) {
|
|
122
|
+
const subflowOutId = this.getId();
|
|
123
|
+
const logId = this.getId();
|
|
124
|
+
return [
|
|
125
|
+
{
|
|
126
|
+
"id": id,
|
|
127
|
+
"type": "Subflow_in",
|
|
128
|
+
"z": tabId,
|
|
129
|
+
"name": "Business Logic In",
|
|
130
|
+
"priority": "50",
|
|
131
|
+
"links": [],
|
|
132
|
+
"scope": "global",
|
|
133
|
+
"x": 50,
|
|
134
|
+
"y": y,
|
|
135
|
+
"wires": [[ logId]],
|
|
136
|
+
"caname": "subflow",
|
|
137
|
+
"category": "routing"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"id": subflowOutId,
|
|
141
|
+
"type": "Subflow_out",
|
|
142
|
+
"z": tabId,
|
|
143
|
+
"name": "Business Logic Out",
|
|
144
|
+
"links":[],
|
|
145
|
+
"x": 50 + 2 * this.shiftx,
|
|
146
|
+
"y": y,
|
|
147
|
+
"wires":[],
|
|
148
|
+
"caname": "subflow",
|
|
149
|
+
"category": "routing"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"id": logId,
|
|
153
|
+
"type":"Logger",
|
|
154
|
+
"z": tabId,
|
|
155
|
+
"name": "Replace with logic",
|
|
156
|
+
"level": "INFO",
|
|
157
|
+
"message": "replace this node with business logic implementation",
|
|
158
|
+
"format": "string",
|
|
159
|
+
"headers": {},
|
|
160
|
+
"x": 50 + this.shiftx,
|
|
161
|
+
"y": y,
|
|
162
|
+
"wires":[[ subflowOutId ]],
|
|
163
|
+
"caname":"logger",
|
|
164
|
+
"category":"logging"
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
getTab(tabName) {
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
|
|
173
|
+
"id": this.getId(),
|
|
174
|
+
|
|
175
|
+
"type": "tab",
|
|
176
|
+
|
|
177
|
+
"label": tabName,
|
|
178
|
+
|
|
179
|
+
"disabled": false,
|
|
180
|
+
|
|
181
|
+
"info": ""
|
|
182
|
+
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
getEventListener(id, nextId, tabId, x, y, provider, verb, resource, notes) {
|
|
188
|
+
return {
|
|
189
|
+
"id": id,
|
|
190
|
+
"type": "EventListener",
|
|
191
|
+
"z": tabId,
|
|
192
|
+
"name": verb.toUpperCase() + " " + resource,
|
|
193
|
+
"provider": provider,
|
|
194
|
+
"eventSource": "api",
|
|
195
|
+
"dynamodbOperation": "",
|
|
196
|
+
"apiUrl": provider === "aws" ? resource : "",
|
|
197
|
+
"apiMethod": provider === "aws" ? verb : "",
|
|
198
|
+
"albMethod": "any",
|
|
199
|
+
"albUrl": "",
|
|
200
|
+
"bucketName": "",
|
|
201
|
+
"event": "",
|
|
202
|
+
"kapiUrl": "",
|
|
203
|
+
"kcronexpression": "",
|
|
204
|
+
"zapiUrl": provider === "azure" ? resource : "",
|
|
205
|
+
"zapiMethod": provider === "azure" ? verb : "",
|
|
206
|
+
"gapiUrl": provider === "gcp" ? resource : "",
|
|
207
|
+
"gapiMethod": provider === "gcp" ? verb : "",
|
|
208
|
+
"napiUrl": provider === "nodejs" ? resource : "",
|
|
209
|
+
"napiMethod": provider === "nodejs" ? verb : "",
|
|
210
|
+
"x": x,
|
|
211
|
+
"y": y,
|
|
212
|
+
"wires": [
|
|
213
|
+
[nextId]
|
|
214
|
+
],
|
|
215
|
+
"caname": "event-handler",
|
|
216
|
+
"category": "general",
|
|
217
|
+
"info": notes || ""
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
getTest(tabId, x, y, verb, resource, statusCode) {
|
|
222
|
+
|
|
223
|
+
const idEnd = this.getId();
|
|
224
|
+
const idStart = this.getId();
|
|
225
|
+
const idAssert = this.getId();
|
|
226
|
+
|
|
227
|
+
return [{
|
|
228
|
+
|
|
229
|
+
"id": idAssert,
|
|
230
|
+
"type": "Assertion",
|
|
231
|
+
"z": tabId,
|
|
232
|
+
"name": "check status",
|
|
233
|
+
"selector": "statusCode",
|
|
234
|
+
"property": "hello",
|
|
235
|
+
"comparison": "equals",
|
|
236
|
+
"value": statusCode,
|
|
237
|
+
"valueType": "str",
|
|
238
|
+
"x": x + this.shiftx * 2,
|
|
239
|
+
"y": y,
|
|
240
|
+
"wires": [[idEnd]],
|
|
241
|
+
"caname": "test-assertion",
|
|
242
|
+
"category": "testing"
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
"id": idEnd,
|
|
246
|
+
"type": "TestCaseEnd",
|
|
247
|
+
"z": tabId,
|
|
248
|
+
"name": "TestCaseEnd",
|
|
249
|
+
"x": x + this.shiftx * 3,
|
|
250
|
+
"y": y,
|
|
251
|
+
"wires": [],
|
|
252
|
+
"caname": "test-case-end",
|
|
253
|
+
"category": "testing"
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"id": idStart,
|
|
257
|
+
"type": "HTTPTestCase",
|
|
258
|
+
"z": tabId,
|
|
259
|
+
"name": "HTTP Test " + verb + " " + resource,
|
|
260
|
+
"method": verb.toUpperCase(),
|
|
261
|
+
"path": resource,
|
|
262
|
+
"headers": {
|
|
263
|
+
"Accept": "application/json"
|
|
264
|
+
},
|
|
265
|
+
"authtype": "none",
|
|
266
|
+
"secUser": "",
|
|
267
|
+
"secPassword": "",
|
|
268
|
+
"secToken": "",
|
|
269
|
+
"payload": "",
|
|
270
|
+
"x": x + this.shiftx,
|
|
271
|
+
"y": y,
|
|
272
|
+
"wires": [[idAssert]],
|
|
273
|
+
"caname": "http-test-case",
|
|
274
|
+
"category": "testing"
|
|
275
|
+
}];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
getXML(id, nextId, tabId, x, y) {
|
|
279
|
+
return {
|
|
280
|
+
"id": id,
|
|
281
|
+
"type":"XML",
|
|
282
|
+
"z": tabId,
|
|
283
|
+
"name":"XML",
|
|
284
|
+
"property":"msg.payload",
|
|
285
|
+
"attr":"",
|
|
286
|
+
"chr":"",
|
|
287
|
+
"x":x,
|
|
288
|
+
"y":y,
|
|
289
|
+
"wires":[ [nextId] ],
|
|
290
|
+
"caname":"XML",
|
|
291
|
+
"category":"transformation"
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
getForm(id, nextId, tabId, x, y) {
|
|
296
|
+
return {
|
|
297
|
+
"id":id,
|
|
298
|
+
"type":"Form Data",
|
|
299
|
+
"z":tabId,
|
|
300
|
+
"name":"Form Data",
|
|
301
|
+
"attr":"",
|
|
302
|
+
"chr":"",
|
|
303
|
+
"x":x,
|
|
304
|
+
"y":y,
|
|
305
|
+
"wires":[[nextId]],
|
|
306
|
+
"caname":"Form2Json",
|
|
307
|
+
"category":"transformation"}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
getSetProperty(id, nextId, tabId, x, y) {
|
|
311
|
+
return {
|
|
312
|
+
"id":id,
|
|
313
|
+
"type":"Set-Property",
|
|
314
|
+
"z":tabId,
|
|
315
|
+
"name":"Set Content Type",
|
|
316
|
+
"rules":[
|
|
317
|
+
{
|
|
318
|
+
"t":"set",
|
|
319
|
+
"p":"contentType",
|
|
320
|
+
"pt":"camsg",
|
|
321
|
+
"to":"$exists(msg.header.event.Records[0].headers.'Content-Type') ? msg.header.event.Records[0].headers.'Content-Type' : msg.header.event.Records[0].headers.'content-type'",
|
|
322
|
+
"tot":"jsonata"
|
|
323
|
+
}
|
|
324
|
+
],
|
|
325
|
+
"action":"",
|
|
326
|
+
"property":"",
|
|
327
|
+
"from":"",
|
|
328
|
+
"to":"",
|
|
329
|
+
"reg":false,
|
|
330
|
+
"x":x,
|
|
331
|
+
"y":y,
|
|
332
|
+
"wires":[[ nextId ]],
|
|
333
|
+
"caname":"setproperty",
|
|
334
|
+
"category":"transformation"
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
getSwitch(id, tabId, x, y, label) {
|
|
339
|
+
|
|
340
|
+
let s = {
|
|
341
|
+
"id":id,
|
|
342
|
+
"type":"Switch",
|
|
343
|
+
"z":tabId,
|
|
344
|
+
"name": label,
|
|
345
|
+
"property":"msg.contentType",
|
|
346
|
+
"propertyType":"",
|
|
347
|
+
"rules":[],
|
|
348
|
+
"repair":false,
|
|
349
|
+
"outputs":0,
|
|
350
|
+
"x":x,
|
|
351
|
+
"y":y,
|
|
352
|
+
"wires":[],
|
|
353
|
+
"caname":"switch",
|
|
354
|
+
"category":"routing"
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return s;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
handleApi(projectName, provider = "aws") {
|
|
361
|
+
|
|
362
|
+
const flowFileName = projectName + "-flow.json";
|
|
363
|
+
|
|
364
|
+
const { codegen } = require('@kumologica/builder');
|
|
365
|
+
const packageInfo = require('../../../package.json');
|
|
366
|
+
|
|
367
|
+
let pc = codegen.genPackageJson(
|
|
368
|
+
projectName,
|
|
369
|
+
"lambda.js",
|
|
370
|
+
flowFileName,
|
|
371
|
+
packageInfo.version,
|
|
372
|
+
this.api?.info?.title,
|
|
373
|
+
this.api?.info?.version,
|
|
374
|
+
this.api?.info?.description,
|
|
375
|
+
this.api?.info?.contact ? this.api?.info?.contact?.email: "",
|
|
376
|
+
this.api?.info?.license ? this.api?.info?.license?.name: "MIT");
|
|
377
|
+
|
|
378
|
+
const paths = this.api.paths;
|
|
379
|
+
|
|
380
|
+
let flow = [];
|
|
381
|
+
|
|
382
|
+
Object.keys(paths).forEach(path => {
|
|
383
|
+
|
|
384
|
+
const svc = paths[path];
|
|
385
|
+
flow = flow.concat(this.handleResource(path, svc, provider));
|
|
386
|
+
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
return { pck: pc, flow: flow };
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
generateTabName(path, v) {
|
|
393
|
+
|
|
394
|
+
return v.toUpperCase() + ":" + path;
|
|
395
|
+
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
updateIdsAndPosition(id, nextId, x) {
|
|
399
|
+
id = nextId;
|
|
400
|
+
nextId = this.getId();
|
|
401
|
+
x += this.shiftx;
|
|
402
|
+
return { id, nextId, x };
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
handleVerb(path, v, verb, provider) {
|
|
406
|
+
|
|
407
|
+
const resource = path.replace(/{/g, ":").replace(/}/g, "");
|
|
408
|
+
const tabName = this.generateTabName(resource, v);
|
|
409
|
+
|
|
410
|
+
console.log(` ${v}: ${path}`);
|
|
411
|
+
|
|
412
|
+
let x = 50;
|
|
413
|
+
let y = 50;
|
|
414
|
+
let listenerEndY = 50; // to align listeners end
|
|
415
|
+
let listenerEndX = 50; // to align listeners end
|
|
416
|
+
let flow = [];
|
|
417
|
+
|
|
418
|
+
// create tab for verb - resource
|
|
419
|
+
const tab = this.getTab(tabName);
|
|
420
|
+
flow.push(tab);
|
|
421
|
+
|
|
422
|
+
let id = this.getId();
|
|
423
|
+
let nextId = this.getId();
|
|
424
|
+
let logicId = this.getId();
|
|
425
|
+
|
|
426
|
+
flow.push(this.getEventListener(id, nextId, tab.id, x, y, provider, v, resource, verb.summary + "\n" + verb.description));
|
|
427
|
+
({ id, nextId, x } = this.updateIdsAndPosition(id, nextId, x));
|
|
428
|
+
|
|
429
|
+
flow.push(this.getLog(id, nextId, tab.id, x, y, "Log Request", "received: " + v + " " + resource, "INFO"));
|
|
430
|
+
({ id, nextId, x } = this.updateIdsAndPosition(id, nextId, x));
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
// Handle requestBody content
|
|
434
|
+
if (verb.requestBody && verb.requestBody.content) {
|
|
435
|
+
|
|
436
|
+
const requestBodyContentTypes = Object.keys(verb.requestBody.content);
|
|
437
|
+
|
|
438
|
+
let switchNode;
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
if (requestBodyContentTypes.length > 1) {
|
|
442
|
+
// set content type
|
|
443
|
+
flow.push(this.getSetProperty(id, nextId, tab.id, x, y));
|
|
444
|
+
({ id, nextId, x } = this.updateIdsAndPosition(id, nextId, x));
|
|
445
|
+
|
|
446
|
+
// switch
|
|
447
|
+
switchNode = this.getSwitch(id, tab.id, x, y, "Request Content Type"); // add switch node with no wires (no nextId)
|
|
448
|
+
flow.push(switchNode);
|
|
449
|
+
({ id, nextId, x } = this.updateIdsAndPosition(id, nextId, x));
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const requestX = x;
|
|
453
|
+
requestBodyContentTypes.forEach((contentType, i) => {
|
|
454
|
+
const content = verb.requestBody.content[contentType];
|
|
455
|
+
|
|
456
|
+
let schemaId = content.schema? id: logicId;
|
|
457
|
+
|
|
458
|
+
let currId = null;
|
|
459
|
+
|
|
460
|
+
// for application/json there is no need for conversion, just pass through to validation
|
|
461
|
+
if (contentType === 'application/xml') {
|
|
462
|
+
currId = this.getId();
|
|
463
|
+
flow.push(this.getXML(currId, schemaId, tab.id, requestX, y + (i * this.shifty)));
|
|
464
|
+
|
|
465
|
+
} else if (contentType === 'application/x-www-form-urlencoded') {
|
|
466
|
+
currId = this.getId();
|
|
467
|
+
flow.push(this.getForm(currId, schemaId, tab.id, requestX, y + (i * this.shifty)));
|
|
468
|
+
|
|
469
|
+
} else {
|
|
470
|
+
// link switch to schema or logic node
|
|
471
|
+
// currId = logicId;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// attach schema to current content type and point to the logic node,
|
|
475
|
+
if (content.schema) {
|
|
476
|
+
const schema = this.getSchema(schemaId, logicId, tab.id, requestX + this.shiftx, y + (i * this.shifty), JSON.stringify(content.schema, null, 2));
|
|
477
|
+
x = requestX + this.shiftx;
|
|
478
|
+
({ id, nextId, x } = this.updateIdsAndPosition(id, nextId, x));
|
|
479
|
+
|
|
480
|
+
flow.push(schema);
|
|
481
|
+
// logicNode.x = schema.x + this.shiftx; // adjust logic node x position
|
|
482
|
+
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (switchNode) {
|
|
486
|
+
if (!currId) {
|
|
487
|
+
currId = schemaId;
|
|
488
|
+
}
|
|
489
|
+
switchNode.rules.push({t: "eq", v: contentType, vt: "str"});
|
|
490
|
+
switchNode.outputs++;
|
|
491
|
+
switchNode.wires.push([ currId ]);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
});
|
|
495
|
+
} else {
|
|
496
|
+
logicId = id;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// set logic node, x will be updated
|
|
500
|
+
const logicInId = this.getId(); // this is for business logic subflow in
|
|
501
|
+
let logicNode = this.getLogicCall(logicId, nextId, tab.id, x, y, logicInId);
|
|
502
|
+
|
|
503
|
+
//let logicNode = this.getLog(logicId, nextId, tab.id, x , y, "Log Logic", "logic for: " + v + " " + resource, "INFO");
|
|
504
|
+
({ id, nextId, x } = this.updateIdsAndPosition(id, nextId, x));
|
|
505
|
+
|
|
506
|
+
flow.push(logicNode);
|
|
507
|
+
x = logicNode.x + this.shiftx;
|
|
508
|
+
listenerEndX = x;
|
|
509
|
+
|
|
510
|
+
//sort by code
|
|
511
|
+
const sortedResponses = Object.keys(verb.responses).sort((a, b) => a.localeCompare(b));
|
|
512
|
+
|
|
513
|
+
let currId = null;
|
|
514
|
+
// 200, 201, 400, 500, ...
|
|
515
|
+
sortedResponses.forEach((r, i) => {
|
|
516
|
+
const res = verb.responses[r];
|
|
517
|
+
// Transform headers into key-value pair with empty string values
|
|
518
|
+
const transformedHeaders = Object.keys(res.headers || {}).reduce((acc, headerKey) => {
|
|
519
|
+
acc[headerKey] = "";
|
|
520
|
+
return acc;
|
|
521
|
+
}, {});
|
|
522
|
+
|
|
523
|
+
//console.log(" processing " + r);
|
|
524
|
+
|
|
525
|
+
// for each content type
|
|
526
|
+
let responseSwitchNode = null;
|
|
527
|
+
|
|
528
|
+
//console.log((JSON.stringify(verb.responses[r])));
|
|
529
|
+
if ( res.content) {
|
|
530
|
+
|
|
531
|
+
if (Object.keys(res.content).length > 1) {
|
|
532
|
+
|
|
533
|
+
responseSwitchNode = this.getSwitch(id, tab.id, x, y, "Response Content Type"); // add switch node with no wires (no nextId)
|
|
534
|
+
flow.push(responseSwitchNode);
|
|
535
|
+
listenerEndX += this.shiftx;
|
|
536
|
+
|
|
537
|
+
({ id, nextId, x } = this.updateIdsAndPosition(id, nextId, x));
|
|
538
|
+
logicNode.wires.push([ responseSwitchNode.id ]);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
Object.keys(res.content).sort((a, b) => b.localeCompare(a)).forEach((c) => {
|
|
542
|
+
|
|
543
|
+
currId = null;
|
|
544
|
+
if (c === 'application/xml') {
|
|
545
|
+
currId = id;
|
|
546
|
+
flow.push(this.getXML(currId, nextId, tab.id, x, y + (i * this.shifty)));
|
|
547
|
+
({ id, nextId, x } = this.updateIdsAndPosition(id, nextId, x));
|
|
548
|
+
listenerEndX += this.shiftx;
|
|
549
|
+
}
|
|
550
|
+
const endId = id;
|
|
551
|
+
flow.push(this.getLog(id, nextId, tab.id, listenerEndX, listenerEndY, "Log Response " + r, `msg.payload`, "INFO"));
|
|
552
|
+
({ id, nextId, x } = this.updateIdsAndPosition(id, nextId, x));
|
|
553
|
+
|
|
554
|
+
flow.push(this.getEventListenerEnd(id, tab.id, listenerEndX+this.shiftx, listenerEndY, r, null, res.description, { ...transformedHeaders, "Content-Type": c }));
|
|
555
|
+
listenerEndY += this.shifty;
|
|
556
|
+
({ id, nextId, x } = this.updateIdsAndPosition(id, nextId, x));
|
|
557
|
+
|
|
558
|
+
if (responseSwitchNode) {
|
|
559
|
+
responseSwitchNode.rules.push({t: "eq", v: c, vt: "str"});
|
|
560
|
+
responseSwitchNode.outputs++;
|
|
561
|
+
responseSwitchNode.wires.push([ currId? currId: endId ]);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
});
|
|
565
|
+
} else {
|
|
566
|
+
|
|
567
|
+
// just end or exception
|
|
568
|
+
if (i == 0) {
|
|
569
|
+
flow.push(this.getLog(id, nextId, tab.id, listenerEndX, listenerEndY, "Log Response " + r, "msg.payload", "INFO"));
|
|
570
|
+
({ id, nextId, x } = this.updateIdsAndPosition(id, nextId, x));
|
|
571
|
+
|
|
572
|
+
} else if ((r.startsWith("4") || r.startsWith("5")) ) {
|
|
573
|
+
flow.push(this.getError(this.getId(), nextId, tab.id, listenerEndX-this.shiftx, listenerEndY, r, res.description));;
|
|
574
|
+
({ id, nextId, x } = this.updateIdsAndPosition(id, nextId, x));
|
|
575
|
+
|
|
576
|
+
flow.push(this.getLog(id, nextId, tab.id, listenerEndX, listenerEndY, "Log Error " + r, `msg.error = null? "Error: ${r}": msg.error`, "ERROR"));
|
|
577
|
+
({ id, nextId, x } = this.updateIdsAndPosition(id, nextId, x));
|
|
578
|
+
|
|
579
|
+
}
|
|
580
|
+
flow.push(this.getEventListenerEnd(id, tab.id, listenerEndX+this.shiftx, listenerEndY, r, null, res.description, { ...transformedHeaders, "Content-Type": "application/json" }));
|
|
581
|
+
|
|
582
|
+
//flow = [...flow, ...this.getTest(tab.id, listenerEndX + this.shiftx, listenerEndY, v, resource, r)];
|
|
583
|
+
|
|
584
|
+
listenerEndY += this.shifty;
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
flow = [...flow, ...this.getLogicSubflow(logicInId, tab.id, listenerEndY)];
|
|
589
|
+
|
|
590
|
+
return flow;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
handleResource(path, svc, provider) {
|
|
594
|
+
|
|
595
|
+
let flow = [];
|
|
596
|
+
|
|
597
|
+
Object.keys(svc).forEach(v => {
|
|
598
|
+
const verb = svc[v];
|
|
599
|
+
flow = flow.concat(this.handleVerb(path, v, verb, provider));
|
|
600
|
+
})
|
|
601
|
+
|
|
602
|
+
return flow;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
module.exports = {
|
|
607
|
+
OpenApiBuilder
|
|
608
|
+
}
|
|
@@ -1,42 +1,65 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
type: 'input',
|
|
19
|
-
name: 'directory',
|
|
20
|
-
message: 'Enter the path where the project will be created '
|
|
21
|
-
});
|
|
22
|
-
let aProjectPath = directoryResponse.directory;
|
|
23
|
-
|
|
24
|
-
// Template name
|
|
25
|
-
const promptTemplate = new Select({
|
|
26
|
-
name: 'template',
|
|
27
|
-
message: 'Pick one of the available templates',
|
|
28
|
-
choices: availableTemplates
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
let aTemplateName = await promptTemplate.run();
|
|
32
|
-
|
|
33
|
-
try{
|
|
34
|
-
await downloadTemplateFromRepo(undefined, aTemplateName, aProjectPath, aProjectName);
|
|
35
|
-
logNotice(`Successfully created project: ${pathlib.join(aProjectPath, aProjectName)}`)
|
|
36
|
-
} catch (err){
|
|
37
|
-
logError(`Error found while creating project due to: ${err.message}`)
|
|
1
|
+
const { logNotice, logError, logInfo } = require('../../utils/logger');
|
|
2
|
+
const SwaggerParser = require('@apidevtools/swagger-parser');
|
|
3
|
+
const { OpenApiBuilder } = require('./OpenApiBuilder');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs-extra');
|
|
6
|
+
const { URL } = require('url');
|
|
7
|
+
|
|
8
|
+
function getFileName(fullPath) {
|
|
9
|
+
let fileName;
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
// Check if the path is a URL
|
|
13
|
+
const url = new URL(fullPath);
|
|
14
|
+
fileName = path.basename(url.pathname);
|
|
15
|
+
} catch (e) {
|
|
16
|
+
// If it's not a valid URL, treat it as a filesystem path
|
|
17
|
+
fileName = path.basename(fullPath);
|
|
38
18
|
}
|
|
19
|
+
|
|
20
|
+
// Remove the file extension
|
|
21
|
+
return fileName.replace(path.extname(fileName), '');
|
|
39
22
|
}
|
|
23
|
+
|
|
24
|
+
function createProject(p, projectName, pck, flow) {
|
|
25
|
+
|
|
26
|
+
// create project directory
|
|
27
|
+
const rootDir = path.join(p || process.cwd(), projectName);
|
|
28
|
+
|
|
29
|
+
// logInfo(rootDir);
|
|
30
|
+
fs.ensureDir(rootDir);
|
|
40
31
|
|
|
32
|
+
fs.outputFileSync(path.join(rootDir, projectName + "-flow.json"), JSON.stringify(flow, null, 2), 'utf-8');
|
|
33
|
+
fs.outputFileSync(path.join(rootDir, "package.json"), pck, 'utf-8');
|
|
34
|
+
|
|
35
|
+
return rootDir;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function createOpenApiProject(p, apiSpec) {
|
|
41
39
|
|
|
42
|
-
|
|
40
|
+
try {
|
|
41
|
+
const api = await SwaggerParser.validate(apiSpec);
|
|
42
|
+
|
|
43
|
+
// Print some basic API info.
|
|
44
|
+
logInfo(`Successfully parsed API: ${api?.info?.title}`);
|
|
45
|
+
logInfo(`Version: ${api?.info?.version}`);
|
|
46
|
+
const projectName = getFileName(apiSpec);
|
|
47
|
+
|
|
48
|
+
const builder = new OpenApiBuilder(api);
|
|
49
|
+
const response = builder.handleApi(projectName);
|
|
50
|
+
|
|
51
|
+
const rootDir = createProject(p, projectName, response.pck, response.flow);
|
|
52
|
+
|
|
53
|
+
logInfo(" ");
|
|
54
|
+
logInfo("Project created successfully.");
|
|
55
|
+
logInfo("To open project in kumologica designer run the following command:");
|
|
56
|
+
logInfo("kl open " + rootDir);
|
|
57
|
+
|
|
58
|
+
} catch (error) {
|
|
59
|
+
logError("Error parsing OpenAPI specification:", error.message);
|
|
60
|
+
logError(error);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = createOpenApiProject;
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
const {
|
|
2
|
+
CognitoIdentityProviderClient,
|
|
3
|
+
SignUpCommand,
|
|
4
|
+
ConfirmSignUpCommand,
|
|
5
|
+
InitiateAuthCommand,
|
|
6
|
+
RefreshTokenCommand,
|
|
7
|
+
} = require('@aws-sdk/client-cognito-identity-provider');
|
|
8
|
+
const fs = require('fs').promises;
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const axios = require('axios');
|
|
11
|
+
const { prompt } = require('enquirer'); // Import enquirer for prompting
|
|
12
|
+
|
|
13
|
+
// Simulated DNS-based config fetch
|
|
14
|
+
async function fetchCognitoConfig(dnsName = 'config.myapp.com') {
|
|
15
|
+
try {
|
|
16
|
+
const response = await axios.get(`https://${dnsName}/cognito-config`);
|
|
17
|
+
return response.data;
|
|
18
|
+
} catch (err) {
|
|
19
|
+
console.error('Config fetch error:', err.message);
|
|
20
|
+
return {
|
|
21
|
+
region: 'YOUR_AWS_REGION', // e.g., 'us-east-1'
|
|
22
|
+
userPoolId: 'YOUR_USER_POOL_ID', // e.g., 'us-east-1_abc123'
|
|
23
|
+
clientId: 'YOUR_CLIENT_ID' // e.g., '1a2b3c4d5e6f7g8h9i0j'
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Directory and file for storing tokens
|
|
29
|
+
const tokensDir = path.join(__dirname, 'tokens');
|
|
30
|
+
const tokensFile = path.join(tokensDir, 'tokens.json');
|
|
31
|
+
|
|
32
|
+
// --- Initialize Cognito Client with Dynamic Config ---
|
|
33
|
+
async function getCognitoClient() {
|
|
34
|
+
const config = await fetchCognitoConfig();
|
|
35
|
+
return {
|
|
36
|
+
client: new CognitoIdentityProviderClient({ region: config.region }),
|
|
37
|
+
userPoolId: config.userPoolId,
|
|
38
|
+
clientId: config.clientId,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// --- Signup Function with Email Verification ---
|
|
43
|
+
async function signUp(email, password, client, clientId) {
|
|
44
|
+
const params = {
|
|
45
|
+
ClientId: clientId,
|
|
46
|
+
Username: email,
|
|
47
|
+
Password: password,
|
|
48
|
+
UserAttributes: [{ Name: 'email', Value: email }],
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const command = new SignUpCommand(params);
|
|
52
|
+
const result = await client.send(command);
|
|
53
|
+
console.log('Signup successful! Please check your email for a verification code.', result.UserSub);
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// --- Email Verification Function ---
|
|
58
|
+
async function verifyEmail(email, verificationCode, client, clientId) {
|
|
59
|
+
const params = {
|
|
60
|
+
ClientId: clientId,
|
|
61
|
+
Username: email,
|
|
62
|
+
ConfirmationCode: verificationCode,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const command = new ConfirmSignUpCommand(params);
|
|
66
|
+
const result = await client.send(command);
|
|
67
|
+
console.log('Email verified successfully!');
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// --- Login Function ---
|
|
72
|
+
async function login(email, password, client, clientId) {
|
|
73
|
+
const params = {
|
|
74
|
+
AuthFlow: 'USER_PASSWORD_AUTH',
|
|
75
|
+
ClientId: clientId,
|
|
76
|
+
AuthParameters: { USERNAME: email, PASSWORD: password },
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const command = new InitiateAuthCommand(params);
|
|
80
|
+
const result = await client.send(command);
|
|
81
|
+
const tokens = {
|
|
82
|
+
accessToken: result.AuthenticationResult.AccessToken,
|
|
83
|
+
idToken: result.AuthenticationResult.IdToken,
|
|
84
|
+
refreshToken: result.AuthenticationResult.RefreshToken,
|
|
85
|
+
};
|
|
86
|
+
console.log('Login successful!', tokens);
|
|
87
|
+
|
|
88
|
+
await fs.mkdir(tokensDir, { recursive: true });
|
|
89
|
+
await fs.writeFile(tokensFile, JSON.stringify(tokens, null, 2));
|
|
90
|
+
console.log('Tokens stored in', tokensFile);
|
|
91
|
+
|
|
92
|
+
return tokens;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// --- Token Validation Function ---
|
|
96
|
+
function validateToken(token) {
|
|
97
|
+
try {
|
|
98
|
+
const decoded = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
|
|
99
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
100
|
+
if (decoded.exp < currentTime) {
|
|
101
|
+
console.log('Token expired');
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
console.log('Token is valid');
|
|
105
|
+
return true;
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error('Token validation error:', err.message);
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// --- Refresh Token Function ---
|
|
113
|
+
async function refreshToken(refreshToken, client, clientId) {
|
|
114
|
+
const params = {
|
|
115
|
+
AuthFlow: 'REFRESH_TOKEN_AUTH',
|
|
116
|
+
ClientId: clientId,
|
|
117
|
+
AuthParameters: { REFRESH_TOKEN: refreshToken },
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const command = new RefreshTokenCommand(params);
|
|
121
|
+
const result = await client.send(command);
|
|
122
|
+
const newTokens = {
|
|
123
|
+
accessToken: result.AuthenticationResult.AccessToken,
|
|
124
|
+
idToken: result.AuthenticationResult.IdToken,
|
|
125
|
+
refreshToken: refreshToken,
|
|
126
|
+
};
|
|
127
|
+
console.log('Token refreshed successfully!', newTokens);
|
|
128
|
+
|
|
129
|
+
await fs.writeFile(tokensFile, JSON.stringify(newTokens, null, 2));
|
|
130
|
+
console.log('Refreshed tokens updated in', tokensFile);
|
|
131
|
+
|
|
132
|
+
return newTokens;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// --- Check for Existing Tokens ---
|
|
136
|
+
async function getStoredTokens() {
|
|
137
|
+
try {
|
|
138
|
+
const data = await fs.readFile(tokensFile, 'utf8');
|
|
139
|
+
return JSON.parse(data);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
if (err.code === 'ENOENT') {
|
|
142
|
+
console.log('No stored tokens found.');
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
throw err;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// --- Updated Main Function with Enquirer Prompt ---
|
|
150
|
+
async function main() {
|
|
151
|
+
const email = 'user@example.com'; // You could prompt for this too
|
|
152
|
+
const password = 'SecurePass123!'; // You could prompt for this too
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const { client, clientId } = await getCognitoClient();
|
|
156
|
+
|
|
157
|
+
const storedTokens = await getStoredTokens();
|
|
158
|
+
|
|
159
|
+
if (storedTokens) {
|
|
160
|
+
const isValid = validateToken(storedTokens.accessToken);
|
|
161
|
+
if (isValid) {
|
|
162
|
+
console.log('Using existing valid tokens:', storedTokens);
|
|
163
|
+
return storedTokens;
|
|
164
|
+
} else {
|
|
165
|
+
const refreshedTokens = await refreshToken(storedTokens.refreshToken, client, clientId);
|
|
166
|
+
console.log('Using refreshed tokens:', refreshedTokens);
|
|
167
|
+
return refreshedTokens;
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
// No tokens, proceed with signup
|
|
171
|
+
await signUp(email, password, client, clientId);
|
|
172
|
+
|
|
173
|
+
// Prompt user for verification code
|
|
174
|
+
const { verificationCode } = await prompt({
|
|
175
|
+
type: 'input',
|
|
176
|
+
name: 'verificationCode',
|
|
177
|
+
message: 'Enter the verification code sent to your email:',
|
|
178
|
+
validate: (value) => value.length > 0 || 'Please enter a valid code',
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
await verifyEmail(email, verificationCode, client, clientId);
|
|
182
|
+
const tokens = await login(email, password, client, clientId);
|
|
183
|
+
console.log('New tokens generated and stored:', tokens);
|
|
184
|
+
return tokens;
|
|
185
|
+
}
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error('Error in process:', error.message);
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Run the script
|
|
193
|
+
main()
|
|
194
|
+
.then((tokens) => console.log('Final tokens:', tokens))
|
|
195
|
+
.catch((err) => console.error('Main execution failed:', err.message));
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
-----
|
|
200
|
+
|
|
201
|
+
v2
|
|
202
|
+
|
|
203
|
+
const {
|
|
204
|
+
CognitoIdentityProviderClient,
|
|
205
|
+
SignUpCommand,
|
|
206
|
+
ConfirmSignUpCommand,
|
|
207
|
+
InitiateAuthCommand,
|
|
208
|
+
RefreshTokenCommand,
|
|
209
|
+
} = require('@aws-sdk/client-cognito-identity-provider');
|
|
210
|
+
const fs = require('fs').promises;
|
|
211
|
+
const path = require('path');
|
|
212
|
+
const axios = require('axios');
|
|
213
|
+
const { prompt } = require('enquirer');
|
|
214
|
+
const jwt = require('jsonwebtoken');
|
|
215
|
+
const jwksClient = require('jwks-rsa');
|
|
216
|
+
|
|
217
|
+
// Simulated DNS-based config fetch
|
|
218
|
+
async function fetchCognitoConfig(dnsName = 'config.myapp.com') {
|
|
219
|
+
try {
|
|
220
|
+
const response = await axios.get(`https://${dnsName}/cognito-config`);
|
|
221
|
+
return response.data;
|
|
222
|
+
} catch (err) {
|
|
223
|
+
console.error('Config fetch error:', err.message);
|
|
224
|
+
return {
|
|
225
|
+
region: 'YOUR_AWS_REGION', // e.g., 'us-east-1'
|
|
226
|
+
userPoolId: 'YOUR_USER_POOL_ID', // e.g., 'us-east-1_abc123'
|
|
227
|
+
clientId: 'YOUR_CLIENT_ID' // e.g., '1a2b3c4d5e6f7g8h9i0j'
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Directory and file for storing tokens
|
|
233
|
+
const tokensDir = path.join(__dirname, 'tokens');
|
|
234
|
+
const tokensFile = path.join(tokensDir, 'tokens.json');
|
|
235
|
+
|
|
236
|
+
// --- Initialize Cognito Client with Dynamic Config ---
|
|
237
|
+
async function getCognitoClient() {
|
|
238
|
+
const config = await fetchCognitoConfig();
|
|
239
|
+
return {
|
|
240
|
+
client: new CognitoIdentityProviderClient({ region: config.region }),
|
|
241
|
+
userPoolId: config.userPoolId,
|
|
242
|
+
clientId: config.clientId,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// --- Signup Function with Email Verification ---
|
|
247
|
+
async function signUp(email, password, client, clientId) {
|
|
248
|
+
const params = {
|
|
249
|
+
ClientId: clientId,
|
|
250
|
+
Username: email,
|
|
251
|
+
Password: password,
|
|
252
|
+
UserAttributes: [{ Name: 'email', Value: email }],
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const command = new SignUpCommand(params);
|
|
256
|
+
const result = await client.send(command);
|
|
257
|
+
console.log('Signup successful! Please check your email for a verification code.', result.UserSub);
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// --- Email Verification Function ---
|
|
262
|
+
async function verifyEmail(email, verificationCode, client, clientId) {
|
|
263
|
+
const params = {
|
|
264
|
+
ClientId: clientId,
|
|
265
|
+
Username: email,
|
|
266
|
+
ConfirmationCode: verificationCode,
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const command = new ConfirmSignUpCommand(params);
|
|
270
|
+
const result = await client.send(command);
|
|
271
|
+
console.log('Email verified successfully!');
|
|
272
|
+
return result;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// --- Login Function ---
|
|
276
|
+
async function login(email, password, client, clientId) {
|
|
277
|
+
const params = {
|
|
278
|
+
AuthFlow: 'USER_PASSWORD_AUTH',
|
|
279
|
+
ClientId: clientId,
|
|
280
|
+
AuthParameters: { USERNAME: email, PASSWORD: password },
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const command = new InitiateAuthCommand(params);
|
|
284
|
+
const result = await client.send(command);
|
|
285
|
+
const tokens = {
|
|
286
|
+
accessToken: result.AuthenticationResult.AccessToken,
|
|
287
|
+
idToken: result.AuthenticationResult.IdToken,
|
|
288
|
+
refreshToken: result.AuthenticationResult.RefreshToken,
|
|
289
|
+
};
|
|
290
|
+
console.log('Login successful!', tokens);
|
|
291
|
+
|
|
292
|
+
await fs.mkdir(tokensDir, { recursive: true });
|
|
293
|
+
await fs.writeFile(tokensFile, JSON.stringify(tokens, null, 2));
|
|
294
|
+
console.log('Tokens stored in', tokensFile);
|
|
295
|
+
|
|
296
|
+
return tokens;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// --- Full Token Validation Function for Production ---
|
|
300
|
+
async function validateToken(token, userPoolId, clientId) {
|
|
301
|
+
const jwksUri = `https://cognito-idp.${process.env.AWS_REGION || 'us-east-1'}.amazonaws.com/${userPoolId}/.well-known/jwks.json`;
|
|
302
|
+
const client = jwksClient({
|
|
303
|
+
jwksUri,
|
|
304
|
+
cache: true,
|
|
305
|
+
rateLimit: true,
|
|
306
|
+
jwksRequestsPerMinute: 5,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
function getKey(header, callback) {
|
|
310
|
+
client.getSigningKey(header.kid, (err, key) => {
|
|
311
|
+
if (err) {
|
|
312
|
+
callback(err);
|
|
313
|
+
} else {
|
|
314
|
+
const signingKey = key.getPublicKey();
|
|
315
|
+
callback(null, signingKey);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return new Promise((resolve, reject) => {
|
|
321
|
+
jwt.verify(
|
|
322
|
+
token,
|
|
323
|
+
getKey,
|
|
324
|
+
{
|
|
325
|
+
issuer: `https://cognito-idp.${process.env.AWS_REGION || 'us-east-1'}.amazonaws.com/${userPoolId}`,
|
|
326
|
+
audience: clientId, // For access token; idToken uses 'aud' differently, adjust if validating idToken
|
|
327
|
+
algorithms: ['RS256'],
|
|
328
|
+
},
|
|
329
|
+
(err, decoded) => {
|
|
330
|
+
if (err) {
|
|
331
|
+
console.error('Token validation failed:', err.message);
|
|
332
|
+
resolve(false);
|
|
333
|
+
} else {
|
|
334
|
+
console.log('Token is valid:', decoded);
|
|
335
|
+
resolve(true);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
);
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// --- Refresh Token Function ---
|
|
343
|
+
async function refreshToken(refreshToken, client, clientId) {
|
|
344
|
+
const params = {
|
|
345
|
+
AuthFlow: 'REFRESH_TOKEN_AUTH',
|
|
346
|
+
ClientId: clientId,
|
|
347
|
+
AuthParameters: { REFRESH_TOKEN: refreshToken },
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const command = new RefreshTokenCommand(params);
|
|
351
|
+
const result = await client.send(command);
|
|
352
|
+
const newTokens = {
|
|
353
|
+
accessToken: result.AuthenticationResult.AccessToken,
|
|
354
|
+
idToken: result.AuthenticationResult.IdToken,
|
|
355
|
+
refreshToken: refreshToken,
|
|
356
|
+
};
|
|
357
|
+
console.log('Token refreshed successfully!', newTokens);
|
|
358
|
+
|
|
359
|
+
await fs.writeFile(tokensFile, JSON.stringify(newTokens, null, 2));
|
|
360
|
+
console.log('Refreshed tokens updated in', tokensFile);
|
|
361
|
+
|
|
362
|
+
return newTokens;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// --- Check for Existing Tokens ---
|
|
366
|
+
async function getStoredTokens() {
|
|
367
|
+
try {
|
|
368
|
+
const data = await fs.readFile(tokensFile, 'utf8');
|
|
369
|
+
return JSON.parse(data);
|
|
370
|
+
} catch (err) {
|
|
371
|
+
if (err.code === 'ENOENT') {
|
|
372
|
+
console.log('No stored tokens found.');
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
throw err;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// --- Updated Main Function ---
|
|
380
|
+
async function main() {
|
|
381
|
+
const email = 'user@example.com';
|
|
382
|
+
const password = 'SecurePass123!';
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
const { client, userPoolId, clientId } = await getCognitoClient();
|
|
386
|
+
|
|
387
|
+
const storedTokens = await getStoredTokens();
|
|
388
|
+
|
|
389
|
+
if (storedTokens) {
|
|
390
|
+
const isValid = await validateToken(storedTokens.accessToken, userPoolId, clientId);
|
|
391
|
+
if (isValid) {
|
|
392
|
+
console.log('Using existing valid tokens:', storedTokens);
|
|
393
|
+
return storedTokens;
|
|
394
|
+
} else {
|
|
395
|
+
const refreshedTokens = await refreshToken(storedTokens.refreshToken, client, clientId);
|
|
396
|
+
console.log('Using refreshed tokens:', refreshedTokens);
|
|
397
|
+
return refreshedTokens;
|
|
398
|
+
}
|
|
399
|
+
} else {
|
|
400
|
+
await signUp(email, password, client, clientId);
|
|
401
|
+
|
|
402
|
+
const { verificationCode } = await prompt({
|
|
403
|
+
type: 'input',
|
|
404
|
+
name: 'verificationCode',
|
|
405
|
+
message: 'Enter the verification code sent to your email:',
|
|
406
|
+
validate: (value) => value.length > 0 || 'Please enter a valid code',
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
await verifyEmail(email, verificationCode, client, clientId);
|
|
410
|
+
const tokens = await login(email, password, client, clientId);
|
|
411
|
+
console.log('New tokens generated and stored:', tokens);
|
|
412
|
+
return tokens;
|
|
413
|
+
}
|
|
414
|
+
} catch (error) {
|
|
415
|
+
console.error('Error in process:', error.message);
|
|
416
|
+
throw error;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Run the script
|
|
421
|
+
main()
|
|
422
|
+
.then((tokens) => console.log('Final tokens:', tokens))
|
|
423
|
+
.catch((err) => console.error('Main execution failed:', err.message));
|
|
424
|
+
|
|
425
|
+
|
package/cli/commands/create.js
CHANGED
|
@@ -3,7 +3,8 @@ const { codegen } = require('@kumologica/builder');
|
|
|
3
3
|
|
|
4
4
|
const { logNotice, logError } = require('../utils/logger');
|
|
5
5
|
const { downloadTemplateFromRepo, isPlainGitURL } = require('../utils/download-template-from-repo');
|
|
6
|
-
const { createProjectIteratively
|
|
6
|
+
const { createProjectIteratively,
|
|
7
|
+
createOpenApiProject } = require('./create-commands');
|
|
7
8
|
|
|
8
9
|
exports.command = 'create [options]'
|
|
9
10
|
exports.builder = (yargs) => {
|
|
@@ -33,6 +34,11 @@ exports.desc = 'Create a kumologica project. Do not pass any options to create i
|
|
|
33
34
|
exports.handler = async ({ path, template, api }) => {
|
|
34
35
|
const { version } = require('../../package.json');
|
|
35
36
|
|
|
37
|
+
if (api) {
|
|
38
|
+
createOpenApiProject(path, api);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
36
42
|
if (!path && !template && !api) {
|
|
37
43
|
await createProjectIteratively();
|
|
38
44
|
|
package/cli/commands/run.js
CHANGED
|
@@ -100,23 +100,24 @@ exports.handler = async ({ project_directory, loglevel, port, taskName, args })
|
|
|
100
100
|
},
|
|
101
101
|
body: JSON.stringify({args: args})
|
|
102
102
|
};
|
|
103
|
-
const url = `http://127.0.0.1:${port||1880}/__task__/${tName}`;
|
|
104
|
-
|
|
105
|
-
const response = await fetch(url, req);
|
|
106
|
-
const data = await response.text();
|
|
107
|
-
|
|
108
|
-
logInfo("Status: " + response.status);
|
|
109
|
-
logInfo("Message: " + data);
|
|
110
|
-
|
|
111
|
-
await server.stop();
|
|
112
|
-
|
|
113
|
-
exitCode = response.status >= 200 && response.status < 300 ? 0 : 1;
|
|
114
103
|
|
|
104
|
+
if (tName) {
|
|
105
|
+
const url = `http://127.0.0.1:${port||1880}/__task__/${tName}`;
|
|
106
|
+
|
|
107
|
+
const response = await fetch(url, req);
|
|
108
|
+
const data = await response.text();
|
|
109
|
+
|
|
110
|
+
logInfo("Status: " + response.status);
|
|
111
|
+
logInfo("Message: " + data);
|
|
112
|
+
|
|
113
|
+
await server.stop();
|
|
114
|
+
|
|
115
|
+
exitCode = response.status >= 200 && response.status < 300 ? 0 : 1;
|
|
116
|
+
process.exit(exitCode); // this is task, terminate after completion
|
|
117
|
+
}
|
|
115
118
|
} catch (e) {
|
|
116
119
|
logFatal(e.message);
|
|
117
120
|
}
|
|
118
|
-
|
|
119
|
-
process.exit(exitCode);
|
|
120
121
|
};
|
|
121
122
|
|
|
122
123
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kumologica/sdk",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.2-beta1",
|
|
4
4
|
"productName": "Kumologica Designer",
|
|
5
5
|
"copyright": "Copyright 2020 Kumologica Pty Ltd, All Rights Reserved.",
|
|
6
6
|
"author": "Kumologica Pty Ltd <contact@kumologica.com>",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
},
|
|
64
64
|
"license": "Proprietary",
|
|
65
65
|
"dependencies": {
|
|
66
|
+
"@apidevtools/swagger-parser": "^10.1.1",
|
|
66
67
|
"@aws-sdk/client-api-gateway": "^3.556.0",
|
|
67
68
|
"@aws-sdk/client-cloudformation": "^3.556.0",
|
|
68
69
|
"@aws-sdk/client-cloudwatch-events": "^3.556.0",
|
|
@@ -82,9 +83,9 @@
|
|
|
82
83
|
"@aws-sdk/credential-providers": "^3.556.0",
|
|
83
84
|
"@aws-sdk/lib-dynamodb": "^3.549.0",
|
|
84
85
|
"@electron/remote": "^2.0.8",
|
|
85
|
-
"@kumologica/builder": "3.6.
|
|
86
|
-
"@kumologica/devkit": "3.6.
|
|
87
|
-
"@kumologica/runtime": "3.6.
|
|
86
|
+
"@kumologica/builder": "3.6.2-beta1",
|
|
87
|
+
"@kumologica/devkit": "3.6.2-beta1",
|
|
88
|
+
"@kumologica/runtime": "3.6.2-beta1",
|
|
88
89
|
"adm-zip": "0.4.13",
|
|
89
90
|
"ajv": "8.10.0",
|
|
90
91
|
"archive-type": "^4.0.0",
|
|
@@ -148,7 +149,7 @@
|
|
|
148
149
|
"when": "3.7.8",
|
|
149
150
|
"wide-align": "^1.1.5",
|
|
150
151
|
"wildcard-match": "^5.1.2",
|
|
151
|
-
"ws": "
|
|
152
|
+
"ws": "8.18.1",
|
|
152
153
|
"xterm": "4.1.0",
|
|
153
154
|
"xterm-addon-fit": "0.2.1",
|
|
154
155
|
"yargs": "17.3.1"
|
|
@@ -39,6 +39,7 @@ class ModalHome {
|
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
this.window = new BrowserWindow(windowOptions);
|
|
42
|
+
require('@electron/remote/main').enable(this.window.webContents);
|
|
42
43
|
|
|
43
44
|
this.window.on('focus', () => this.window.webContents.send('focus'));
|
|
44
45
|
this.window.on('blur', () => this.window.webContents.send('blur'));
|
|
@@ -38,6 +38,8 @@ class ModalWelcome {
|
|
|
38
38
|
|
|
39
39
|
this.window = new BrowserWindow(windowOptions);
|
|
40
40
|
|
|
41
|
+
require('@electron/remote/main').enable(this.window.webContents);
|
|
42
|
+
|
|
41
43
|
this.window.on('focus', () => this.window.webContents.send('focus'));
|
|
42
44
|
this.window.on('blur', () => this.window.webContents.send('blur'));
|
|
43
45
|
|