systemview 1.18.1 → 1.20.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.
@@ -40,12 +40,13 @@ function cookieHeader() {
40
40
  return pairs || null;
41
41
  }
42
42
 
43
- function createCookieHttpClient() {
43
+ function createCookieHttpClient(extraHeaders = {}) {
44
44
  return {
45
45
  request: async ({ method = "get", url, body: data, headers }) => {
46
46
  method = method.toLowerCase();
47
47
  const cookie = cookieHeader();
48
- if (cookie) headers = { ...headers, Cookie: cookie };
48
+ headers = { ...headers, ...extraHeaders };
49
+ if (cookie) headers.Cookie = cookie;
49
50
  try {
50
51
  const res = await axios({ url, method, headers, data });
51
52
  captureCookies(res.headers["set-cookie"]);
@@ -66,6 +67,7 @@ function createCookieHttpClient() {
66
67
  if (__arguments) form.append("__arguments", JSON.stringify(__arguments));
67
68
  const cookie = cookieHeader();
68
69
  const allHeaders = {
70
+ ...extraHeaders,
69
71
  ...(cookie ? { Cookie: cookie } : {}),
70
72
  ...headers,
71
73
  "Content-Type": "multipart/form-data",
@@ -87,5 +89,5 @@ function createCookieHttpClient() {
87
89
 
88
90
  module.exports = {
89
91
  createCookieHttpClient,
90
- createCookieClient: () => createClient(createCookieHttpClient()),
92
+ createCookieClient: (extraHeaders = {}) => createClient(createCookieHttpClient(extraHeaders)),
91
93
  };
package/cli/index.js CHANGED
@@ -2,18 +2,23 @@
2
2
 
3
3
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
4
4
 
5
+ const fs = require("fs");
6
+ const path = require("path");
5
7
  const { input, flags, showHelp } = require("./utils/cli");
6
8
  const init = require("./utils/init");
7
9
  const log = require("./logger");
8
10
  const launchApp = require("./launchApp");
9
11
  const runTests = require("./runTests");
12
+ const listTests = require("./listTests");
10
13
  const appIsRunning = require("./appIsRunning");
11
14
  const openBrowser = require("./openBrowser");
12
15
  const connectService = require("./connectService");
13
16
  const probe = require("./probe");
14
17
  const { HttpClient } = require("systemlynx");
18
+ const { createCookieClient } = require("./cookieClient");
15
19
 
16
20
  const DEFAULT_PORT = 3000;
21
+ const VERSION = require("../package.json").version;
17
22
 
18
23
  async function startApp() {
19
24
  const port = isNaN(input[1]) ? DEFAULT_PORT : Number(input[1]);
@@ -34,6 +39,12 @@ async function startTest() {
34
39
  json: flags.json,
35
40
  verbose: flags.verbose,
36
41
  manifest: flags.manifest,
42
+ headers: flags.headers,
43
+ bail: flags.bail,
44
+ dryRun: flags.dryRun,
45
+ phase: flags.phase,
46
+ index: flags.index,
47
+ skip: flags.skip,
37
48
  });
38
49
  process.exit(exitCode);
39
50
  } catch (error) {
@@ -42,6 +53,18 @@ async function startTest() {
42
53
  }
43
54
  }
44
55
 
56
+ async function list() {
57
+ const project_code = input[1];
58
+ const namespace = input[2];
59
+ const url = `http://localhost:${DEFAULT_PORT}`;
60
+ const api = `${url}/systemview/api`;
61
+ if (!(await appIsRunning(api))) {
62
+ await launchApp(DEFAULT_PORT);
63
+ }
64
+ await listTests(url, project_code, namespace, { manifest: flags.manifest, verbose: flags.verbose });
65
+ process.exit(0);
66
+ }
67
+
45
68
  async function open() {
46
69
  const project_code = input[1];
47
70
  const namespace = input[2];
@@ -50,7 +73,24 @@ async function open() {
50
73
  if (!(await appIsRunning(api))) {
51
74
  await launchApp(DEFAULT_PORT);
52
75
  }
53
- openBrowser(ui, project_code, namespace);
76
+
77
+ let connectedServices = [];
78
+ if (namespace && project_code) {
79
+ const manifestFile = flags.manifest || path.join(process.cwd(), "systemview.manifest.json");
80
+ if (fs.existsSync(manifestFile)) {
81
+ try {
82
+ const raw = JSON.parse(fs.readFileSync(manifestFile, "utf8"));
83
+ connectedServices = raw.services ? raw.services : [raw];
84
+ } catch {}
85
+ } else {
86
+ try {
87
+ const { SystemView } = await createCookieClient().loadService(api);
88
+ connectedServices = (await SystemView.getServices(project_code)) || [];
89
+ } catch {}
90
+ }
91
+ }
92
+
93
+ openBrowser(ui, project_code, namespace, connectedServices);
54
94
  process.exit(0);
55
95
  }
56
96
 
@@ -84,8 +124,13 @@ async function quitApp() {
84
124
  init();
85
125
  const command = input[0];
86
126
 
87
- if (command === "open") {
127
+ if (process.argv.includes("--version")) {
128
+ console.log(VERSION);
129
+ process.exit(0);
130
+ } else if (command === "open") {
88
131
  await open();
132
+ } else if (command === "list") {
133
+ await list();
89
134
  } else if (command === "help" || input.includes("help")) {
90
135
  showHelp();
91
136
  } else if (command === "test") {
@@ -96,7 +141,11 @@ async function quitApp() {
96
141
  await connectService(input[1], input[2], { manifest: flags.manifest });
97
142
  process.exit(0);
98
143
  } else if (command === "probe") {
99
- const exitCode = await probe(input[1], input[2], { json: flags.json, manifest: flags.manifest });
144
+ const exitCode = await probe(input[1], input[2], {
145
+ json: flags.json,
146
+ manifest: flags.manifest,
147
+ headers: flags.headers,
148
+ });
100
149
  process.exit(exitCode || 0);
101
150
  } else {
102
151
  await startApp();
@@ -0,0 +1,162 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { createCookieClient } = require("./cookieClient");
4
+ const log = require("./logger");
5
+ const chalk = require("chalk");
6
+
7
+ module.exports = async function listTests(url, project_code, namespace, { manifest: manifestPath, verbose = false } = {}) {
8
+ const api = `${url}/systemview/api`;
9
+ const Client = createCookieClient();
10
+
11
+ if (!project_code) {
12
+ await listAllProjects(api, Client, verbose);
13
+ return;
14
+ }
15
+
16
+ const connectedServices = await loadServices(api, project_code, manifestPath, Client);
17
+ if (!connectedServices.length) {
18
+ log.warn("No connected services found for project: " + project_code);
19
+ return;
20
+ }
21
+
22
+ console.log("");
23
+ console.log(` ${chalk.bold(project_code)}`);
24
+
25
+ for (let si = 0; si < connectedServices.length; si++) {
26
+ const { serviceId, system } = connectedServices[si];
27
+ const isLastService = si === connectedServices.length - 1;
28
+ const servicePrefix = isLastService ? " └── " : " ├── ";
29
+ const childPad = isLastService ? " " : " │ ";
30
+
31
+ let testList = [];
32
+ try {
33
+ const svc = createCookieClient().createService(system.connectionData);
34
+ testList = await svc.Plugin.getTests();
35
+ } catch {
36
+ console.log(`${servicePrefix}${chalk.dim(serviceId)} ${chalk.dim("(could not load tests)")}`);
37
+ continue;
38
+ }
39
+
40
+ const filtered = namespace
41
+ ? testList.filter(({ namespace: n }) =>
42
+ `${n.serviceId}.${n.moduleName}.${n.methodName}`.includes(namespace)
43
+ )
44
+ : testList;
45
+
46
+ if (!filtered.length) {
47
+ console.log(`${servicePrefix}${chalk.dim(serviceId)} ${chalk.dim("(no matching tests)")}`);
48
+ continue;
49
+ }
50
+
51
+ const tree = {};
52
+ filtered.forEach(({ namespace: n, title }) => {
53
+ if (!tree[n.moduleName]) tree[n.moduleName] = {};
54
+ if (verbose) {
55
+ if (!tree[n.moduleName][n.methodName]) tree[n.moduleName][n.methodName] = [];
56
+ tree[n.moduleName][n.methodName].push(title);
57
+ } else {
58
+ tree[n.moduleName][n.methodName] = (tree[n.moduleName][n.methodName] || 0) + 1;
59
+ }
60
+ });
61
+
62
+ console.log(`${servicePrefix}${chalk.bold(serviceId)}`);
63
+
64
+ const modules = Object.keys(tree);
65
+ modules.forEach((moduleName, mi) => {
66
+ const isLastModule = mi === modules.length - 1;
67
+ const modPrefix = childPad + (isLastModule ? "└── " : "├── ");
68
+ const modPad = childPad + (isLastModule ? " " : "│ ");
69
+ console.log(`${modPrefix}${moduleName}`);
70
+
71
+ const methods = Object.keys(tree[moduleName]);
72
+ methods.forEach((methodName, mei) => {
73
+ const isLastMethod = mei === methods.length - 1;
74
+ const methodPrefix = modPad + (isLastMethod ? "└── " : "├── ");
75
+ const methodPad = modPad + (isLastMethod ? " " : "│ ");
76
+ if (verbose) {
77
+ const titles = tree[moduleName][methodName];
78
+ console.log(`${methodPrefix}${chalk.cyan(methodName)}`);
79
+ titles.forEach((title, ti) => {
80
+ const isLastTitle = ti === titles.length - 1;
81
+ const titlePrefix = methodPad + (isLastTitle ? "└── " : "├── ");
82
+ console.log(`${titlePrefix}${chalk.dim(title)}`);
83
+ });
84
+ } else {
85
+ const count = tree[moduleName][methodName];
86
+ const countStr = chalk.dim(`${count} ${count === 1 ? "test" : "tests"}`);
87
+ console.log(`${methodPrefix}${chalk.cyan(methodName)} ${countStr}`);
88
+ }
89
+ });
90
+ });
91
+ }
92
+
93
+ console.log("");
94
+ };
95
+
96
+ async function listAllProjects(api, Client, verbose = false) {
97
+ let projects;
98
+ try {
99
+ const { SystemView } = await Client.loadService(api);
100
+ projects = await SystemView.getProjects();
101
+ } catch {
102
+ log.error("Could not connect to SystemView. Is it running?");
103
+ return;
104
+ }
105
+
106
+ const codes = Object.keys(projects);
107
+ if (!codes.length) {
108
+ log.warn("No connected projects found.");
109
+ return;
110
+ }
111
+
112
+ const total = codes.reduce((n, c) => n + projects[c].length, 0);
113
+ console.log("");
114
+ console.log(chalk.dim(` ${codes.length} ${codes.length === 1 ? "project" : "projects"}, ${total} ${total === 1 ? "service" : "services"}`));
115
+ console.log("");
116
+ codes.forEach((projectCode, pi) => {
117
+ console.log(` ${chalk.bold(projectCode)}`);
118
+ const services = projects[projectCode];
119
+ services.forEach(({ serviceId, serviceUrl, connectionData }, si) => {
120
+ const isLastService = si === services.length - 1;
121
+ const servicePrefix = isLastService ? " └── " : " ├── ";
122
+ const childPad = isLastService ? " " : " │ ";
123
+
124
+ if (verbose && connectionData && connectionData.modules) {
125
+ console.log(`${servicePrefix}${chalk.bold(serviceId)} ${chalk.dim(serviceUrl)}`);
126
+ const mods = connectionData.modules.filter((m) => m.name !== "Plugin");
127
+ mods.forEach((mod, mi) => {
128
+ const isLastMod = mi === mods.length - 1;
129
+ const modPrefix = childPad + (isLastMod ? "└── " : "├── ");
130
+ const modPad = childPad + (isLastMod ? " " : "│ ");
131
+ console.log(`${modPrefix}${mod.name}`);
132
+ const methods = mod.methods || [];
133
+ methods.forEach((m, mei) => {
134
+ const isLastMethod = mei === methods.length - 1;
135
+ const methodPrefix = modPad + (isLastMethod ? "└── " : "├── ");
136
+ console.log(`${methodPrefix}${chalk.cyan(m.fn)}`);
137
+ });
138
+ });
139
+ } else {
140
+ console.log(`${servicePrefix}${serviceId} ${chalk.dim(serviceUrl)}`);
141
+ }
142
+ });
143
+ if (pi < codes.length - 1) console.log("");
144
+ });
145
+ console.log("");
146
+ }
147
+
148
+ async function loadServices(api, project_code, manifestPath, Client) {
149
+ const manifestFile = manifestPath || path.join(process.cwd(), "systemview.manifest.json");
150
+ if (fs.existsSync(manifestFile)) {
151
+ try {
152
+ const raw = JSON.parse(fs.readFileSync(manifestFile, "utf8"));
153
+ return raw.services ? raw.services : [raw];
154
+ } catch {}
155
+ }
156
+ try {
157
+ const { SystemView } = await Client.loadService(api);
158
+ return (await SystemView.getServices(project_code)) || [];
159
+ } catch {
160
+ return [];
161
+ }
162
+ }
@@ -1,6 +1,7 @@
1
1
  const { exec } = require("child_process");
2
+ const resolveNamespace = require("./utils/resolveNamespace");
2
3
 
3
- module.exports = function openBrowser(url, project_code, namespace) {
4
+ module.exports = function openBrowser(url, project_code, namespace, connectedServices) {
4
5
  if (!url || typeof url !== "string" || url.trim() === "") {
5
6
  console.error("Invalid URL provided");
6
7
  return;
@@ -8,15 +9,28 @@ module.exports = function openBrowser(url, project_code, namespace) {
8
9
 
9
10
  const browserCommand = process.platform === "win32" ? "start" : "open";
10
11
  const code = project_code ? "/" + project_code : "";
12
+
13
+ if (namespace && connectedServices && connectedServices.length) {
14
+ const matches = resolveNamespace(namespace, connectedServices);
15
+ if (matches.length) {
16
+ matches.forEach(({ serviceId, moduleName, methodName }) => {
17
+ const nsp = `/${serviceId}/${moduleName}/${methodName}`;
18
+ const fullUrl = `${url}${code}${nsp}`;
19
+ console.log(`opening... ${fullUrl}`);
20
+ exec(`${browserCommand} ${fullUrl}`, (error, stdout, stderr) => {
21
+ if (error) console.error(`Failed to open browser: ${error.message}`);
22
+ else if (stderr) console.error(`Failed to open browser: ${stderr}`);
23
+ });
24
+ });
25
+ return;
26
+ }
27
+ }
28
+
11
29
  const nsp = code && namespace ? "/" + namespace.replace(/\./g, "/") : "";
12
30
  console.log(`opening... ${url}${code}${nsp}`);
13
31
  exec(`${browserCommand} ${url}${code}${nsp}`, (error, stdout, stderr) => {
14
- if (error) {
15
- console.error(`Failed to open browser: ${error.message}`);
16
- } else if (stderr) {
17
- console.error(`Failed to open browser: ${stderr}`);
18
- } else {
19
- console.log("Browser opened successfully!");
20
- }
32
+ if (error) console.error(`Failed to open browser: ${error.message}`);
33
+ else if (stderr) console.error(`Failed to open browser: ${stderr}`);
34
+ else console.log("Browser opened successfully!");
21
35
  });
22
36
  };
package/cli/probe.js CHANGED
@@ -3,7 +3,7 @@ const path = require("path");
3
3
  const { createCookieClient } = require("./cookieClient");
4
4
  const log = require("./logger");
5
5
 
6
- module.exports = async function probe(namespace, argsStr, { json = false, manifest: manifestPath } = {}) {
6
+ module.exports = async function probe(namespace, argsStr, { json = false, manifest: manifestPath, headers: cliHeaders = {} } = {}) {
7
7
  if (!namespace) {
8
8
  log.error("Usage: systemview probe <ServiceId.Module.method> [args]");
9
9
  return 1;
@@ -48,10 +48,11 @@ module.exports = async function probe(namespace, argsStr, { json = false, manife
48
48
  return 1;
49
49
  }
50
50
 
51
+ const extraHeaders = { ...(manifest.probeHeaders || {}), ...cliHeaders };
51
52
  if (!json) log.info(`${serviceId}.${moduleName}.${methodName}(${argsStr || ""})`);
52
53
 
53
54
  try {
54
- const client = createCookieClient().createService(service.system.connectionData);
55
+ const client = createCookieClient(extraHeaders).createService(service.system.connectionData);
55
56
  const result = await client[moduleName][methodName](...args);
56
57
  if (json) {
57
58
  process.stdout.write(JSON.stringify({ serviceId, moduleName, methodName, args, result }, null, 2) + "\n");