@kumologica/sdk 3.6.1 → 3.6.2-beta2

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.
@@ -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,5 +1,7 @@
1
1
  const createProjectIteratively = require('./create-project-iteratively');
2
+ const createOpenApiProject = require('./openapi');
2
3
 
3
4
  module.exports = {
4
- createProjectIteratively
5
+ createProjectIteratively,
6
+ createOpenApiProject
5
7
  }
@@ -1,42 +1,65 @@
1
- const pathlib = require('path');
2
- const { prompt, Select } = require('enquirer');
3
- const { logNotice, logError } = require('../../utils/logger');
4
- const { downloadTemplateFromRepo, listAvailableTemplates } = require('../../utils/download-template-from-repo');
5
-
6
- async function createProjectIteratively(){
7
- const availableTemplates = await listAvailableTemplates();
8
- // Project name
9
- const projectNameResponse = await prompt({
10
- type: 'input',
11
- name: 'projectName',
12
- message: 'Enter the name for the project'
13
- });
14
- let aProjectName = projectNameResponse.projectName;
15
-
16
- // Target Path
17
- const directoryResponse = await prompt({
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
- module.exports = createProjectIteratively;
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;
@@ -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 } = require('./create-commands');
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
 
@@ -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.1",
3
+ "version": "3.6.2-beta2",
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.1",
86
- "@kumologica/devkit": "3.6.1",
87
- "@kumologica/runtime": "3.6.1",
86
+ "@kumologica/builder": "3.6.2-beta2",
87
+ "@kumologica/devkit": "3.6.2-beta2",
88
+ "@kumologica/runtime": "3.6.2-beta2",
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": "7.5.9",
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