Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 96bfb81

Browse files
authoredJan 23, 2025··
Add a setting for logging REST traffic (#1466)
1 parent 95cf1db commit 96bfb81

File tree

5 files changed

+314
-51
lines changed

5 files changed

+314
-51
lines changed
 

‎package-lock.json

Lines changed: 256 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,11 +1466,6 @@
14661466
"default": true,
14671467
"markdownDescription": "Show inline `Copy Invocation` CodeLens action for ClassMethods and Routine Labels."
14681468
},
1469-
"objectscript.studioActionDebugOutput": {
1470-
"type": "boolean",
1471-
"default": false,
1472-
"description": "Output in JSON format the action that VS Code should perform as requested by the server."
1473-
},
14741469
"objectscript.autoShowTerminal": {
14751470
"description": "Automatically show terminal when connected to docker-compose.",
14761471
"type": "boolean",
@@ -1606,6 +1601,11 @@
16061601
"description": "Controls whether files in client-side workspace folders that are created, deleted, or changed are automatically synced to the server. Synching will occur whether changes are made due to user actions in VS Code (for example, saving a file that's being actively edited) or outside of VS Code (for example, deleting a file from the OS file explorer while VS Code has its folder open).",
16071602
"type": "boolean",
16081603
"default": true
1604+
},
1605+
"objectscript.outputRESTTraffic": {
1606+
"description": "If true, REST requests and responses to and from InterSystems servers will be logged to the ObjectScript Output channel. This should only be enabled when debugging a potential issue.",
1607+
"type": "boolean",
1608+
"default": false
16091609
}
16101610
}
16111611
},
@@ -1793,7 +1793,7 @@
17931793
"eslint-plugin-node": "^11.1.0",
17941794
"eslint-plugin-prettier": "^5.2.1",
17951795
"eslint-plugin-promise": "^7.1.0",
1796-
"glob": "^7.1.6",
1796+
"glob": "^11.0.1",
17971797
"globals": "^15.12.0",
17981798
"mocha": "^9.1.3",
17991799
"path-browserify": "^1.0.1",

‎src/api/index.ts

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,27 @@ export class AtelierAPI {
334334
auth = authRequest;
335335
}
336336

337+
const outputTraffic = vscode.workspace.getConfiguration("objectscript").get<boolean>("outputRESTTraffic");
338+
let cookie;
339+
let reqTs: Date;
340+
const outputRequest = () => {
341+
outputChannel.appendLine(`+- REQUEST - ${reqTs.toLocaleTimeString()} ----------------------------`);
342+
outputChannel.appendLine(`${method} ${proto}://${host}:${port}${path}`);
343+
if (cookie) outputChannel.appendLine("COOKIE: <value>");
344+
for (const [h, v] of Object.entries(headers)) {
345+
// Don't output value of the Authorization header
346+
const hUpper = h.toUpperCase();
347+
outputChannel.appendLine(`${hUpper}: ${hUpper == "AUTHORIZATION" ? "<value>" : v}`);
348+
}
349+
if (body) {
350+
outputChannel.appendLine(
351+
`Body:\n${headers["Content-Type"] == "application/json" ? JSON.stringify(body, null, 2) : body}`
352+
);
353+
}
354+
};
337355
try {
338-
const cookie = await auth;
356+
cookie = await auth;
357+
reqTs = new Date();
339358
const response = await axios.request({
340359
method,
341360
url: `${proto}://${host}:${port}${path}`,
@@ -350,6 +369,22 @@ export class AtelierAPI {
350369
signal: options?.timeout ? AbortSignal.timeout(options.timeout) : undefined,
351370
validateStatus: (status) => status < 504,
352371
});
372+
if (outputTraffic) {
373+
outputRequest();
374+
outputChannel.appendLine(`+- RESPONSE - ${new Date().toLocaleTimeString()} ---------------------------`);
375+
outputChannel.appendLine(`${response.status} ${response.statusText}`);
376+
for (const [h, v] of Object.entries(response.headers)) {
377+
// Don't output value of the Set-Cookie header
378+
const hUpper = h.toUpperCase();
379+
outputChannel.appendLine(`${hUpper}: ${hUpper == "SET-COOKIE" ? "<value>" : v}`);
380+
}
381+
if (response.data) {
382+
outputChannel.appendLine(
383+
`Body:\n${typeof response.data == "object" ? JSON.stringify(response.data, null, 2) : response.data}`
384+
);
385+
}
386+
outputChannel.appendLine(`+- END ----------------------------------------------`);
387+
}
353388
if (response.status === 503) {
354389
// User likely ran out of licenses
355390
throw {
@@ -394,7 +429,7 @@ export class AtelierAPI {
394429
throw {
395430
statusCode: response.status,
396431
message: response.statusText,
397-
errorText: `Non-JSON response to ${path} request. Is the web server suppressing detailed errors?`,
432+
errorText: `Body of '${response.status} ${response.statusText}' response to ${path} request is not JSON. Is the web server suppressing detailed errors?`,
398433
};
399434
}
400435
const data: Atelier.Response = response.data;
@@ -446,6 +481,13 @@ export class AtelierAPI {
446481

447482
return data;
448483
} catch (error) {
484+
if (outputTraffic && !error.statusCode) {
485+
// Only output errors here if they were "hard" errors, not HTTP response errors
486+
outputRequest();
487+
outputChannel.appendLine(`+- ERROR --------------------------------------------`);
488+
outputChannel.appendLine(`${JSON.stringify(error, null, 2)}`);
489+
outputChannel.appendLine(`+- END ----------------------------------------------`);
490+
}
449491
// always discard the cached authentication promise
450492
authRequestMap.delete(target);
451493

‎src/commands/studio.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,6 @@ export class StudioActions {
119119
outputChannel.appendLine(errorText);
120120
outputChannel.show(true);
121121
}
122-
if (vscode.workspace.getConfiguration("objectscript").get("studioActionDebugOutput")) {
123-
outputChannel.appendLine(JSON.stringify(userAction));
124-
}
125122
switch (serverAction) {
126123
case 0:
127124
/// do nothing
@@ -292,10 +289,6 @@ export class StudioActions {
292289
? [type.toString(), action.id, this.name, answer, msg]
293290
: [type.toString(), action.id, this.name, selectedText];
294291

295-
if (vscode.workspace.getConfiguration("objectscript").get("studioActionDebugOutput")) {
296-
outputChannel.appendLine(`${query.slice(0, query.indexOf("("))}(${JSON.stringify(parameters).slice(1, -1)})`);
297-
}
298-
299292
return vscode.window.withProgress(
300293
{
301294
cancellable: false,

‎src/test/suite/index.ts

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as path from "path";
22
import * as Mocha from "mocha";
3-
import * as glob from "glob";
3+
import { glob } from "glob";
44

55
export function run(): Promise<void> {
66
// Create the mocha test
@@ -11,26 +11,14 @@ export function run(): Promise<void> {
1111

1212
const testsRoot = path.resolve(__dirname, "..");
1313

14-
return new Promise((c, e) => {
15-
glob("**/**.test.js", { cwd: testsRoot }, (err, files) => {
16-
if (err) {
17-
return e(err);
18-
}
19-
20-
// Add files to the test suite
21-
files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f)));
14+
return glob("**/**.test.js", { cwd: testsRoot }).then((files) => {
15+
// Add files to the test suite
16+
files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f)));
2217

23-
try {
24-
// Run the mocha test
25-
mocha.run((failures) => {
26-
if (failures > 0) {
27-
e(new Error(`${failures} tests failed.`));
28-
} else {
29-
c();
30-
}
31-
});
32-
} catch (err) {
33-
e(err);
18+
// Run the mocha test
19+
mocha.run((failures) => {
20+
if (failures > 0) {
21+
throw new Error(`${failures} tests failed.`);
3422
}
3523
});
3624
});

0 commit comments

Comments
 (0)
Please sign in to comment.