@sasjs/cli 6.7.0 → 6.8.1

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.
Files changed (39) hide show
  1. package/README.md +1 -1
  2. package/build/commands/flow/flowCommand.js +11 -1
  3. package/build/commands/flow/internal/executeFlow.js +44 -19
  4. package/build/commands/flow/internal/saveLog.d.ts +12 -1
  5. package/build/commands/flow/internal/saveLog.js +2 -2
  6. package/build/commands/flow/internal/spec/executeFlow.spec.js +44 -1
  7. package/build/commands/flow/spec/flowCommand.spec.js +44 -0
  8. package/build/commands/fs/fsCommand.d.ts +2 -2
  9. package/build/commands/fs/fsCommand.js +44 -15
  10. package/build/commands/fs/internal/executeCode.d.ts +6 -0
  11. package/build/commands/fs/internal/executeCode.js +6 -0
  12. package/build/commands/job/internal/execute/sas9.js +16 -13
  13. package/build/commands/job/internal/execute/sasjs.js +12 -9
  14. package/build/commands/job/internal/execute/viya.js +46 -36
  15. package/build/commands/job/jobCommand.js +13 -0
  16. package/build/commands/job/spec/execute.spec.js +29 -0
  17. package/build/commands/job/spec/executeLogOrder.spec.d.ts +1 -0
  18. package/build/commands/job/spec/executeLogOrder.spec.js +119 -0
  19. package/build/commands/job/spec/jobCommand.spec.js +32 -0
  20. package/build/commands/logcheck/logCheckConfig.d.ts +11 -0
  21. package/build/commands/logcheck/logCheckConfig.js +86 -26
  22. package/build/commands/logcheck/renderLogCheck.d.ts +1 -0
  23. package/build/commands/logcheck/renderLogCheck.js +1 -0
  24. package/build/commands/logcheck/spec/logCheckConfig.spec.js +63 -2
  25. package/build/commands/logcheck/types.d.ts +8 -0
  26. package/build/commands/request/request.js +66 -27
  27. package/build/commands/request/requestCommand.js +11 -1
  28. package/build/commands/request/spec/requestCommand.spec.js +40 -0
  29. package/build/commands/run/run.d.ts +1 -3
  30. package/build/commands/run/run.js +20 -5
  31. package/build/commands/run/runCommand.js +9 -0
  32. package/build/commands/run/spec/runCommand.spec.js +50 -0
  33. package/build/utils/index.d.ts +1 -0
  34. package/build/utils/index.js +1 -0
  35. package/build/utils/logcheck.d.ts +44 -0
  36. package/build/utils/logcheck.js +224 -0
  37. package/build/utils/spec/logcheck.spec.d.ts +1 -0
  38. package/build/utils/spec/logcheck.spec.js +297 -0
  39. package/package.json +1 -1
@@ -44,12 +44,14 @@ exports.getContextName = getContextName;
44
44
  var displayResult_1 = require("../../../../utils/displayResult");
45
45
  var file_1 = require("../../../../utils/file");
46
46
  var fetchLogFileContent_1 = require("../../../shared/fetchLogFileContent");
47
+ var logcheck_1 = require("../../../../utils/logcheck");
48
+ var utils_1 = require("../../../../utils/utils");
47
49
  var path_1 = __importDefault(require("path"));
48
50
  var node_1 = require("@sasjs/adapter/node");
49
- var utils_1 = require("@sasjs/utils");
50
- var utils_2 = require("../../../../utils/");
51
+ var utils_2 = require("@sasjs/utils");
52
+ var utils_3 = require("../../../../utils/");
51
53
  var command_1 = require("../../../../types/command");
52
- var utils_3 = require("../utils");
54
+ var utils_4 = require("../utils");
53
55
  /**
54
56
  * Triggers existing job for execution on Viya server.
55
57
  * @param {object} sasjs - configuration object of SAS adapter.
@@ -90,23 +92,23 @@ function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output,
90
92
  if (!source) return [3 /*break*/, 5];
91
93
  if (!(0, file_1.isJsonFile)(source))
92
94
  throw 'Source file has to be JSON.';
93
- return [4 /*yield*/, (0, utils_1.fileExists)(source).catch(function (_) {
95
+ return [4 /*yield*/, (0, utils_2.fileExists)(source).catch(function (_) {
94
96
  throw 'Error while checking if source file exists.';
95
97
  })];
96
98
  case 3:
97
99
  _c.sent();
98
- return [4 /*yield*/, (0, utils_1.readFile)(source).catch(function (_) {
100
+ return [4 /*yield*/, (0, utils_2.readFile)(source).catch(function (_) {
99
101
  throw 'Error while reading source file.';
100
102
  })];
101
103
  case 4:
102
104
  source = _c.sent();
103
105
  macroVars = JSON.parse(source);
104
- if (!(0, utils_1.isMacroVars)(macroVars)) {
106
+ if (!(0, utils_2.isMacroVars)(macroVars)) {
105
107
  throw "Provided source is not valid. An example of valid source:\n{ macroVars: { name1: 'value1', name2: 'value2' } }";
106
108
  }
107
109
  _c.label = 5;
108
110
  case 5: return [4 /*yield*/, sasjs
109
- .startComputeJob(jobPath, null, { contextName: contextName }, authConfig, waitForJob || !!logFile, pollOptions, true, macroVars === null || macroVars === void 0 ? void 0 : macroVars.macroVars, undefined, (0, utils_2.persistTokensRefreshedByAdapter)(target))
111
+ .startComputeJob(jobPath, null, { contextName: contextName }, authConfig, waitForJob || !!logFile, pollOptions, true, macroVars === null || macroVars === void 0 ? void 0 : macroVars.macroVars, undefined, (0, utils_3.persistTokensRefreshedByAdapter)(target))
110
112
  .catch(function (err) { return __awaiter(_this, void 0, void 0, function () {
111
113
  var logData;
112
114
  var _a, _b;
@@ -121,7 +123,7 @@ function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output,
121
123
  case 1:
122
124
  logData = _c.sent();
123
125
  // save log
124
- return [4 /*yield*/, (0, utils_3.saveLog)(logData, logFile, jobPath)];
126
+ return [4 /*yield*/, (0, utils_4.saveLog)(logData, logFile, jobPath)];
125
127
  case 2:
126
128
  // save log
127
129
  _c.sent();
@@ -129,11 +131,11 @@ function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output,
129
131
  case 3:
130
132
  (0, displayResult_1.displayError)(err);
131
133
  // terminate process with status code 2
132
- (0, utils_2.terminateProcess)(2);
134
+ (0, utils_3.terminateProcess)(2);
133
135
  _c.label = 4;
134
136
  case 4:
135
137
  if (!(err === null || err === void 0 ? void 0 : err.log)) return [3 /*break*/, 6];
136
- return [4 /*yield*/, (0, utils_3.saveLog)(err.log, logFile, jobPath)
138
+ return [4 /*yield*/, (0, utils_4.saveLog)(err.log, logFile, jobPath)
137
139
  // handle use case when job wasn't found
138
140
  ];
139
141
  case 5:
@@ -174,13 +176,13 @@ function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output,
174
176
  _c.sent();
175
177
  _c.label = 8;
176
178
  case 8:
177
- if (!(submittedJob && submittedJob.links)) return [3 /*break*/, 21];
179
+ if (!(submittedJob && submittedJob.links)) return [3 /*break*/, 22];
178
180
  if (!result)
179
181
  result = true;
180
- if (!(output || logFile)) return [3 /*break*/, 20];
182
+ if (!(output || logFile)) return [3 /*break*/, 21];
181
183
  _c.label = 9;
182
184
  case 9:
183
- _c.trys.push([9, 19, , 20]);
185
+ _c.trys.push([9, 20, , 21]);
184
186
  outputJson = JSON.stringify(submittedJob, null, 2);
185
187
  if (!(typeof output === 'string')) return [3 /*break*/, 14];
186
188
  currentDirPath = path_1.default.isAbsolute(output)
@@ -192,14 +194,14 @@ function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output,
192
194
  folderPath = outputPath.split(path_1.default.sep);
193
195
  folderPath.pop();
194
196
  parentFolderPath = folderPath.join(path_1.default.sep);
195
- return [4 /*yield*/, (0, utils_1.folderExists)(parentFolderPath)];
197
+ return [4 /*yield*/, (0, utils_2.folderExists)(parentFolderPath)];
196
198
  case 10:
197
199
  if (!!(_c.sent())) return [3 /*break*/, 12];
198
- return [4 /*yield*/, (0, utils_1.createFolder)(parentFolderPath)];
200
+ return [4 /*yield*/, (0, utils_2.createFolder)(parentFolderPath)];
199
201
  case 11:
200
202
  _c.sent();
201
203
  _c.label = 12;
202
- case 12: return [4 /*yield*/, (0, utils_1.createFile)(outputPath, outputJson)];
204
+ case 12: return [4 /*yield*/, (0, utils_2.createFile)(outputPath, outputJson)];
203
205
  case 13:
204
206
  _c.sent();
205
207
  (0, displayResult_1.displaySuccess)("Output saved to: ".concat(outputPath));
@@ -212,50 +214,58 @@ function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output,
212
214
  }
213
215
  _c.label = 15;
214
216
  case 15:
215
- if (!(logFile && !pollOptions.streamLog)) return [3 /*break*/, 18];
217
+ if (!(logFile && !pollOptions.streamLog)) return [3 /*break*/, 19];
216
218
  logObj = submittedJob.links.find(function (link) { return link.rel === 'log' && link.method === 'GET'; });
217
- if (!logObj) return [3 /*break*/, 18];
219
+ if (!logObj) return [3 /*break*/, 19];
218
220
  logUrl = target.serverUrl + logObj.href;
219
221
  logCount = submittedJob.logStatistics.lineCount;
220
222
  return [4 /*yield*/, (0, fetchLogFileContent_1.fetchLogFileContent)(sasjs, authConfig.access_token, logUrl, logCount)];
221
223
  case 16:
222
224
  logData = _c.sent();
223
- return [4 /*yield*/, (0, utils_3.saveLog)(logData, logFile, jobPath)];
225
+ return [4 /*yield*/, (0, utils_4.saveLog)(logData, logFile, jobPath)];
224
226
  case 17:
225
227
  _c.sent();
226
- _c.label = 18;
228
+ return [4 /*yield*/, (0, logcheck_1.checkLog)(typeof logData === 'string' ? logData : (0, utils_1.parseLogLines)(logData), jobPath)];
227
229
  case 18:
228
- result = submittedJob;
229
- return [3 /*break*/, 20];
230
+ _c.sent();
231
+ _c.label = 19;
230
232
  case 19:
233
+ result = submittedJob;
234
+ return [3 /*break*/, 21];
235
+ case 20:
231
236
  error_1 = _c.sent();
237
+ // A log-check failure is the verdict the caller asked for, not a parse
238
+ // error: swallowing it here printed "Logcheck failed" and then exited 0,
239
+ // which is the green pipeline the feature exists to prevent.
240
+ if ((0, logcheck_1.isLogCheckFailure)(error_1))
241
+ throw error_1;
232
242
  // handle use case when submitted job can't be parsed
233
243
  result = false;
234
244
  (0, displayResult_1.displayError)(null, 'An error has occurred when parsing an output of the job.');
235
- return [3 /*break*/, 20];
236
- case 20:
245
+ return [3 /*break*/, 21];
246
+ case 21:
237
247
  // wait for job state and return status(0,1,2) based on it
238
248
  if (waitForJob && submittedJob.state) {
239
249
  switch (submittedJob.state) {
240
250
  case 'completed':
241
- (0, utils_2.terminateProcess)(0);
251
+ (0, utils_3.terminateProcess)(0);
242
252
  case 'warning':
243
253
  // if ignoreWarnings flag is present, process will be terminated with
244
254
  // status code 0, otherwise status code will be 1
245
- (0, utils_2.terminateProcess)(ignoreWarnings ? 0 : 1);
255
+ (0, utils_3.terminateProcess)(ignoreWarnings ? 0 : 1);
246
256
  case 'error':
247
- (0, utils_2.terminateProcess)(2);
257
+ (0, utils_3.terminateProcess)(2);
248
258
  default:
249
259
  break;
250
260
  }
251
261
  }
252
- return [3 /*break*/, 22];
253
- case 21:
262
+ return [3 /*break*/, 23];
263
+ case 22:
254
264
  if (result) {
255
- (0, utils_2.terminateProcess)(command_1.ReturnCode.InternalError);
265
+ (0, utils_3.terminateProcess)(command_1.ReturnCode.InternalError);
256
266
  }
257
- _c.label = 22;
258
- case 22:
267
+ _c.label = 23;
268
+ case 23:
259
269
  (_b = process.logger) === null || _b === void 0 ? void 0 : _b.info("This job was executed in ".concat((endTime - startTime) / 1000, " seconds"));
260
270
  return [2 /*return*/, result];
261
271
  }
@@ -271,7 +281,7 @@ function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output,
271
281
  function getContextName(target) {
272
282
  var _a;
273
283
  // default execution context of @sasjs/cli
274
- var defaultContextName = utils_2.contextName;
284
+ var defaultContextName = utils_3.contextName;
275
285
  // get gontext name from target
276
286
  if (target === null || target === void 0 ? void 0 : target.contextName)
277
287
  return target.contextName;
@@ -311,14 +321,14 @@ function displayStatus(submittedJob_1, statusFile_1) {
311
321
  folderPath = statusFile.split(path_1.default.sep);
312
322
  folderPath.pop();
313
323
  parentFolderPath = folderPath.join(path_1.default.sep);
314
- return [4 /*yield*/, (0, utils_1.folderExists)(parentFolderPath)];
324
+ return [4 /*yield*/, (0, utils_2.folderExists)(parentFolderPath)];
315
325
  case 1:
316
326
  if (!!(_a.sent())) return [3 /*break*/, 3];
317
- return [4 /*yield*/, (0, utils_1.createFolder)(parentFolderPath)];
327
+ return [4 /*yield*/, (0, utils_2.createFolder)(parentFolderPath)];
318
328
  case 2:
319
329
  _a.sent();
320
330
  _a.label = 3;
321
- case 3: return [4 /*yield*/, (0, utils_1.createFile)(statusFile, status)
331
+ case 3: return [4 /*yield*/, (0, utils_2.createFile)(statusFile, status)
322
332
  // display file path to status file
323
333
  ];
324
334
  case 4:
@@ -62,6 +62,7 @@ var targetCommand_1 = require("../../types/command/targetCommand");
62
62
  var utils_2 = require("../../utils");
63
63
  var getLogFilePath_1 = require("../../utils/getLogFilePath");
64
64
  var prefixAppLoc_1 = require("../../utils/prefixAppLoc");
65
+ var logcheck_1 = require("../../utils/logcheck");
65
66
  var execute_1 = require("./internal/execute");
66
67
  var node_1 = __importDefault(require("@sasjs/adapter/node"));
67
68
  var JobSubCommand;
@@ -126,6 +127,11 @@ var executeParseOptions = {
126
127
  alias: 'v',
127
128
  default: false,
128
129
  description: "If present, CLI will return status 0, 1 or 2 together with HTTP response summaries. If set to 'bleached', HTTP response summaries will be logged without extra colors."
130
+ },
131
+ logcheck: {
132
+ type: 'boolean',
133
+ default: undefined,
134
+ description: 'Check the SAS log this job returned, and fail when logCheck in sasjsconfig.json says so. --no-logcheck switches both the report and the verdict off.'
129
135
  }
130
136
  };
131
137
  var JobCommand = /** @class */ (function (_super) {
@@ -160,6 +166,7 @@ var JobCommand = /** @class */ (function (_super) {
160
166
  return __generator(this, function (_f) {
161
167
  switch (_f.label) {
162
168
  case 0:
169
+ (0, logcheck_1.setLogCheckMode)((0, logcheck_1.logCheckModeOf)(this.parsed.logcheck));
163
170
  returnStatusOnly = !!this.parsed.returnStatusOnly;
164
171
  if (returnStatusOnly) {
165
172
  process.logger.warn('--returnStatusOnly (-r) flag is deprecated.');
@@ -257,6 +264,8 @@ var JobCommand = /** @class */ (function (_super) {
257
264
  .catch(function (err) {
258
265
  var _a;
259
266
  // handle job execution failure
267
+ if ((0, logcheck_1.isLogCheckFailure)(err))
268
+ return command_1.ReturnCode.LogCheckError;
260
269
  (_a = process.logger) === null || _a === void 0 ? void 0 : _a.error('Error executing job: ', err);
261
270
  return command_1.ReturnCode.InternalError;
262
271
  })];
@@ -293,6 +302,8 @@ var JobCommand = /** @class */ (function (_super) {
293
302
  .catch(function (err) {
294
303
  var _a;
295
304
  // handle job execution failure
305
+ if ((0, logcheck_1.isLogCheckFailure)(err))
306
+ return command_1.ReturnCode.LogCheckError;
296
307
  (_a = process.logger) === null || _a === void 0 ? void 0 : _a.error('Error executing job: ', err);
297
308
  return command_1.ReturnCode.InternalError;
298
309
  })];
@@ -334,6 +345,8 @@ var JobCommand = /** @class */ (function (_super) {
334
345
  .catch(function (err) {
335
346
  var _a;
336
347
  // handle job execution failure
348
+ if ((0, logcheck_1.isLogCheckFailure)(err))
349
+ return command_1.ReturnCode.LogCheckError;
337
350
  (_a = process.logger) === null || _a === void 0 ? void 0 : _a.error('Error executing job: ', err);
338
351
  return command_1.ReturnCode.InternalError;
339
352
  })];
@@ -79,6 +79,9 @@ var path_1 = __importDefault(require("path"));
79
79
  var utils_2 = require("../../../utils");
80
80
  var utilsModule = __importStar(require("../../../utils/utils"));
81
81
  var configUtils = __importStar(require("../../../utils/config"));
82
+ var logcheckModule = __importStar(require("../../../utils/logcheck"));
83
+ var fetchLogFileContentModule = __importStar(require("../../shared/fetchLogFileContent"));
84
+ var logcheck_1 = require("../../../utils/logcheck");
82
85
  var execute_1 = require("../internal/execute");
83
86
  var mocks_1 = require("./mocks");
84
87
  var sasjs = new node_1.default();
@@ -165,6 +168,32 @@ describe('executeJobViya', function () {
165
168
  }
166
169
  });
167
170
  }); });
171
+ it('propagates a log-check failure instead of reporting a parse error', function () { return __awaiter(void 0, void 0, void 0, function () {
172
+ var failure;
173
+ return __generator(this, function (_a) {
174
+ switch (_a.label) {
175
+ case 0:
176
+ // a job whose log fails the check used to print the verdict and then exit 0,
177
+ // because this function's catch-all treated the failure as a bad output
178
+ jest.spyOn(sasjs, 'startComputeJob').mockResolvedValue({
179
+ job: {
180
+ state: 'completed',
181
+ links: [{ rel: 'log', method: 'GET', href: '/log' }],
182
+ logStatistics: { lineCount: 5 }
183
+ }
184
+ });
185
+ jest
186
+ .spyOn(fetchLogFileContentModule, 'fetchLogFileContent')
187
+ .mockResolvedValue('ERROR: deliberate');
188
+ failure = new logcheck_1.LogCheckFailure('SAS log check failed for test/job');
189
+ jest.spyOn(logcheckModule, 'checkLog').mockRejectedValue(failure);
190
+ return [4 /*yield*/, expect((0, execute_1.executeJobViya)(sasjs, mocks_1.mockAuthConfig, 'test/job', target, true, false, testLogsPath, undefined, false, undefined, false)).rejects.toBe(failure)];
191
+ case 1:
192
+ _a.sent();
193
+ return [2 /*return*/];
194
+ }
195
+ });
196
+ }); });
168
197
  });
169
198
  describe('executeJobSasjs', function () {
170
199
  beforeEach(function () { return __awaiter(void 0, void 0, void 0, function () {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ var __generator = (this && this.__generator) || function (thisArg, body) {
45
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
46
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
47
+ function verb(n) { return function (v) { return step([n, v]); }; }
48
+ function step(op) {
49
+ if (f) throw new TypeError("Generator is already executing.");
50
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
51
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
52
+ if (y = 0, t) op = [op[0] & 2, t.value];
53
+ switch (op[0]) {
54
+ case 0: case 1: t = op; break;
55
+ case 4: _.label++; return { value: op[1], done: false };
56
+ case 5: _.label++; y = op[1]; op = [0]; continue;
57
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
58
+ default:
59
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
60
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
61
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
62
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
63
+ if (t[2]) _.ops.pop();
64
+ _.trys.pop(); continue;
65
+ }
66
+ op = body.call(thisArg, _);
67
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
68
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
69
+ }
70
+ };
71
+ var __importDefault = (this && this.__importDefault) || function (mod) {
72
+ return (mod && mod.__esModule) ? mod : { "default": mod };
73
+ };
74
+ Object.defineProperty(exports, "__esModule", { value: true });
75
+ var node_1 = __importDefault(require("@sasjs/adapter/node"));
76
+ var jobUtils = __importStar(require("../internal/utils"));
77
+ var logcheck = __importStar(require("../../../utils/logcheck"));
78
+ var sas9_1 = require("../internal/execute/sas9");
79
+ /**
80
+ * A run that fails the log check is the run whose log someone wants to read, so
81
+ * the file has to be written before the check can throw - otherwise `--log`
82
+ * produces nothing exactly when it is needed.
83
+ */
84
+ describe('job execute log handling', function () {
85
+ beforeEach(function () {
86
+ process.logger = {
87
+ success: jest.fn(),
88
+ info: jest.fn(),
89
+ warn: jest.fn(),
90
+ error: jest.fn()
91
+ };
92
+ });
93
+ it('writes the log before a failing check can throw', function () { return __awaiter(void 0, void 0, void 0, function () {
94
+ var sasjs, saveSpy, checkSpy;
95
+ return __generator(this, function (_a) {
96
+ switch (_a.label) {
97
+ case 0:
98
+ sasjs = new node_1.default();
99
+ jest.spyOn(sasjs, 'request').mockResolvedValue({
100
+ status: 200,
101
+ log: 'ERROR 22-322: Syntax error',
102
+ result: {}
103
+ });
104
+ saveSpy = jest
105
+ .spyOn(jobUtils, 'saveLog')
106
+ .mockResolvedValue(undefined);
107
+ checkSpy = jest
108
+ .spyOn(logcheck, 'checkLog')
109
+ .mockRejectedValue(new Error('SAS log check failed'));
110
+ return [4 /*yield*/, expect((0, sas9_1.executeJobSas9)(sasjs, {}, 'job/path', 'job.log')).rejects.toThrow('SAS log check failed')];
111
+ case 1:
112
+ _a.sent();
113
+ expect(saveSpy).toHaveBeenCalledWith('ERROR 22-322: Syntax error', 'job.log', 'job/path');
114
+ expect(saveSpy.mock.invocationCallOrder[0]).toBeLessThan(checkSpy.mock.invocationCallOrder[0]);
115
+ return [2 /*return*/];
116
+ }
117
+ });
118
+ }); });
119
+ });
@@ -106,6 +106,7 @@ var utils_1 = require("@sasjs/utils");
106
106
  var SasjsUtilsFilesModule = __importStar(require("@sasjs/utils/file"));
107
107
  var configUtils = __importStar(require("../../../utils/config"));
108
108
  var command_1 = require("../../../types/command");
109
+ var logcheck_1 = require("../../../utils/logcheck");
109
110
  var utilsModule = __importStar(require("../internal/utils"));
110
111
  var defaultArgs = ['node', 'sasjs', 'job', 'execute'];
111
112
  var targetViya = new utils_1.Target({
@@ -149,6 +150,37 @@ describe('JobCommand', function () {
149
150
  beforeEach(function () {
150
151
  setupMocksForViya();
151
152
  });
153
+ afterEach(function () { return (0, logcheck_1.setLogCheckMode)('auto'); });
154
+ it('should return the logcheck code when the log check failed the job', function () { return __awaiter(void 0, void 0, void 0, function () {
155
+ var args, _a;
156
+ return __generator(this, function (_b) {
157
+ switch (_b.label) {
158
+ case 0:
159
+ jest
160
+ .spyOn(viyaExecuteModule, 'executeJobViya')
161
+ .mockImplementation(function () {
162
+ return Promise.reject(new logcheck_1.LogCheckFailure('2 errors'));
163
+ });
164
+ args = __spreadArray(__spreadArray([], __read(defaultArgs), false), [jobPath], false);
165
+ _a = expect;
166
+ return [4 /*yield*/, new jobCommand_1.JobCommand(args).execute()];
167
+ case 1:
168
+ _a.apply(void 0, [_b.sent()]).toEqual(command_1.ReturnCode.LogCheckError);
169
+ return [2 /*return*/];
170
+ }
171
+ });
172
+ }); });
173
+ it('should switch the log check off for --no-logcheck', function () { return __awaiter(void 0, void 0, void 0, function () {
174
+ return __generator(this, function (_a) {
175
+ switch (_a.label) {
176
+ case 0: return [4 /*yield*/, executeCommandWrapper([jobPath, '--no-logcheck'])];
177
+ case 1:
178
+ _a.sent();
179
+ expect((0, logcheck_1.getLogCheckMode)()).toEqual('off');
180
+ return [2 /*return*/];
181
+ }
182
+ });
183
+ }); });
152
184
  it('should parse sasjs job execute command', function () { return __awaiter(void 0, void 0, void 0, function () {
153
185
  var _a;
154
186
  return __generator(this, function (_b) {
@@ -14,6 +14,17 @@ export declare const DEFAULT_FOLDER = "sasjsresults";
14
14
  * @param source - the file it came from, named in the error message.
15
15
  */
16
16
  export declare const parseLogCheckSection: (section: unknown, source?: string) => LogCheckConfig;
17
+ /**
18
+ * Where a project's config may be, in the order they are tried.
19
+ *
20
+ * `sasjs/sasjsconfig.json` is the SASjs convention and the one the rest of the
21
+ * CLI reads, so it wins. A `sasjsconfig.json` at the project root is still
22
+ * honoured, because a folder holding a single log and a hand-written config is
23
+ * exactly the case this command is meant to serve - but the two are never
24
+ * confused for each other, and the convention cannot be shadowed by a stray
25
+ * file next to it.
26
+ */
27
+ export declare const configCandidates: (projectDir: string) => string[];
17
28
  /**
18
29
  * Reads the `logCheck` policy from the project's `sasjsconfig.json`.
19
30
  *