@sasjs/cli 6.7.0 → 6.8.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.
Files changed (34) 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/job/internal/execute/sas9.js +16 -13
  9. package/build/commands/job/internal/execute/sasjs.js +12 -9
  10. package/build/commands/job/internal/execute/viya.js +41 -36
  11. package/build/commands/job/jobCommand.js +13 -0
  12. package/build/commands/job/spec/executeLogOrder.spec.d.ts +1 -0
  13. package/build/commands/job/spec/executeLogOrder.spec.js +119 -0
  14. package/build/commands/job/spec/jobCommand.spec.js +32 -0
  15. package/build/commands/logcheck/logCheckConfig.d.ts +11 -0
  16. package/build/commands/logcheck/logCheckConfig.js +86 -26
  17. package/build/commands/logcheck/renderLogCheck.d.ts +1 -0
  18. package/build/commands/logcheck/renderLogCheck.js +1 -0
  19. package/build/commands/logcheck/spec/logCheckConfig.spec.js +63 -2
  20. package/build/commands/logcheck/types.d.ts +8 -0
  21. package/build/commands/request/request.js +66 -27
  22. package/build/commands/request/requestCommand.js +11 -1
  23. package/build/commands/request/spec/requestCommand.spec.js +40 -0
  24. package/build/commands/run/run.d.ts +1 -3
  25. package/build/commands/run/run.js +20 -5
  26. package/build/commands/run/runCommand.js +9 -0
  27. package/build/commands/run/spec/runCommand.spec.js +50 -0
  28. package/build/utils/index.d.ts +1 -0
  29. package/build/utils/index.js +1 -0
  30. package/build/utils/logcheck.d.ts +44 -0
  31. package/build/utils/logcheck.js +224 -0
  32. package/build/utils/spec/logcheck.spec.d.ts +1 -0
  33. package/build/utils/spec/logcheck.spec.js +297 -0
  34. 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,53 @@ 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();
232
237
  // handle use case when submitted job can't be parsed
233
238
  result = false;
234
239
  (0, displayResult_1.displayError)(null, 'An error has occurred when parsing an output of the job.');
235
- return [3 /*break*/, 20];
236
- case 20:
240
+ return [3 /*break*/, 21];
241
+ case 21:
237
242
  // wait for job state and return status(0,1,2) based on it
238
243
  if (waitForJob && submittedJob.state) {
239
244
  switch (submittedJob.state) {
240
245
  case 'completed':
241
- (0, utils_2.terminateProcess)(0);
246
+ (0, utils_3.terminateProcess)(0);
242
247
  case 'warning':
243
248
  // if ignoreWarnings flag is present, process will be terminated with
244
249
  // status code 0, otherwise status code will be 1
245
- (0, utils_2.terminateProcess)(ignoreWarnings ? 0 : 1);
250
+ (0, utils_3.terminateProcess)(ignoreWarnings ? 0 : 1);
246
251
  case 'error':
247
- (0, utils_2.terminateProcess)(2);
252
+ (0, utils_3.terminateProcess)(2);
248
253
  default:
249
254
  break;
250
255
  }
251
256
  }
252
- return [3 /*break*/, 22];
253
- case 21:
257
+ return [3 /*break*/, 23];
258
+ case 22:
254
259
  if (result) {
255
- (0, utils_2.terminateProcess)(command_1.ReturnCode.InternalError);
260
+ (0, utils_3.terminateProcess)(command_1.ReturnCode.InternalError);
256
261
  }
257
- _c.label = 22;
258
- case 22:
262
+ _c.label = 23;
263
+ case 23:
259
264
  (_b = process.logger) === null || _b === void 0 ? void 0 : _b.info("This job was executed in ".concat((endTime - startTime) / 1000, " seconds"));
260
265
  return [2 /*return*/, result];
261
266
  }
@@ -271,7 +276,7 @@ function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output,
271
276
  function getContextName(target) {
272
277
  var _a;
273
278
  // default execution context of @sasjs/cli
274
- var defaultContextName = utils_2.contextName;
279
+ var defaultContextName = utils_3.contextName;
275
280
  // get gontext name from target
276
281
  if (target === null || target === void 0 ? void 0 : target.contextName)
277
282
  return target.contextName;
@@ -311,14 +316,14 @@ function displayStatus(submittedJob_1, statusFile_1) {
311
316
  folderPath = statusFile.split(path_1.default.sep);
312
317
  folderPath.pop();
313
318
  parentFolderPath = folderPath.join(path_1.default.sep);
314
- return [4 /*yield*/, (0, utils_1.folderExists)(parentFolderPath)];
319
+ return [4 /*yield*/, (0, utils_2.folderExists)(parentFolderPath)];
315
320
  case 1:
316
321
  if (!!(_a.sent())) return [3 /*break*/, 3];
317
- return [4 /*yield*/, (0, utils_1.createFolder)(parentFolderPath)];
322
+ return [4 /*yield*/, (0, utils_2.createFolder)(parentFolderPath)];
318
323
  case 2:
319
324
  _a.sent();
320
325
  _a.label = 3;
321
- case 3: return [4 /*yield*/, (0, utils_1.createFile)(statusFile, status)
326
+ case 3: return [4 /*yield*/, (0, utils_2.createFile)(statusFile, status)
322
327
  // display file path to status file
323
328
  ];
324
329
  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
  })];
@@ -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
  *
@@ -46,6 +46,17 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
46
46
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
47
  }
48
48
  };
49
+ var __values = (this && this.__values) || function(o) {
50
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
51
+ if (m) return m.call(o);
52
+ if (o && typeof o.length === "number") return {
53
+ next: function () {
54
+ if (o && i >= o.length) o = void 0;
55
+ return { value: o && o[i++], done: !o };
56
+ }
57
+ };
58
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
59
+ };
49
60
  var __read = (this && this.__read) || function (o, n) {
50
61
  var m = typeof Symbol === "function" && o[Symbol.iterator];
51
62
  if (!m) return o;
@@ -75,7 +86,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
75
86
  return (mod && mod.__esModule) ? mod : { "default": mod };
76
87
  };
77
88
  Object.defineProperty(exports, "__esModule", { value: true });
78
- exports.toPosix = exports.globToRegExp = exports.isIgnoredPath = exports.matchesSegments = exports.matchesPath = exports.mergeConfig = exports.readLogCheckConfig = exports.parseLogCheckSection = exports.DEFAULT_FOLDER = exports.CONFIG_KEY = void 0;
89
+ exports.toPosix = exports.globToRegExp = exports.isIgnoredPath = exports.matchesSegments = exports.matchesPath = exports.mergeConfig = exports.readLogCheckConfig = exports.configCandidates = exports.parseLogCheckSection = exports.DEFAULT_FOLDER = exports.CONFIG_KEY = void 0;
79
90
  var path_1 = __importDefault(require("path"));
80
91
  var utils_1 = require("@sasjs/utils");
81
92
  var types_1 = require("./types");
@@ -102,7 +113,7 @@ var parseLogCheckSection = function (section, source) {
102
113
  if (typeof section !== 'object' || Array.isArray(section)) {
103
114
  throw new Error("The `".concat(exports.CONFIG_KEY, "` property in ").concat(source, " must be an object."));
104
115
  }
105
- var _a = section, failOn = _a.failOn, maxWarnings = _a.maxWarnings, ignoreCodes = _a.ignoreCodes, ignorePaths = _a.ignorePaths, folder = _a.folder;
116
+ var _a = section, failOn = _a.failOn, maxWarnings = _a.maxWarnings, ignoreCodes = _a.ignoreCodes, ignorePaths = _a.ignorePaths, folder = _a.folder, autoCheck = _a.autoCheck;
106
117
  if (failOn !== undefined) {
107
118
  if (!failOnValues.includes(failOn)) {
108
119
  throw new Error("The `".concat(exports.CONFIG_KEY, ".failOn` property in ").concat(source, " must be one of ").concat(failOnValues.join(', '), " - received `").concat(String(failOn), "`."));
@@ -123,6 +134,12 @@ var parseLogCheckSection = function (section, source) {
123
134
  if (ignorePaths !== undefined) {
124
135
  config.ignorePaths = stringList(ignorePaths, "".concat(exports.CONFIG_KEY, ".ignorePaths"), source);
125
136
  }
137
+ if (autoCheck !== undefined) {
138
+ if (typeof autoCheck !== 'boolean') {
139
+ throw new Error("The `".concat(exports.CONFIG_KEY, ".autoCheck` property in ").concat(source, " must be true or false - received `").concat(String(autoCheck), "`."));
140
+ }
141
+ config.autoCheck = autoCheck;
142
+ }
126
143
  if (folder !== undefined) {
127
144
  if (typeof folder !== 'string' || !folder.trim()) {
128
145
  throw new Error("The `".concat(exports.CONFIG_KEY, ".folder` property in ").concat(source, " must be a non-empty string."));
@@ -132,6 +149,21 @@ var parseLogCheckSection = function (section, source) {
132
149
  return config;
133
150
  };
134
151
  exports.parseLogCheckSection = parseLogCheckSection;
152
+ /**
153
+ * Where a project's config may be, in the order they are tried.
154
+ *
155
+ * `sasjs/sasjsconfig.json` is the SASjs convention and the one the rest of the
156
+ * CLI reads, so it wins. A `sasjsconfig.json` at the project root is still
157
+ * honoured, because a folder holding a single log and a hand-written config is
158
+ * exactly the case this command is meant to serve - but the two are never
159
+ * confused for each other, and the convention cannot be shadowed by a stray
160
+ * file next to it.
161
+ */
162
+ var configCandidates = function (projectDir) { return [
163
+ path_1.default.join(projectDir, 'sasjs', 'sasjsconfig.json'),
164
+ path_1.default.join(projectDir, 'sasjsconfig.json')
165
+ ]; };
166
+ exports.configCandidates = configCandidates;
135
167
  /**
136
168
  * Reads the `logCheck` policy from the project's `sasjsconfig.json`.
137
169
  *
@@ -140,39 +172,67 @@ exports.parseLogCheckSection = parseLogCheckSection;
140
172
  * whole point of checking a log somebody else produced.
141
173
  */
142
174
  var readLogCheckConfig = function () { return __awaiter(void 0, void 0, void 0, function () {
143
- var projectDir, configPath, raw, parsed, section, config, resultsFolder;
144
- var _a, _b, _c, _d;
145
- return __generator(this, function (_e) {
146
- switch (_e.label) {
175
+ var projectDir, _a, _b, configPath, raw, e_1_1;
176
+ var e_1, _c;
177
+ return __generator(this, function (_d) {
178
+ switch (_d.label) {
147
179
  case 0:
148
180
  projectDir = process.projectDir || process.cwd();
149
- configPath = path_1.default.join(projectDir, 'sasjsconfig.json');
150
- return [4 /*yield*/, (0, utils_1.readFile)(configPath).catch(function () { return undefined; })];
181
+ _d.label = 1;
151
182
  case 1:
152
- raw = _e.sent();
153
- if (!raw)
154
- return [2 /*return*/, __assign(__assign({}, types_1.defaultLogCheckConfig), { ignoreCodes: [], ignorePaths: [] })];
183
+ _d.trys.push([1, 6, 7, 8]);
184
+ _a = __values((0, exports.configCandidates)(projectDir)), _b = _a.next();
185
+ _d.label = 2;
186
+ case 2:
187
+ if (!!_b.done) return [3 /*break*/, 5];
188
+ configPath = _b.value;
189
+ return [4 /*yield*/, (0, utils_1.readFile)(configPath).catch(function () { return undefined; })];
190
+ case 3:
191
+ raw = _d.sent();
192
+ if (raw)
193
+ return [2 /*return*/, readConfigAt(configPath, raw)];
194
+ _d.label = 4;
195
+ case 4:
196
+ _b = _a.next();
197
+ return [3 /*break*/, 2];
198
+ case 5: return [3 /*break*/, 8];
199
+ case 6:
200
+ e_1_1 = _d.sent();
201
+ e_1 = { error: e_1_1 };
202
+ return [3 /*break*/, 8];
203
+ case 7:
155
204
  try {
156
- parsed = JSON.parse(raw);
205
+ if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
157
206
  }
158
- catch (error) {
159
- throw new Error("Could not parse ".concat(configPath, ": ").concat(error.message));
160
- }
161
- section = (_b = (_a = parsed === null || parsed === void 0 ? void 0 : parsed.config) === null || _a === void 0 ? void 0 : _a[exports.CONFIG_KEY]) !== null && _b !== void 0 ? _b : parsed === null || parsed === void 0 ? void 0 : parsed[exports.CONFIG_KEY];
162
- config = (0, exports.parseLogCheckSection)(section, configPath);
163
- // A project may have moved its results folder; the bare command follows it,
164
- // unless the logCheck section names a folder of its own.
165
- if (!config.folder) {
166
- resultsFolder = (_d = (_c = parsed === null || parsed === void 0 ? void 0 : parsed.config) === null || _c === void 0 ? void 0 : _c.sasjsResultsFolder) !== null && _d !== void 0 ? _d : parsed === null || parsed === void 0 ? void 0 : parsed.sasjsResultsFolder;
167
- if (typeof resultsFolder === 'string' && resultsFolder.trim()) {
168
- config.folder = resultsFolder.trim();
169
- }
170
- }
171
- return [2 /*return*/, config];
207
+ finally { if (e_1) throw e_1.error; }
208
+ return [7 /*endfinally*/];
209
+ case 8: return [2 /*return*/, __assign(__assign({}, types_1.defaultLogCheckConfig), { ignoreCodes: [], ignorePaths: [] })];
172
210
  }
173
211
  });
174
212
  }); };
175
213
  exports.readLogCheckConfig = readLogCheckConfig;
214
+ /** Parses one config document into the policy - see {@link readLogCheckConfig}. */
215
+ var readConfigAt = function (configPath, raw) {
216
+ var _a, _b, _c, _d;
217
+ var parsed;
218
+ try {
219
+ parsed = JSON.parse(raw);
220
+ }
221
+ catch (error) {
222
+ throw new Error("Could not parse ".concat(configPath, ": ").concat(error.message));
223
+ }
224
+ var section = (_b = (_a = parsed === null || parsed === void 0 ? void 0 : parsed.config) === null || _a === void 0 ? void 0 : _a[exports.CONFIG_KEY]) !== null && _b !== void 0 ? _b : parsed === null || parsed === void 0 ? void 0 : parsed[exports.CONFIG_KEY];
225
+ var config = (0, exports.parseLogCheckSection)(section, configPath);
226
+ // A project may have moved its results folder; the bare command follows it,
227
+ // unless the logCheck section names a folder of its own.
228
+ if (!config.folder) {
229
+ var resultsFolder = (_d = (_c = parsed === null || parsed === void 0 ? void 0 : parsed.config) === null || _c === void 0 ? void 0 : _c.sasjsResultsFolder) !== null && _d !== void 0 ? _d : parsed === null || parsed === void 0 ? void 0 : parsed.sasjsResultsFolder;
230
+ if (typeof resultsFolder === 'string' && resultsFolder.trim()) {
231
+ config.folder = resultsFolder.trim();
232
+ }
233
+ }
234
+ return config;
235
+ };
176
236
  /**
177
237
  * Layers the command line over the config file. Suppressions are added, never
178
238
  * replaced: a flag silences one more code for one run, it does not un-silence
@@ -84,6 +84,7 @@ export declare const toJson: (summary: LogCheckSummary, config: LogCheckConfig)
84
84
  config: {
85
85
  failOn: import("./types").FailOn;
86
86
  maxWarnings: number | null;
87
+ autoCheck: boolean;
87
88
  ignoreCodes: string[];
88
89
  ignorePaths: string[];
89
90
  };
@@ -346,6 +346,7 @@ var toJson = function (summary, config) {
346
346
  config: {
347
347
  failOn: config.failOn,
348
348
  maxWarnings: (_a = config.maxWarnings) !== null && _a !== void 0 ? _a : null,
349
+ autoCheck: config.autoCheck === true,
349
350
  ignoreCodes: config.ignoreCodes,
350
351
  ignorePaths: config.ignorePaths
351
352
  },