@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
@@ -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
  },
@@ -75,16 +75,21 @@ describe('parseLogCheckSection', function () {
75
75
  maxWarnings: 4,
76
76
  ignoreCodes: ['49-169'],
77
77
  ignorePaths: ['sasjsresults/legacy'],
78
- folder: 'logs'
78
+ folder: 'logs',
79
+ autoCheck: true
79
80
  });
80
81
  expect(config).toEqual({
81
82
  failOn: 'warning',
82
83
  maxWarnings: 4,
83
84
  ignoreCodes: ['49-169'],
84
85
  ignorePaths: ['sasjsresults/legacy'],
85
- folder: 'logs'
86
+ folder: 'logs',
87
+ autoCheck: true
86
88
  });
87
89
  });
90
+ it('refuses an autoCheck that is not a boolean', function () {
91
+ expect(function () { return (0, logCheckConfig_1.parseLogCheckSection)({ autoCheck: 'yes' }); }).toThrow('must be true or false');
92
+ });
88
93
  it('refuses a failOn that is not one of the four policies', function () {
89
94
  expect(function () { return (0, logCheckConfig_1.parseLogCheckSection)({ failOn: 'warnings' }); }).toThrow('must be one of error, warning, note, never');
90
95
  });
@@ -150,6 +155,54 @@ describe('readLogCheckConfig', function () {
150
155
  }
151
156
  });
152
157
  }); });
158
+ it('reads the config from the SASjs convention, sasjs/sasjsconfig.json', function () { return __awaiter(void 0, void 0, void 0, function () {
159
+ var folder, config;
160
+ return __generator(this, function (_a) {
161
+ switch (_a.label) {
162
+ case 0: return [4 /*yield*/, tempFolder()];
163
+ case 1:
164
+ folder = _a.sent();
165
+ process.projectDir = folder;
166
+ return [4 /*yield*/, fs_1.promises.mkdir(path_1.default.join(folder, 'sasjs'))];
167
+ case 2:
168
+ _a.sent();
169
+ return [4 /*yield*/, fs_1.promises.writeFile(path_1.default.join(folder, 'sasjs', 'sasjsconfig.json'), JSON.stringify({ logCheck: { failOn: 'warning', autoCheck: true } }))];
170
+ case 3:
171
+ _a.sent();
172
+ return [4 /*yield*/, (0, logCheckConfig_1.readLogCheckConfig)()];
173
+ case 4:
174
+ config = _a.sent();
175
+ expect(config.failOn).toEqual('warning');
176
+ expect(config.autoCheck).toBeTrue();
177
+ return [2 /*return*/];
178
+ }
179
+ });
180
+ }); });
181
+ it('prefers sasjs/sasjsconfig.json when a root file also exists', function () { return __awaiter(void 0, void 0, void 0, function () {
182
+ var folder, _a;
183
+ return __generator(this, function (_b) {
184
+ switch (_b.label) {
185
+ case 0: return [4 /*yield*/, tempFolder()];
186
+ case 1:
187
+ folder = _b.sent();
188
+ process.projectDir = folder;
189
+ return [4 /*yield*/, fs_1.promises.mkdir(path_1.default.join(folder, 'sasjs'))];
190
+ case 2:
191
+ _b.sent();
192
+ return [4 /*yield*/, fs_1.promises.writeFile(path_1.default.join(folder, 'sasjs', 'sasjsconfig.json'), JSON.stringify({ logCheck: { failOn: 'note' } }))];
193
+ case 3:
194
+ _b.sent();
195
+ return [4 /*yield*/, fs_1.promises.writeFile(path_1.default.join(folder, 'sasjsconfig.json'), JSON.stringify({ logCheck: { failOn: 'never' } }))];
196
+ case 4:
197
+ _b.sent();
198
+ _a = expect;
199
+ return [4 /*yield*/, (0, logCheckConfig_1.readLogCheckConfig)()];
200
+ case 5:
201
+ _a.apply(void 0, [(_b.sent()).failOn]).toEqual('note');
202
+ return [2 /*return*/];
203
+ }
204
+ });
205
+ }); });
153
206
  it('reads the section from the top level of the config', function () { return __awaiter(void 0, void 0, void 0, function () {
154
207
  var _a;
155
208
  return __generator(this, function (_b) {
@@ -227,6 +280,14 @@ describe('readLogCheckConfig', function () {
227
280
  });
228
281
  }); });
229
282
  });
283
+ describe('configCandidates', function () {
284
+ it('tries the SASjs convention before the project root', function () {
285
+ expect((0, logCheckConfig_1.configCandidates)(path_1.default.join('some', 'project'))).toEqual([
286
+ path_1.default.join('some', 'project', 'sasjs', 'sasjsconfig.json'),
287
+ path_1.default.join('some', 'project', 'sasjsconfig.json')
288
+ ]);
289
+ });
290
+ });
230
291
  describe('mergeConfig', function () {
231
292
  var base = __assign(__assign({}, types_1.defaultLogCheckConfig), { ignoreCodes: ['49-169'], ignorePaths: ['legacy'] });
232
293
  it('prefers the command line over the file', function () {
@@ -41,6 +41,14 @@ export interface LogCheckConfig {
41
41
  * against the project folder.
42
42
  */
43
43
  folder?: string;
44
+ /**
45
+ * Whether the commands that receive a SAS log (`run`, `request`, `job
46
+ * execute`, `flow execute`) fail on it as well as reporting it. Off by
47
+ * default: those commands' exit codes are already load-bearing in pipelines,
48
+ * and a job that prints a notable note is not a failed job. `--logcheck` and
49
+ * `--no-logcheck` override it per run.
50
+ */
51
+ autoCheck?: boolean;
44
52
  }
45
53
  export declare const defaultLogCheckConfig: LogCheckConfig;
46
54
  /** Counts of the issues a log carries, after suppression. */
@@ -35,6 +35,31 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
35
35
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
36
  }
37
37
  };
38
+ var __read = (this && this.__read) || function (o, n) {
39
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
40
+ if (!m) return o;
41
+ var i = m.call(o), r, ar = [], e;
42
+ try {
43
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
44
+ }
45
+ catch (error) { e = { error: error }; }
46
+ finally {
47
+ try {
48
+ if (r && !r.done && (m = i["return"])) m.call(i);
49
+ }
50
+ finally { if (e) throw e.error; }
51
+ }
52
+ return ar;
53
+ };
54
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
55
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
56
+ if (ar || !(i in from)) {
57
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
58
+ ar[i] = from[i];
59
+ }
60
+ }
61
+ return to.concat(ar || Array.prototype.slice.call(from));
62
+ };
38
63
  var __importDefault = (this && this.__importDefault) || function (mod) {
39
64
  return (mod && mod.__esModule) ? mod : { "default": mod };
40
65
  };
@@ -145,7 +170,7 @@ function runSasJob(target, isLocal, sasJobLocation, dataFilePath, configFilePath
145
170
  case 4:
146
171
  _a.sent();
147
172
  _a.label = 5;
148
- case 5: return [4 /*yield*/, saveLogFile(sasjs, sasJobLocation, logFile, jobPath)];
173
+ case 5: return [4 /*yield*/, saveLogFile(sasjs, sasJobLocation, logFile, jobPath, true)];
149
174
  case 6:
150
175
  _a.sent();
151
176
  result = true;
@@ -170,33 +195,47 @@ function runSasJob(target, isLocal, sasJobLocation, dataFilePath, configFilePath
170
195
  });
171
196
  });
172
197
  }
173
- var saveLogFile = function (sasjs, sasJobLocation, logFile, jobPath) { return __awaiter(void 0, void 0, void 0, function () {
174
- var sasRequests, currentRequestLog, logPath, timestamp, filename;
175
- return __generator(this, function (_a) {
176
- switch (_a.label) {
177
- case 0:
178
- sasRequests = sasjs.getSasRequests();
179
- currentRequestLog = sasRequests.find(function (x) { return x.serviceLink === sasJobLocation; });
180
- if (!(currentRequestLog && jobPath)) return [3 /*break*/, 4];
181
- if (!!logFile) return [3 /*break*/, 2];
182
- return [4 /*yield*/, (0, utils_2.isSASjsProject)()];
183
- case 1:
184
- logPath = (_a.sent())
185
- ? path_1.default.join(process.sasjsConstants.buildDestinationResultsFolder, 'requests')
186
- : process.projectDir;
187
- timestamp = (0, utils_1.generateTimestamp)();
188
- filename = sasJobLocation.split('/').pop() // always use forward slash in SAS Drive
189
- ;
190
- logFile = path_1.default.join(logPath, "".concat(filename, "-").concat(timestamp, ".log"));
191
- _a.label = 2;
192
- case 2: return [4 /*yield*/, (0, utils_2.saveLog)(currentRequestLog.logFile, logFile, jobPath || '', false)];
193
- case 3:
194
- _a.sent();
195
- _a.label = 4;
196
- case 4: return [2 /*return*/];
197
- }
198
+ var saveLogFile = function (sasjs_1, sasJobLocation_1, logFile_1, jobPath_1) {
199
+ var args_1 = [];
200
+ for (var _i = 4; _i < arguments.length; _i++) {
201
+ args_1[_i - 4] = arguments[_i];
202
+ }
203
+ return __awaiter(void 0, __spreadArray([sasjs_1, sasJobLocation_1, logFile_1, jobPath_1], __read(args_1), false), void 0, function (sasjs, sasJobLocation, logFile, jobPath, check) {
204
+ var sasRequests, currentRequestLog, logPath, timestamp, filename;
205
+ if (check === void 0) { check = false; }
206
+ return __generator(this, function (_a) {
207
+ switch (_a.label) {
208
+ case 0:
209
+ sasRequests = sasjs.getSasRequests();
210
+ currentRequestLog = sasRequests.find(function (x) { return x.serviceLink === sasJobLocation; });
211
+ if (!(currentRequestLog && jobPath)) return [3 /*break*/, 5];
212
+ if (!!logFile) return [3 /*break*/, 2];
213
+ return [4 /*yield*/, (0, utils_2.isSASjsProject)()];
214
+ case 1:
215
+ logPath = (_a.sent())
216
+ ? path_1.default.join(process.sasjsConstants.buildDestinationResultsFolder, 'requests')
217
+ : process.projectDir;
218
+ timestamp = (0, utils_1.generateTimestamp)();
219
+ filename = sasJobLocation.split('/').pop() // always use forward slash in SAS Drive
220
+ ;
221
+ logFile = path_1.default.join(logPath, "".concat(filename, "-").concat(timestamp, ".log"));
222
+ _a.label = 2;
223
+ case 2: return [4 /*yield*/, (0, utils_2.saveLog)(currentRequestLog.logFile, logFile, jobPath || '', false)
224
+ // after the write, not before: a request that fails the check is the one
225
+ // whose log is wanted on disk
226
+ ];
227
+ case 3:
228
+ _a.sent();
229
+ if (!check) return [3 /*break*/, 5];
230
+ return [4 /*yield*/, (0, utils_2.checkLog)(currentRequestLog.logFile, sasJobLocation)];
231
+ case 4:
232
+ _a.sent();
233
+ _a.label = 5;
234
+ case 5: return [2 /*return*/];
235
+ }
236
+ });
198
237
  });
199
- }); };
238
+ };
200
239
  /**
201
240
  * Writes output to the file
202
241
  * @param outputPathParam path to output file
@@ -58,6 +58,7 @@ var targetCommand_1 = require("../../types/command/targetCommand");
58
58
  var utils_2 = require("../../utils");
59
59
  var request_1 = require("./request");
60
60
  var getLogFilePath_1 = require("../../utils/getLogFilePath");
61
+ var logcheck_1 = require("../../utils/logcheck");
61
62
  var syntax = 'request <sasProgramPath> [options]';
62
63
  var usage = 'sasjs request <sasProgramPath> --data <path-to-datafile> --config <path-to-configfile> --target <target-name> --output <path-to-outputfile> --log <path-to-logfile>';
63
64
  var description = "Lets the user run a SAS job against a specified target.\n" +
@@ -104,6 +105,11 @@ var RequestCommand = /** @class */ (function (_super) {
104
105
  type: 'string',
105
106
  alias: 'o',
106
107
  description: 'path where output of the finished job execution will be saved.'
108
+ },
109
+ logcheck: {
110
+ type: 'boolean',
111
+ default: undefined,
112
+ description: 'Check the SAS log this request received, and fail when logCheck in sasjsconfig.json says so. --no-logcheck switches both the report and the verdict off.'
107
113
  }
108
114
  },
109
115
  syntax: syntax,
@@ -117,7 +123,9 @@ var RequestCommand = /** @class */ (function (_super) {
117
123
  var _a, target, isLocal, jobPath, log, output, authConfig, _b, e_1, _c, sasProgramPath, data, config;
118
124
  return __generator(this, function (_d) {
119
125
  switch (_d.label) {
120
- case 0: return [4 /*yield*/, this.getTargetInfo()];
126
+ case 0:
127
+ (0, logcheck_1.setLogCheckMode)((0, logcheck_1.logCheckModeOf)(this.parsed.logcheck));
128
+ return [4 /*yield*/, this.getTargetInfo()];
121
129
  case 1:
122
130
  _a = _d.sent(), target = _a.target, isLocal = _a.isLocal;
123
131
  jobPath = (0, utils_2.prefixAppLoc)(target.appLoc, process.currentDir);
@@ -152,6 +160,8 @@ var RequestCommand = /** @class */ (function (_super) {
152
160
  return command_1.ReturnCode.Success;
153
161
  })
154
162
  .catch(function (err) {
163
+ if ((0, logcheck_1.isLogCheckFailure)(err))
164
+ return command_1.ReturnCode.LogCheckError;
155
165
  (0, utils_2.displayError)(err, 'An error has occurred when running your SAS job.');
156
166
  return command_1.ReturnCode.InternalError;
157
167
  })];
@@ -100,6 +100,7 @@ var utils_1 = require("@sasjs/utils");
100
100
  var configUtils = __importStar(require("../../../utils/config"));
101
101
  var setConstantsUtils = __importStar(require("../../../utils/setConstants"));
102
102
  var command_1 = require("../../../types/command");
103
+ var logcheck_1 = require("../../../utils/logcheck");
103
104
  var defaultArgs = ['node', 'sasjs'];
104
105
  var target = new utils_1.Target({
105
106
  name: 'test',
@@ -116,6 +117,45 @@ describe('RequestCommand', function () {
116
117
  beforeEach(function () {
117
118
  setupMocks();
118
119
  });
120
+ afterEach(function () { return (0, logcheck_1.setLogCheckMode)('auto'); });
121
+ it('should return the logcheck code when the log check failed the request', function () { return __awaiter(void 0, void 0, void 0, function () {
122
+ var command, _a;
123
+ return __generator(this, function (_b) {
124
+ switch (_b.label) {
125
+ case 0:
126
+ jest
127
+ .spyOn(requestModule, 'runSasJob')
128
+ .mockImplementation(function () { return Promise.reject(new logcheck_1.LogCheckFailure('1 error')); });
129
+ command = new requestCommand_1.RequestCommand(__spreadArray(__spreadArray([], __read(defaultArgs), false), [
130
+ 'request',
131
+ sasProgramPath
132
+ ], false));
133
+ _a = expect;
134
+ return [4 /*yield*/, command.execute()];
135
+ case 1:
136
+ _a.apply(void 0, [_b.sent()]).toEqual(command_1.ReturnCode.LogCheckError);
137
+ return [2 /*return*/];
138
+ }
139
+ });
140
+ }); });
141
+ it('should switch the log check off for --no-logcheck', function () { return __awaiter(void 0, void 0, void 0, function () {
142
+ var command;
143
+ return __generator(this, function (_a) {
144
+ switch (_a.label) {
145
+ case 0:
146
+ command = new requestCommand_1.RequestCommand(__spreadArray(__spreadArray([], __read(defaultArgs), false), [
147
+ 'request',
148
+ sasProgramPath,
149
+ '--no-logcheck'
150
+ ], false));
151
+ return [4 /*yield*/, command.execute()];
152
+ case 1:
153
+ _a.sent();
154
+ expect((0, logcheck_1.getLogCheckMode)()).toEqual('off');
155
+ return [2 /*return*/];
156
+ }
157
+ });
158
+ }); });
119
159
  it('should parse sasjs request command', function () { return __awaiter(void 0, void 0, void 0, function () {
120
160
  var args, command, targetInfo;
121
161
  return __generator(this, function (_a) {
@@ -6,6 +6,4 @@ import { Target } from '@sasjs/utils';
6
6
  * @param {boolean} compile - compiles sas file present at 'filePath' before running code.
7
7
  * @param {string} logFile - (Optional) Path to log file.
8
8
  */
9
- export declare function runSasCode(target: Target, filePath: string, compile?: boolean, logFile?: string, source?: string): Promise<{
10
- log: any;
11
- }>;
9
+ export declare function runSasCode(target: Target, filePath: string, compile?: boolean, logFile?: string, source?: string): Promise<any>;
@@ -74,6 +74,7 @@ var utils_2 = require("../../utils/");
74
74
  var axios_1 = __importDefault(require("axios"));
75
75
  var saveLog_1 = require("../../utils/saveLog");
76
76
  var parseSourceFile_1 = require("../../utils/parseSourceFile");
77
+ var logcheck_1 = require("../../utils/logcheck");
77
78
  /**
78
79
  * Runs SAS code from a given file on the specified target.
79
80
  * @param {Target} target - the target to run the SAS code on.
@@ -83,7 +84,7 @@ var parseSourceFile_1 = require("../../utils/parseSourceFile");
83
84
  */
84
85
  function runSasCode(target_1, filePath_1) {
85
86
  return __awaiter(this, arguments, void 0, function (target, filePath, compile, logFile, source) {
86
- var isTempFile, sasFilePath, sasFileContent, linesToExecute, macroVars, macroVarStatements, _a;
87
+ var isTempFile, sasFilePath, sasFileContent, linesToExecute, macroVars, macroVarStatements, result, _a;
87
88
  var _b;
88
89
  var _c;
89
90
  if (compile === void 0) { compile = false; }
@@ -137,12 +138,26 @@ function runSasCode(target_1, filePath_1) {
137
138
  }
138
139
  return [3 /*break*/, 17];
139
140
  case 11: return [4 /*yield*/, executeOnSasViya(filePath, target, linesToExecute, logFile)];
140
- case 12: return [2 /*return*/, _d.sent()];
141
+ case 12:
142
+ result = _d.sent();
143
+ return [3 /*break*/, 17];
141
144
  case 13: return [4 /*yield*/, executeOnSas9(target, linesToExecute, logFile)];
142
- case 14: return [2 /*return*/, _d.sent()];
145
+ case 14:
146
+ result = _d.sent();
147
+ return [3 /*break*/, 17];
143
148
  case 15: return [4 /*yield*/, executeOnSasJS(filePath, target, linesToExecute, logFile)];
144
- case 16: return [2 /*return*/, _d.sent()];
145
- case 17: return [2 /*return*/];
149
+ case 16:
150
+ result = _d.sent();
151
+ _d.label = 17;
152
+ case 17:
153
+ // Only a run that claims to have worked is worth checking: an executor that
154
+ // threw has already written the log it failed on, truncated at the failure.
155
+ return [4 /*yield*/, (0, logcheck_1.checkLog)(result === null || result === void 0 ? void 0 : result.log, path_1.default.basename(filePath))];
156
+ case 18:
157
+ // Only a run that claims to have worked is worth checking: an executor that
158
+ // threw has already written the log it failed on, truncated at the failure.
159
+ _d.sent();
160
+ return [2 /*return*/, result];
146
161
  }
147
162
  });
148
163
  });
@@ -55,6 +55,7 @@ exports.RunCommand = void 0;
55
55
  var command_1 = require("../../types/command");
56
56
  var targetCommand_1 = require("../../types/command/targetCommand");
57
57
  var utils_1 = require("../../utils");
58
+ var logcheck_1 = require("../../utils/logcheck");
58
59
  var run_1 = require("./run");
59
60
  var syntax = 'run <sasFilePath> [options]';
60
61
  var usage = 'sasjs run <sasFilePath> --target <target-name>';
@@ -103,6 +104,11 @@ var RunCommand = /** @class */ (function (_super) {
103
104
  type: 'string',
104
105
  alias: 's',
105
106
  description: 'Path to an input JSON containing job variables.'
107
+ },
108
+ logcheck: {
109
+ type: 'boolean',
110
+ default: undefined,
111
+ description: 'Check the SAS log this command received, and fail when logCheck in sasjsconfig.json says so. --no-logcheck switches both the report and the verdict off.'
106
112
  }
107
113
  },
108
114
  syntax: syntax,
@@ -120,11 +126,14 @@ var RunCommand = /** @class */ (function (_super) {
120
126
  case 1:
121
127
  target = (_b.sent()).target;
122
128
  _a = this.parsed, sasFilePath = _a.sasFilePath, compile = _a.compile, log = _a.log, source = _a.source;
129
+ (0, logcheck_1.setLogCheckMode)((0, logcheck_1.logCheckModeOf)(this.parsed.logcheck));
123
130
  return [4 /*yield*/, (0, run_1.runSasCode)(target, sasFilePath, compile, log, source)
124
131
  .then(function () {
125
132
  return command_1.ReturnCode.Success;
126
133
  })
127
134
  .catch(function (err) {
135
+ if ((0, logcheck_1.isLogCheckFailure)(err))
136
+ return command_1.ReturnCode.LogCheckError;
128
137
  (0, utils_1.displayError)(err, 'An error has occurred when running your SAS code.');
129
138
  return command_1.ReturnCode.InternalError;
130
139
  })];