systemview 1.20.1 → 2.0.0
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/api/connections.json +1 -1
- package/api/index.js +42 -3
- package/build/asset-manifest.json +8 -6
- package/build/index.html +1 -1
- package/build/static/css/main.ff3d0549.css +15 -0
- package/build/static/css/main.ff3d0549.css.map +1 -0
- package/build/static/js/422.05c538a9.chunk.js +2 -0
- package/build/static/js/422.05c538a9.chunk.js.map +1 -0
- package/build/static/js/main.72103448.js +3 -0
- package/build/static/js/{main.c3485d98.js.LICENSE.txt → main.72103448.js.LICENSE.txt} +26 -48
- package/build/static/js/main.72103448.js.map +1 -0
- package/cli/connectService.js +4 -3
- package/cli/cookieClient.js +1 -4
- package/cli/index.js +23 -3
- package/cli/launchApp.js +1 -0
- package/cli/listTests.js +6 -3
- package/cli/logs.js +219 -0
- package/cli/probe.js +7 -2
- package/cli/runTests.js +6 -5
- package/cli/startLineReader.js +29 -9
- package/cli/utils/cli.js +28 -1
- package/package.json +3 -3
- package/testing-utilities/Test.class.js +2 -1
- package/testing-utilities/transformTests.js +7 -6
- package/build/static/css/main.3396e24d.css +0 -15
- package/build/static/css/main.3396e24d.css.map +0 -1
- package/build/static/js/main.c3485d98.js +0 -3
- package/build/static/js/main.c3485d98.js.map +0 -1
package/cli/connectService.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
|
-
const { HttpClient } = require("systemlynx");
|
|
4
|
-
const {
|
|
5
|
-
const
|
|
3
|
+
const { HttpClient, createClient } = require("systemlynx");
|
|
4
|
+
const { createCookieHttpClient } = require("./cookieClient");
|
|
5
|
+
const cookieHttpClient = createCookieHttpClient();
|
|
6
|
+
const Client = createClient(cookieHttpClient);
|
|
6
7
|
const log = require("./logger");
|
|
7
8
|
|
|
8
9
|
async function probeService(url) {
|
package/cli/cookieClient.js
CHANGED
|
@@ -87,7 +87,4 @@ function createCookieHttpClient(extraHeaders = {}) {
|
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
module.exports = {
|
|
91
|
-
createCookieHttpClient,
|
|
92
|
-
createCookieClient: (extraHeaders = {}) => createClient(createCookieHttpClient(extraHeaders)),
|
|
93
|
-
};
|
|
90
|
+
module.exports = { createCookieHttpClient };
|
package/cli/index.js
CHANGED
|
@@ -14,8 +14,11 @@ const appIsRunning = require("./appIsRunning");
|
|
|
14
14
|
const openBrowser = require("./openBrowser");
|
|
15
15
|
const connectService = require("./connectService");
|
|
16
16
|
const probe = require("./probe");
|
|
17
|
-
const
|
|
18
|
-
const {
|
|
17
|
+
const logsCommand = require("./logs");
|
|
18
|
+
const { HttpClient, createClient } = require("systemlynx");
|
|
19
|
+
const { createCookieHttpClient } = require("./cookieClient");
|
|
20
|
+
const cookieHttpClient = createCookieHttpClient();
|
|
21
|
+
const Client = createClient(cookieHttpClient);
|
|
19
22
|
|
|
20
23
|
const DEFAULT_PORT = 3000;
|
|
21
24
|
const VERSION = require("../package.json").version;
|
|
@@ -84,7 +87,7 @@ async function open() {
|
|
|
84
87
|
} catch {}
|
|
85
88
|
} else {
|
|
86
89
|
try {
|
|
87
|
-
const { SystemView } = await
|
|
90
|
+
const { SystemView } = await Client.loadService(api);
|
|
88
91
|
connectedServices = (await SystemView.getServices(project_code)) || [];
|
|
89
92
|
} catch {}
|
|
90
93
|
}
|
|
@@ -149,6 +152,23 @@ async function quitApp() {
|
|
|
149
152
|
headers: flags.headers,
|
|
150
153
|
});
|
|
151
154
|
process.exit(exitCode || 0);
|
|
155
|
+
} else if (command === "logs") {
|
|
156
|
+
const url = `http://localhost:${DEFAULT_PORT}`;
|
|
157
|
+
const api = `${url}/systemview/api`;
|
|
158
|
+
if (!(await appIsRunning(api))) {
|
|
159
|
+
await launchApp(DEFAULT_PORT);
|
|
160
|
+
}
|
|
161
|
+
await logsCommand(url, input[1], input[2], {
|
|
162
|
+
level: flags.level,
|
|
163
|
+
limit: flags.limit,
|
|
164
|
+
clear: flags.clear,
|
|
165
|
+
json: flags.json,
|
|
166
|
+
follow: command === "follow" || flags.follow,
|
|
167
|
+
verbose: flags.verbose,
|
|
168
|
+
filter: flags.filter,
|
|
169
|
+
or: flags.or,
|
|
170
|
+
});
|
|
171
|
+
process.exit(0);
|
|
152
172
|
} else {
|
|
153
173
|
await startApp();
|
|
154
174
|
}
|
package/cli/launchApp.js
CHANGED
package/cli/listTests.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
|
-
const {
|
|
3
|
+
const { createClient } = require("systemlynx");
|
|
4
|
+
const { createCookieHttpClient } = require("./cookieClient");
|
|
4
5
|
const log = require("./logger");
|
|
5
6
|
const chalk = require("chalk");
|
|
6
7
|
|
|
8
|
+
const cookieHttpClient = createCookieHttpClient();
|
|
9
|
+
const Client = createClient(cookieHttpClient);
|
|
10
|
+
|
|
7
11
|
module.exports = async function listTests(url, project_code, namespace, { manifest: manifestPath, verbose = false } = {}) {
|
|
8
12
|
const api = `${url}/systemview/api`;
|
|
9
|
-
const Client = createCookieClient();
|
|
10
13
|
|
|
11
14
|
if (!project_code) {
|
|
12
15
|
await listAllProjects(api, Client, verbose);
|
|
@@ -30,7 +33,7 @@ module.exports = async function listTests(url, project_code, namespace, { manife
|
|
|
30
33
|
|
|
31
34
|
let testList = [];
|
|
32
35
|
try {
|
|
33
|
-
const svc =
|
|
36
|
+
const svc = Client.createService(system.connectionData);
|
|
34
37
|
testList = await svc.Plugin.getTests();
|
|
35
38
|
} catch {
|
|
36
39
|
console.log(`${servicePrefix}${chalk.dim(serviceId)} ${chalk.dim("(could not load tests)")}`);
|
package/cli/logs.js
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const chalk = require("chalk");
|
|
4
|
+
const log = require("./logger");
|
|
5
|
+
const { createClient } = require("systemlynx");
|
|
6
|
+
const { createCookieHttpClient } = require("./cookieClient");
|
|
7
|
+
const cookieHttpClient = createCookieHttpClient();
|
|
8
|
+
const Client = createClient(cookieHttpClient);
|
|
9
|
+
|
|
10
|
+
const LEVEL_COLOR = {
|
|
11
|
+
trace: (s) => chalk.dim(s),
|
|
12
|
+
info: (s) => chalk.white(s),
|
|
13
|
+
warn: (s) => chalk.yellow(s),
|
|
14
|
+
error: (s) => chalk.red(s),
|
|
15
|
+
debug: (s) => chalk.blue(s),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function colorLevel(level) {
|
|
19
|
+
const fn = LEVEL_COLOR[level] || ((s) => s);
|
|
20
|
+
return fn((level || "info").padEnd(5));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const SKIP_KEYS = new Set([
|
|
24
|
+
"timestamp",
|
|
25
|
+
"projectCode",
|
|
26
|
+
"serviceId",
|
|
27
|
+
"moduleMethod",
|
|
28
|
+
"traceId",
|
|
29
|
+
"level",
|
|
30
|
+
"message",
|
|
31
|
+
"duration",
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
function formatRow(entry, verbose) {
|
|
35
|
+
const d = new Date(entry.timestamp);
|
|
36
|
+
const time =
|
|
37
|
+
d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }) +
|
|
38
|
+
" " +
|
|
39
|
+
d.toLocaleTimeString("en-US", { hour12: false });
|
|
40
|
+
const project = entry.projectCode || "—";
|
|
41
|
+
const service = entry.serviceId || "—";
|
|
42
|
+
const method = entry.moduleMethod || "—";
|
|
43
|
+
const level = colorLevel(entry.level);
|
|
44
|
+
const msg = entry.message || "";
|
|
45
|
+
const dur = entry.duration != null ? chalk.dim(` ${entry.duration}ms`) : "";
|
|
46
|
+
const traceId = chalk.dim(entry.traceId || "—");
|
|
47
|
+
const main = ` ${chalk.dim(time)} ${chalk.cyan(project)} › ${chalk.cyan(service)} ${method.padEnd(15)} ${level} ${traceId} ${msg}${dur}`;
|
|
48
|
+
if (verbose) {
|
|
49
|
+
const extra = {};
|
|
50
|
+
Object.keys(entry).forEach((k) => {
|
|
51
|
+
if (!SKIP_KEYS.has(k)) extra[k] = entry[k];
|
|
52
|
+
});
|
|
53
|
+
if (Object.keys(extra).length) {
|
|
54
|
+
return (
|
|
55
|
+
main +
|
|
56
|
+
"\n" +
|
|
57
|
+
JSON.stringify(extra, null, 2)
|
|
58
|
+
.split("\n")
|
|
59
|
+
.map((l) => " " + chalk.yellow(l))
|
|
60
|
+
.join("\n")
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return main;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function parseFilters(filters) {
|
|
68
|
+
return (filters || [])
|
|
69
|
+
.map((f) => {
|
|
70
|
+
const eq = f.indexOf("=");
|
|
71
|
+
if (eq === -1) return null;
|
|
72
|
+
return { field: f.slice(0, eq), value: f.slice(eq + 1) };
|
|
73
|
+
})
|
|
74
|
+
.filter(Boolean);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function matchesFilters(entry, andFilters, orFilters) {
|
|
78
|
+
const check = ({ field, value }) => {
|
|
79
|
+
const v = entry[field];
|
|
80
|
+
return v != null && String(v).includes(value);
|
|
81
|
+
};
|
|
82
|
+
const andPass = andFilters.length === 0 || andFilters.every(check);
|
|
83
|
+
const orPass = orFilters.length === 0 || orFilters.some(check);
|
|
84
|
+
if (andFilters.length && orFilters.length) return andPass || orPass;
|
|
85
|
+
return andPass && orPass;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = async function logsCommand(
|
|
89
|
+
url,
|
|
90
|
+
projectCode,
|
|
91
|
+
namespace,
|
|
92
|
+
{ level, limit = 50, clear, json, follow, current, verbose, filter, or: orFilters },
|
|
93
|
+
) {
|
|
94
|
+
const andFilters = parseFilters(filter);
|
|
95
|
+
const orFiltersParsed = parseFilters(orFilters);
|
|
96
|
+
|
|
97
|
+
if (follow) {
|
|
98
|
+
const manifestFile = path.join(process.cwd(), "systemview.manifest.json");
|
|
99
|
+
if (!fs.existsSync(manifestFile)) {
|
|
100
|
+
log.warn("No manifest found. Use: systemview connect <serviceId> <url>");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const manifest = JSON.parse(fs.readFileSync(manifestFile, "utf8"));
|
|
104
|
+
const services = manifest.services || [manifest];
|
|
105
|
+
if (!services.length) {
|
|
106
|
+
log.warn("No services in manifest. Use: systemview connect <serviceId> <url>");
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
console.log("");
|
|
111
|
+
console.log(chalk.bold(" Streaming logs"));
|
|
112
|
+
console.log("");
|
|
113
|
+
|
|
114
|
+
console.log(chalk.dim(` Services (${services.length}):`));
|
|
115
|
+
const connected = [];
|
|
116
|
+
for (const { serviceId, system } of services) {
|
|
117
|
+
const { serviceUrl } = system.connectionData;
|
|
118
|
+
try {
|
|
119
|
+
const svc = await Client.loadService(serviceUrl);
|
|
120
|
+
svc.SystemViewLogs.on("log", (entry) => {
|
|
121
|
+
if (namespace && !(entry.moduleMethod && entry.moduleMethod.includes(namespace))) return;
|
|
122
|
+
if (andFilters.length || orFiltersParsed.length) {
|
|
123
|
+
if (!matchesFilters(entry, andFilters, orFiltersParsed)) return;
|
|
124
|
+
}
|
|
125
|
+
if (level && entry.level !== level) return;
|
|
126
|
+
console.log(formatRow(entry, verbose));
|
|
127
|
+
});
|
|
128
|
+
console.log(` ${chalk.green("✓")} ${chalk.cyan(serviceId).padEnd(20)} ${chalk.dim(serviceUrl)}`);
|
|
129
|
+
connected.push(serviceId);
|
|
130
|
+
} catch (err) {
|
|
131
|
+
console.log(` ${chalk.red("✗")} ${chalk.cyan(serviceId).padEnd(20)} ${chalk.dim(serviceUrl)} ${chalk.red("(failed to connect)")}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const activeFilters = [];
|
|
136
|
+
if (namespace) activeFilters.push(`namespace: ${chalk.white(namespace)}`);
|
|
137
|
+
if (level) activeFilters.push(`level: ${chalk.white(level)}`);
|
|
138
|
+
if (andFilters.length) activeFilters.push(`filter: ${chalk.white(andFilters.map((f) => `${f.field}=${f.value}`).join(", "))}`);
|
|
139
|
+
if (orFiltersParsed.length) activeFilters.push(`or: ${chalk.white(orFiltersParsed.map((f) => `${f.field}=${f.value}`).join(", "))}`);
|
|
140
|
+
if (verbose) activeFilters.push(`verbose: ${chalk.white("on")}`);
|
|
141
|
+
|
|
142
|
+
if (activeFilters.length) {
|
|
143
|
+
console.log("");
|
|
144
|
+
console.log(chalk.dim(` Filters:`));
|
|
145
|
+
activeFilters.forEach((f) => console.log(` ${chalk.dim(f)}`));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
console.log("");
|
|
149
|
+
|
|
150
|
+
if (!connected.length) {
|
|
151
|
+
log.warn("No services connected.");
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (current) {
|
|
156
|
+
try {
|
|
157
|
+
const api = `${url}/systemview/api`;
|
|
158
|
+
const { SystemView } = await Client.loadService(api);
|
|
159
|
+
const all = await SystemView.getLogs({ projectCode: manifest.projectCode, level, limit });
|
|
160
|
+
let entries = all || [];
|
|
161
|
+
if (namespace) entries = entries.filter((e) => e.moduleMethod && e.moduleMethod.includes(namespace));
|
|
162
|
+
if (andFilters.length || orFiltersParsed.length) entries = entries.filter((e) => matchesFilters(e, andFilters, orFiltersParsed));
|
|
163
|
+
if (entries.length) {
|
|
164
|
+
console.log(chalk.dim(` ── current (${entries.length}) ──`));
|
|
165
|
+
entries.forEach((e) => console.log(formatRow(e, verbose)));
|
|
166
|
+
console.log(chalk.dim(` ── streaming ──`));
|
|
167
|
+
}
|
|
168
|
+
} catch (err) {
|
|
169
|
+
log.warn("Could not fetch current logs: " + err.message);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const api = `${url}/systemview/api`;
|
|
177
|
+
const { SystemView } = await Client.loadService(api);
|
|
178
|
+
|
|
179
|
+
if (clear) {
|
|
180
|
+
const confirmed = await promptConfirm("Clear all logs? (y/N) ");
|
|
181
|
+
if (!confirmed) { log.warn("Aborted."); return; }
|
|
182
|
+
await SystemView.clearLogs();
|
|
183
|
+
log.success("Logs cleared.");
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const entries = await (async () => {
|
|
188
|
+
const all = await SystemView.getLogs({ projectCode, level, limit });
|
|
189
|
+
let filtered = all || [];
|
|
190
|
+
if (namespace) filtered = filtered.filter((e) => e.moduleMethod && e.moduleMethod.includes(namespace));
|
|
191
|
+
if (andFilters.length || orFiltersParsed.length) filtered = filtered.filter((e) => matchesFilters(e, andFilters, orFiltersParsed));
|
|
192
|
+
return filtered;
|
|
193
|
+
})();
|
|
194
|
+
|
|
195
|
+
if (json) {
|
|
196
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (!entries.length) {
|
|
201
|
+
log.warn("No logs found.");
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
console.log("");
|
|
206
|
+
entries.forEach((e) => console.log(formatRow(e, verbose)));
|
|
207
|
+
console.log("");
|
|
208
|
+
log.plain(chalk.dim(` ${entries.length} entries`));
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
function promptConfirm(question) {
|
|
212
|
+
return new Promise((resolve) => {
|
|
213
|
+
process.stdout.write(question);
|
|
214
|
+
process.stdin.setEncoding("utf8");
|
|
215
|
+
process.stdin.once("data", (data) => {
|
|
216
|
+
resolve(data.trim().toLowerCase() === "y");
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
}
|
package/cli/probe.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
|
-
const {
|
|
3
|
+
const { createClient } = require("systemlynx");
|
|
4
|
+
const { createCookieHttpClient } = require("./cookieClient");
|
|
4
5
|
const log = require("./logger");
|
|
5
6
|
|
|
7
|
+
const cookieHttpClient = createCookieHttpClient();
|
|
8
|
+
const Client = createClient(cookieHttpClient);
|
|
9
|
+
|
|
6
10
|
module.exports = async function probe(namespace, argsStr, { json = false, manifest: manifestPath, headers: cliHeaders = {} } = {}) {
|
|
7
11
|
if (!namespace) {
|
|
8
12
|
log.error("Usage: systemview probe <ServiceId.Module.method> [args]");
|
|
@@ -52,7 +56,8 @@ module.exports = async function probe(namespace, argsStr, { json = false, manife
|
|
|
52
56
|
if (!json) log.info(`${serviceId}.${moduleName}.${methodName}(${argsStr || ""})`);
|
|
53
57
|
|
|
54
58
|
try {
|
|
55
|
-
const client =
|
|
59
|
+
const client = Client.createService(service.system.connectionData);
|
|
60
|
+
if (Object.keys(extraHeaders).length) client.setHeaders(extraHeaders);
|
|
56
61
|
const result = await client[moduleName][methodName](...args);
|
|
57
62
|
if (json) {
|
|
58
63
|
process.stdout.write(JSON.stringify({ serviceId, moduleName, methodName, args, result }, null, 2) + "\n");
|
package/cli/runTests.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
|
-
const {
|
|
3
|
+
const { createClient } = require("systemlynx");
|
|
4
|
+
const { createCookieHttpClient } = require("./cookieClient");
|
|
5
|
+
const cookieHttpClient = createCookieHttpClient();
|
|
6
|
+
const Client = createClient(cookieHttpClient);
|
|
4
7
|
const { initializeSavedTests } = require("../testing-utilities/transformTests");
|
|
5
8
|
const FullTestController = require("../testing-utilities/FullTestController");
|
|
6
9
|
const log = require("./logger");
|
|
@@ -72,7 +75,6 @@ module.exports = async function runTests(
|
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
const extraHeaders = { ...manifestHeaders, ...cliHeaders };
|
|
75
|
-
const Client = createCookieClient(extraHeaders);
|
|
76
78
|
|
|
77
79
|
if (!json) {
|
|
78
80
|
connectedServices.forEach(({ serviceId, system }) => {
|
|
@@ -121,7 +123,7 @@ module.exports = async function runTests(
|
|
|
121
123
|
if (!json) log.info(`Initializing tests for ${serviceId}...`);
|
|
122
124
|
|
|
123
125
|
try {
|
|
124
|
-
const initialized = initializeSavedTests(testList, connectedServices, Client);
|
|
126
|
+
const initialized = initializeSavedTests(testList, connectedServices, Client, extraHeaders);
|
|
125
127
|
const { passed, failed, jsonTests, failures } = await runAllTests(
|
|
126
128
|
initialized,
|
|
127
129
|
url,
|
|
@@ -376,8 +378,7 @@ async function resolveServices(api, project_code, manifestPath) {
|
|
|
376
378
|
}
|
|
377
379
|
}
|
|
378
380
|
|
|
379
|
-
const
|
|
380
|
-
const services = await getConnectedServices(api, project_code, BaseClient);
|
|
381
|
+
const services = await getConnectedServices(api, project_code, Client);
|
|
381
382
|
return { services, probeHeaders: {} };
|
|
382
383
|
}
|
|
383
384
|
|
package/cli/startLineReader.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const runTests = require("./runTests");
|
|
2
2
|
const cli = require("./utils/cli");
|
|
3
3
|
const openBrowser = require("./openBrowser");
|
|
4
|
+
const logsCommand = require("./logs");
|
|
4
5
|
const readline = require("readline");
|
|
5
6
|
|
|
6
7
|
module.exports = function startLineReader(url) {
|
|
@@ -8,23 +9,42 @@ module.exports = function startLineReader(url) {
|
|
|
8
9
|
input: process.stdin,
|
|
9
10
|
output: process.stdout,
|
|
10
11
|
});
|
|
12
|
+
|
|
11
13
|
const handleInput = (input = "") => {
|
|
12
|
-
const
|
|
13
|
-
const command =
|
|
14
|
+
const parts = input.trim().split(/\s+/);
|
|
15
|
+
const command = parts[0];
|
|
16
|
+
const args = parts.slice(1);
|
|
17
|
+
|
|
18
|
+
const flags = { filter: [], or: [] };
|
|
19
|
+
const positional = [];
|
|
20
|
+
for (let i = 0; i < args.length; i++) {
|
|
21
|
+
if (args[i] === "--level" && args[i + 1]) { flags.level = args[++i]; }
|
|
22
|
+
else if (args[i] === "--limit" && args[i + 1]) { flags.limit = parseInt(args[++i], 10); }
|
|
23
|
+
else if (args[i] === "--filter" && args[i + 1]) { flags.filter.push(args[++i]); }
|
|
24
|
+
else if (args[i] === "--or" && args[i + 1]) { flags.or.push(args[++i]); }
|
|
25
|
+
else if (args[i] === "--verbose") { flags.verbose = true; }
|
|
26
|
+
else if (args[i] === "--current") { flags.current = true; }
|
|
27
|
+
else if (args[i] === "--json") { flags.json = true; }
|
|
28
|
+
else if (!args[i].startsWith("--")) { positional.push(args[i]); }
|
|
29
|
+
}
|
|
30
|
+
|
|
14
31
|
if (["exit", "q", "shutdown", "stop"].includes(command)) {
|
|
15
32
|
process.exit(0);
|
|
16
33
|
} else if (command === "test") {
|
|
17
|
-
try {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
34
|
+
try { runTests(url, positional[0], positional[1]); } catch (e) { console.error(e.message); }
|
|
35
|
+
lineReader.prompt();
|
|
36
|
+
} else if (command === "logs") {
|
|
37
|
+
logsCommand(url, positional[0], positional[1], { ...flags, follow: true, current: flags.current });
|
|
38
|
+
} else if (command === "clearlogs" || command === "flush") {
|
|
39
|
+
logsCommand(url, null, null, { clear: true, follow: false });
|
|
22
40
|
} else if (command === "help") {
|
|
23
41
|
cli.showHelp(0);
|
|
24
42
|
} else if (command === "open") {
|
|
25
|
-
openBrowser(url,
|
|
43
|
+
openBrowser(url, positional[0], positional[1]);
|
|
44
|
+
lineReader.prompt();
|
|
45
|
+
} else {
|
|
46
|
+
lineReader.prompt();
|
|
26
47
|
}
|
|
27
|
-
lineReader.prompt();
|
|
28
48
|
};
|
|
29
49
|
|
|
30
50
|
lineReader.prompt();
|
package/cli/utils/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ const HELP_TEXT = `
|
|
|
8
8
|
start [port] Launch SystemView UI (default port 3000)
|
|
9
9
|
test <projectCode> [namespace] Run saved tests for a project
|
|
10
10
|
list [projectCode] [namespace] List projects, services, or tests
|
|
11
|
+
logs [projectCode] [namespace] View log entries
|
|
11
12
|
connect <serviceId> <url> Register a service and write manifest
|
|
12
13
|
connect Re-probe all services in existing manifest
|
|
13
14
|
probe <ServiceId.Module.method> [args] Call a method ad-hoc
|
|
@@ -25,6 +26,10 @@ const HELP_TEXT = `
|
|
|
25
26
|
--dry-run Print which tests would run without executing
|
|
26
27
|
--phase <before|main|events|after> Run only specified phase(s), comma-separated
|
|
27
28
|
--index <n> Run only action at index n within each phase (0-based)
|
|
29
|
+
--level <trace|info|warn|error|debug> logs: filter by level
|
|
30
|
+
--limit <n> logs: max entries to return (default 50)
|
|
31
|
+
--follow logs: stream new entries as they arrive
|
|
32
|
+
--clear logs: wipe the log file
|
|
28
33
|
|
|
29
34
|
Examples:
|
|
30
35
|
systemview start
|
|
@@ -44,7 +49,7 @@ const HELP_TEXT = `
|
|
|
44
49
|
|
|
45
50
|
const rawArgs = process.argv.slice(2);
|
|
46
51
|
|
|
47
|
-
const flagValueArgs = ["--manifest", "--header", "--skip", "--phase", "--index"];
|
|
52
|
+
const flagValueArgs = ["--manifest", "--header", "--skip", "--phase", "--index", "--level", "--limit", "--follow", "--filter", "--or"];
|
|
48
53
|
|
|
49
54
|
const flags = {
|
|
50
55
|
json: rawArgs.includes("--json"),
|
|
@@ -73,6 +78,28 @@ const flags = {
|
|
|
73
78
|
});
|
|
74
79
|
return result;
|
|
75
80
|
})(),
|
|
81
|
+
level: (() => {
|
|
82
|
+
const i = rawArgs.indexOf("--level");
|
|
83
|
+
return i !== -1 ? rawArgs[i + 1] : null;
|
|
84
|
+
})(),
|
|
85
|
+
limit: (() => {
|
|
86
|
+
const i = rawArgs.indexOf("--limit");
|
|
87
|
+
if (i === -1) return undefined;
|
|
88
|
+
const val = parseInt(rawArgs[i + 1], 10);
|
|
89
|
+
return isNaN(val) ? undefined : val;
|
|
90
|
+
})(),
|
|
91
|
+
follow: rawArgs.includes("--follow") || rawArgs.includes("-f"),
|
|
92
|
+
filter: (() => {
|
|
93
|
+
const result = [];
|
|
94
|
+
rawArgs.forEach((a, i) => { if (a === "--filter" && rawArgs[i + 1]) result.push(rawArgs[i + 1]); });
|
|
95
|
+
return result;
|
|
96
|
+
})(),
|
|
97
|
+
or: (() => {
|
|
98
|
+
const result = [];
|
|
99
|
+
rawArgs.forEach((a, i) => { if (a === "--or" && rawArgs[i + 1]) result.push(rawArgs[i + 1]); });
|
|
100
|
+
return result;
|
|
101
|
+
})(),
|
|
102
|
+
clear: rawArgs.includes("--clear"),
|
|
76
103
|
headers: (() => {
|
|
77
104
|
const result = {};
|
|
78
105
|
rawArgs.forEach((a, i) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "systemview",
|
|
3
3
|
"description": "A documentation and testing suite for SystemLynx",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.0.0",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"bin": {
|
|
7
7
|
"systemview": "cli/index.js"
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"chalk": "^4.1.2",
|
|
17
17
|
"express": "^4.18.2",
|
|
18
18
|
"moment": "^2.29.1",
|
|
19
|
-
"systemlynx": "^
|
|
20
|
-
"systemlynx-client": "^
|
|
19
|
+
"systemlynx": "^2.0.0",
|
|
20
|
+
"systemlynx-client": "^2.0.0",
|
|
21
21
|
"web-vitals": "^0.2.4"
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
@@ -13,6 +13,7 @@ module.exports = function Test({
|
|
|
13
13
|
editMode = true,
|
|
14
14
|
logger,
|
|
15
15
|
client,
|
|
16
|
+
extraHeaders,
|
|
16
17
|
}) {
|
|
17
18
|
this.index = index;
|
|
18
19
|
this.connection = {};
|
|
@@ -101,7 +102,7 @@ module.exports = function Test({
|
|
|
101
102
|
const { connectionData } = service.system;
|
|
102
103
|
|
|
103
104
|
this.connection[serviceId] = (client || Client).createService(connectionData);
|
|
104
|
-
this.connection[serviceId].setHeaders({ Origin: `http://localhost:${3000}
|
|
105
|
+
this.connection[serviceId].setHeaders({ Origin: `http://localhost:${3000}`, ...(extraHeaders || {}) });
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
return this;
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
const { Argument } = require("./Argument.class");
|
|
2
2
|
const Test = require("./Test.class");
|
|
3
3
|
|
|
4
|
-
function initializeSavedTests(savedTests, connectedServices, client) {
|
|
4
|
+
function initializeSavedTests(savedTests, connectedServices, client, extraHeaders) {
|
|
5
5
|
return savedTests.map((ft) => {
|
|
6
6
|
// context matters
|
|
7
7
|
const newTests = [];
|
|
8
8
|
const { title, namespace } = ft;
|
|
9
9
|
|
|
10
10
|
const Before = ft.Before.map((test) =>
|
|
11
|
-
resetTestClass(test, newTests, connectedServices, false, client)
|
|
11
|
+
resetTestClass(test, newTests, connectedServices, false, client, extraHeaders)
|
|
12
12
|
);
|
|
13
13
|
const Main = ft.Main.map((test) =>
|
|
14
|
-
resetTestClass(test, newTests, connectedServices, false, client)
|
|
14
|
+
resetTestClass(test, newTests, connectedServices, false, client, extraHeaders)
|
|
15
15
|
);
|
|
16
16
|
const Events = ft.Events.map((test) =>
|
|
17
|
-
resetTestClass(test, newTests, connectedServices, false, client)
|
|
17
|
+
resetTestClass(test, newTests, connectedServices, false, client, extraHeaders)
|
|
18
18
|
);
|
|
19
19
|
const After = ft.After.map((test) =>
|
|
20
|
-
resetTestClass(test, newTests, connectedServices, false, client)
|
|
20
|
+
resetTestClass(test, newTests, connectedServices, false, client, extraHeaders)
|
|
21
21
|
);
|
|
22
22
|
newTests.push(Before);
|
|
23
23
|
newTests.push(Main);
|
|
@@ -27,7 +27,7 @@ function initializeSavedTests(savedTests, connectedServices, client) {
|
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
const resetTestClass = (test, FullTest, connectedServices, editMode, client) => {
|
|
30
|
+
const resetTestClass = (test, FullTest, connectedServices, editMode, client, extraHeaders) => {
|
|
31
31
|
return new Test({
|
|
32
32
|
...test,
|
|
33
33
|
args: test.args.map(
|
|
@@ -36,6 +36,7 @@ const resetTestClass = (test, FullTest, connectedServices, editMode, client) =>
|
|
|
36
36
|
),
|
|
37
37
|
editMode,
|
|
38
38
|
client,
|
|
39
|
+
extraHeaders,
|
|
39
40
|
}).getConnection(connectedServices);
|
|
40
41
|
};
|
|
41
42
|
|