@kumologica/sdk 3.6.0-beta1 → 3.6.0-beta3
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/cli.js +30 -8
- package/cli/commands/serve.js +139 -0
- package/package.json +4 -4
- package/src/server/DesignerServer.js +152 -153
package/cli/cli.js
CHANGED
|
@@ -1,9 +1,31 @@
|
|
|
1
|
-
const
|
|
1
|
+
const { logError } = require("./utils/logger"); //("../utils/logger");
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
const validCommands = [
|
|
4
|
+
"build",
|
|
5
|
+
"create-node",
|
|
6
|
+
"create",
|
|
7
|
+
"doc",
|
|
8
|
+
"export",
|
|
9
|
+
"list-templates",
|
|
10
|
+
"login",
|
|
11
|
+
"open",
|
|
12
|
+
"serve",
|
|
13
|
+
"test",
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
require("yargs/yargs")(process.argv.slice(2))
|
|
17
|
+
.commandDir("commands")
|
|
18
|
+
.demandCommand(1, "Error: Command missing.")
|
|
19
|
+
.check((argv) => {
|
|
20
|
+
if (!validCommands.includes(argv._[0].toLowerCase())) {
|
|
21
|
+
throw new Error(`Unknown command: ${argv._[0]}. `);
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
})
|
|
25
|
+
.help("help", "Show usage information & exit")
|
|
26
|
+
.alias("help", "h")
|
|
27
|
+
.fail((msg, err, yargs) => {
|
|
28
|
+
logError(msg);
|
|
29
|
+
yargs.showHelp();
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}).argv;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This command should be used to start the kumologica runtime in local mode. And it will
|
|
3
|
+
* be assisting the UI on all aspects of the project development.
|
|
4
|
+
*
|
|
5
|
+
* Example:
|
|
6
|
+
* kl start ./myproject
|
|
7
|
+
* kl start ./myproject/flow.json
|
|
8
|
+
* kl start (this will look for a flow.json in the current directory)
|
|
9
|
+
*
|
|
10
|
+
* If not flow is found, the process will be exited (with code 1)
|
|
11
|
+
*
|
|
12
|
+
* The Runtime API can be found in: packages/runtime/src/runtime/lib/api/rest/index.js
|
|
13
|
+
* The actual runtime is in: packages/runtime/src/runtime/lib/index.js
|
|
14
|
+
*
|
|
15
|
+
* Naming convention used during the code:
|
|
16
|
+
* - AdminApp (express app) is the server that serves the Runtime API
|
|
17
|
+
* - NodeApp (express app) is the server that runs the flow
|
|
18
|
+
*
|
|
19
|
+
* DEVELOPER NOTE:
|
|
20
|
+
* Current implementation seems to be mounting the runtime api in the NodeApp (see runtime/lib/index.js:534)
|
|
21
|
+
* Ideally we wouldl like to separate the two apps and have the AdminApp running on a different port.
|
|
22
|
+
*
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const path = require("path");
|
|
26
|
+
const fs = require("fs");
|
|
27
|
+
const { codegen } = require("@kumologica/builder");
|
|
28
|
+
const { DesignerServer } = require("../../src/server/DesignerServer");
|
|
29
|
+
const { logError, logNotice, logInfo, logFatal } = require("../utils/logger");
|
|
30
|
+
// const opn = require('better-opn');
|
|
31
|
+
|
|
32
|
+
exports.command = "serve [project_directory]";
|
|
33
|
+
exports.desc = `Start a local HTTP server to serve a project`;
|
|
34
|
+
|
|
35
|
+
exports.builder = (yargs) => {
|
|
36
|
+
yargs.positional(`project_directory`, {
|
|
37
|
+
type: "string",
|
|
38
|
+
describe:
|
|
39
|
+
"Path to a valid kumologica project directory or flow file. (Optional)",
|
|
40
|
+
});
|
|
41
|
+
yargs.option(`loglevel`, {
|
|
42
|
+
describe: "Logging level: [error, warn, info, debug, trace]",
|
|
43
|
+
type: "string",
|
|
44
|
+
nargs: 1,
|
|
45
|
+
});
|
|
46
|
+
yargs.option(`port`, {
|
|
47
|
+
describe: "Specifies the listening port utilized by the application",
|
|
48
|
+
type: "number",
|
|
49
|
+
nargs: 1,
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
exports.handler = async ({ project_directory, loglevel, port, secured }) => {
|
|
54
|
+
logNotice(`Starting HTTP Server...`);
|
|
55
|
+
let projectDirectory = project_directory || process.cwd();
|
|
56
|
+
// project_directory can point to a directory or a flow, so lets make sure that first the directory exists
|
|
57
|
+
let absProjectDirectory;
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
absProjectDirectory = fs.realpathSync(projectDirectory);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
logFatal(`Project not found: ${projectDirectory}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
// Resolve the path to the flow path
|
|
68
|
+
const [projectFlowDirname, projectFlowFullPath] =
|
|
69
|
+
resolveProjectFlowPath(absProjectDirectory);
|
|
70
|
+
logInfo(`> Flow file found: ${projectFlowFullPath}`);
|
|
71
|
+
|
|
72
|
+
// Gather all cli params
|
|
73
|
+
const cliParams = {
|
|
74
|
+
loglevel,
|
|
75
|
+
port,
|
|
76
|
+
noadmin: true,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Start a server
|
|
80
|
+
let server = new DesignerServer({
|
|
81
|
+
flowPath: projectFlowFullPath,
|
|
82
|
+
cliParams,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
await server.start();
|
|
86
|
+
|
|
87
|
+
// open("http://localhost:1880/hello");
|
|
88
|
+
} catch (e) {
|
|
89
|
+
console.log(e);
|
|
90
|
+
logFatal(e.message);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Resolve a given path to a new path that points to the project flow json file.
|
|
96
|
+
* Scenarios:
|
|
97
|
+
* - If path undefined will resolve using current directory
|
|
98
|
+
* - If path directory, it should look up for a valid flow.json file, if it does not exist create it
|
|
99
|
+
* - If path is pointing to an actual flowFile, just return it.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} projectDir
|
|
102
|
+
*/
|
|
103
|
+
function resolveProjectFlowPath(projectDir) {
|
|
104
|
+
// output
|
|
105
|
+
let projectFlowFullPath;
|
|
106
|
+
let projectFlowDirname;
|
|
107
|
+
|
|
108
|
+
let projectDirOrFile = projectDir || process.cwd();
|
|
109
|
+
|
|
110
|
+
let isDir = isDirectory(projectDirOrFile);
|
|
111
|
+
if (isDir) {
|
|
112
|
+
let flowFileName = codegen.findFlowFile(projectDirOrFile); // returns only the flowname
|
|
113
|
+
|
|
114
|
+
if (!flowFileName) {
|
|
115
|
+
logError(`No flow found in directory: ${projectDirOrFile}`);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
} else {
|
|
118
|
+
projectFlowDirname = projectDirOrFile;
|
|
119
|
+
projectFlowFullPath = path.join(projectDirOrFile, flowFileName);
|
|
120
|
+
}
|
|
121
|
+
} else if (isDir === false) {
|
|
122
|
+
projectFlowDirname = path.dirname(projectDirOrFile);
|
|
123
|
+
projectFlowFullPath = projectDirOrFile;
|
|
124
|
+
} else {
|
|
125
|
+
logError(`Directory does not exist: ${projectDirOrFile}`);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return [projectFlowDirname, projectFlowFullPath];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function isDirectory(dir) {
|
|
133
|
+
try {
|
|
134
|
+
let stats = fs.statSync(dir);
|
|
135
|
+
return stats.isDirectory();
|
|
136
|
+
} catch (err) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kumologica/sdk",
|
|
3
|
-
"version": "3.6.0-
|
|
3
|
+
"version": "3.6.0-beta3",
|
|
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>",
|
|
@@ -82,9 +82,9 @@
|
|
|
82
82
|
"@aws-sdk/credential-providers": "^3.556.0",
|
|
83
83
|
"@aws-sdk/lib-dynamodb": "^3.549.0",
|
|
84
84
|
"@electron/remote": "^2.0.8",
|
|
85
|
-
"@kumologica/builder": "3.6.0-
|
|
86
|
-
"@kumologica/devkit": "3.6.0-
|
|
87
|
-
"@kumologica/runtime": "3.6.0-
|
|
85
|
+
"@kumologica/builder": "3.6.0-beta3",
|
|
86
|
+
"@kumologica/devkit": "3.6.0-beta3",
|
|
87
|
+
"@kumologica/runtime": "3.6.0-beta3",
|
|
88
88
|
"adm-zip": "0.4.13",
|
|
89
89
|
"ajv": "8.10.0",
|
|
90
90
|
"archive-type": "^4.0.0",
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
const {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} = require(
|
|
6
|
-
const tcpPortUsed = require(
|
|
2
|
+
AbstractServerfulServer,
|
|
3
|
+
ConfigBuilder,
|
|
4
|
+
PLATFORMS,
|
|
5
|
+
} = require("@kumologica/runtime");
|
|
6
|
+
const tcpPortUsed = require("tcp-port-used");
|
|
7
7
|
|
|
8
|
+
const os = require("os");
|
|
8
9
|
/**
|
|
9
10
|
* This class will encapsulate the adminApp and FlowApp into two servers.
|
|
10
11
|
* It will be the one running on the development box of the user (either local or remote),
|
|
@@ -12,155 +13,153 @@ const tcpPortUsed = require('tcp-port-used');
|
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
class DesignerServer {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
16
|
+
/**
|
|
17
|
+
* @param {Object} options - Configuration options.
|
|
18
|
+
* @param {string} options.flowPath - The path of the flow.
|
|
19
|
+
* @param {boolean} options.safe - A boolean indicating safety.
|
|
20
|
+
* @param {Object} options.cliParams - Command line parameters as an object.
|
|
21
|
+
* @param {Object} options.editorApi - API object for the editor.
|
|
22
|
+
* @param {Object} options.startupEmitter - EventEmitter object for handling events.
|
|
23
|
+
* @param {string} options.platform - The platform to run on. Default LOCAL.
|
|
24
|
+
*/
|
|
25
|
+
constructor(options) {
|
|
26
|
+
if (!options.flowPath) {
|
|
27
|
+
console.log(
|
|
28
|
+
"[ERROR] Flow is not defined. Make sure you pass the flow file to the constructor of FlowBuilder"
|
|
29
|
+
);
|
|
30
|
+
os.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this.editorApi = options.editorApi;
|
|
34
|
+
this.startupEmitter = options.startupEmitter;
|
|
35
|
+
|
|
36
|
+
this.config = ConfigBuilder.getInstance().buildConfig(
|
|
37
|
+
options.flowPath,
|
|
38
|
+
options.platform || PLATFORMS.LOCAL,
|
|
39
|
+
options.cliParams
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async start() {
|
|
44
|
+
// Check if port passed by parameter is available otherwise allocate a new one
|
|
45
|
+
this.config.port = await this.allocatePort(this.config.port);
|
|
46
|
+
this.flowServer = new AbstractServerfulServer(
|
|
47
|
+
this.config,
|
|
48
|
+
this.editorApi,
|
|
49
|
+
this.startupEmitter
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
await this.flowServer.start();
|
|
53
|
+
this.flowServer.log.debug(
|
|
54
|
+
`Runtime Configuration:${JSON.stringify(this.config, null, 2)}`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async allocatePort(port) {
|
|
59
|
+
let inUse = await tcpPortUsed.check(port);
|
|
60
|
+
if (inUse) {
|
|
61
|
+
let newPort = port + 1;
|
|
62
|
+
console.log(`> Port: ${port} in use. Trying with port: ${newPort}...`);
|
|
63
|
+
return await this.allocatePort(newPort);
|
|
64
|
+
} else {
|
|
65
|
+
return port;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getFlowServer() {
|
|
70
|
+
return this.flowServer;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
getListeningPort() {
|
|
74
|
+
return this.flowServer.serverSettings.port;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async stop() {
|
|
78
|
+
await this.flowServer.stop();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The express application for HTTP Nodes
|
|
83
|
+
*/
|
|
84
|
+
get httpNode() {
|
|
85
|
+
return this.flowServer.httpNode.expressApp;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Pointer to the internal HTTP Server upon which all the express applications (Nodes and Admin) are deployed upon
|
|
90
|
+
*
|
|
91
|
+
* @readonly
|
|
92
|
+
*/
|
|
93
|
+
get server() {
|
|
94
|
+
return this.flowServer.server;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Access to internal runtime api
|
|
99
|
+
*/
|
|
100
|
+
get runtime() {
|
|
101
|
+
return this.flowServer.runtime;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
*
|
|
106
|
+
* Returns the version of the runtime lib used internally
|
|
107
|
+
* @readonly
|
|
108
|
+
* @static
|
|
109
|
+
*/
|
|
110
|
+
static get version() {
|
|
111
|
+
return AbstractServerfulServer.version;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* This provides access to the internal settings module of the
|
|
116
|
+
* runtime.
|
|
117
|
+
* This is the object derived from the server settings.
|
|
118
|
+
*/
|
|
119
|
+
get settings() {
|
|
120
|
+
return this.flowServer._.settings;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* EventEmitter use to publish and subscribe to internal runtime events
|
|
125
|
+
* For example:
|
|
126
|
+
* runtime.events.on(
|
|
127
|
+
* 'runtime-event',
|
|
128
|
+
* (flowStarted = async data => {
|
|
129
|
+
* if (data.id === 'flow-started') {
|
|
130
|
+
* runtime.events.removeListener('runtime-event', flowStarted);
|
|
131
|
+
*
|
|
132
|
+
* @readonly
|
|
133
|
+
*/
|
|
134
|
+
get events() {
|
|
135
|
+
return this.flowServer.events;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
get eventTypes() {
|
|
139
|
+
return this.flowServer.runtimeEventTypes;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* This provides access to the internal nodes module of the
|
|
144
|
+
* runtime. The details of this API remain undocumented as they should not
|
|
145
|
+
* be used directly.
|
|
146
|
+
*
|
|
147
|
+
* Most administrative actions should be performed use the runtime api
|
|
148
|
+
* under [node.runtime]
|
|
149
|
+
*/
|
|
150
|
+
get nodes() {
|
|
151
|
+
return this.flowServer._.nodes;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
get log() {
|
|
155
|
+
return this.flowServer.log;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
get util() {
|
|
159
|
+
return this.flowServer.util;
|
|
160
|
+
}
|
|
162
161
|
}
|
|
163
162
|
|
|
164
163
|
module.exports = {
|
|
165
|
-
|
|
164
|
+
DesignerServer,
|
|
166
165
|
};
|