@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
package/README.md CHANGED
@@ -23,7 +23,7 @@
23
23
  - Optionally synchronise a local folder onto the server filesystem using `sasjs fs`
24
24
  - Trigger Viya Jobs & Flows (`sasjs job execute` / `sasjs flow execute`)
25
25
  - Enforce code quality rules with `sasjs lint`
26
- - Check what SAS actually printed, and fail a pipeline on it, with `sasjs logcheck`
26
+ - Check what SAS actually printed, and fail a pipeline on it, with `sasjs logcheck` - the same check reports the log of `run`, `request`, `job execute` and `flow execute`, and can fail those commands too via `logCheck.autoCheck`
27
27
  - Generate HTML5 documentation (requires doxygen) with `sasjs doc`
28
28
  - Create and execute SAS tests (`sasjs test`)
29
29
 
@@ -60,6 +60,7 @@ var command_1 = require("../../types/command");
60
60
  var targetCommand_1 = require("../../types/command/targetCommand");
61
61
  var utils_1 = require("../../utils");
62
62
  var execute_1 = require("./execute");
63
+ var logcheck_1 = require("../../utils/logcheck");
63
64
  var FlowSubCommand;
64
65
  (function (FlowSubCommand) {
65
66
  FlowSubCommand["Execute"] = "execute";
@@ -97,6 +98,11 @@ var executeParseOptions = {
97
98
  streamLog: {
98
99
  type: 'boolean',
99
100
  description: 'Flag indicating whether the logs should be streamed to a local file as the job executes.'
101
+ },
102
+ logcheck: {
103
+ type: 'boolean',
104
+ default: undefined,
105
+ description: 'Check the log of every job in the flow, and fail when logCheck in sasjsconfig.json says so. --no-logcheck switches both the report and the verdict off. A log streamed with --streamLog goes straight to its file and is not checked.'
100
106
  }
101
107
  };
102
108
  var FlowCommand = /** @class */ (function (_super) {
@@ -117,7 +123,9 @@ var FlowCommand = /** @class */ (function (_super) {
117
123
  var target, sasjs, authConfig, _a;
118
124
  return __generator(this, function (_b) {
119
125
  switch (_b.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
  target = (_b.sent()).target;
123
131
  sasjs = (0, utils_1.getSASjs)(target);
@@ -160,6 +168,8 @@ var FlowCommand = /** @class */ (function (_super) {
160
168
  return command_1.ReturnCode.Success;
161
169
  })
162
170
  .catch(function (err) {
171
+ if ((0, logcheck_1.isLogCheckFailure)(err))
172
+ return command_1.ReturnCode.LogCheckError;
163
173
  (0, utils_1.displayError)(err, 'An error has occurred when executing this flow.');
164
174
  return command_1.ReturnCode.InternalError;
165
175
  })];
@@ -44,6 +44,7 @@ var utils_1 = require("../../../utils");
44
44
  var _1 = require(".");
45
45
  var csvColumns_1 = __importDefault(require("./csvColumns"));
46
46
  var flow_1 = require("../../../types/flow");
47
+ var logcheck_1 = require("../../../utils/logcheck");
47
48
  var executeFlow = function (flow, sasjs, pollOptions, target, authConfig, csvFile) { return __awaiter(void 0, void 0, void 0, function () {
48
49
  var logFolder, flowName, jobStatus, flowStatus;
49
50
  return __generator(this, function (_a) {
@@ -55,10 +56,10 @@ var executeFlow = function (flow, sasjs, pollOptions, target, authConfig, csvFil
55
56
  jobStatus = true, flowStatus = { terminate: false, message: '' };
56
57
  flow.execution = 'started';
57
58
  return [4 /*yield*/, Promise.all(flow.jobs.map(function (job) { return __awaiter(void 0, void 0, void 0, function () {
58
- var jobLocation, logName, submittedJob, details, predecessors, data;
59
- var _a, _b;
60
- return __generator(this, function (_c) {
61
- switch (_c.label) {
59
+ var jobLocation, logName, submittedJob, details, jobLogText, savedLog, predecessors, data;
60
+ var _a, _b, _c;
61
+ return __generator(this, function (_d) {
62
+ switch (_d.label) {
62
63
  case 0:
63
64
  jobLocation = (0, utils_1.prefixAppLoc)(target.appLoc, job.location);
64
65
  (0, utils_1.displaySuccess)("'".concat(job.location, "' has been submitted for execution."));
@@ -71,7 +72,7 @@ var executeFlow = function (flow, sasjs, pollOptions, target, authConfig, csvFil
71
72
  contextName: target.contextName
72
73
  }, authConfig, true, pollOptions, true, job.macroVars, undefined, (0, utils_1.persistTokensRefreshedByAdapter)(target))
73
74
  .catch(function (err) { return __awaiter(void 0, void 0, void 0, function () {
74
- var logName, predecessors, data;
75
+ var savedLog, predecessors, data;
75
76
  var _a, _b, _c, _d;
76
77
  return __generator(this, function (_e) {
77
78
  switch (_e.label) {
@@ -88,8 +89,10 @@ var executeFlow = function (flow, sasjs, pollOptions, target, authConfig, csvFil
88
89
  (0, utils_1.displayError)(err, 'Error while saving log file.');
89
90
  })];
90
91
  case 1:
91
- logName = _e.sent();
92
- logName = logName ? "".concat(logFolder, "/").concat(logName) : '';
92
+ savedLog = _e.sent();
93
+ logName = (savedLog === null || savedLog === void 0 ? void 0 : savedLog.fileName)
94
+ ? "".concat(logFolder, "/").concat(savedLog.fileName)
95
+ : '';
93
96
  predecessors = ((_c = flow.predecessors) === null || _c === void 0 ? void 0 : _c.length)
94
97
  ? flow.predecessors
95
98
  : ['none'];
@@ -103,9 +106,18 @@ var executeFlow = function (flow, sasjs, pollOptions, target, authConfig, csvFil
103
106
  ];
104
107
  return [4 /*yield*/, (0, _1.saveToCsv)(csvFile, data, Object.values(csvColumns_1.default), 'id').catch(function (err) {
105
108
  (0, utils_1.displayError)(err, 'Error while saving CSV file.');
106
- })];
109
+ })
110
+ // after the report and the log file: a check that fails is still a job
111
+ // someone has to find in the CSV, with its log on disk
112
+ ];
107
113
  case 2:
108
114
  _e.sent();
115
+ if (!(savedLog === null || savedLog === void 0 ? void 0 : savedLog.logText)) return [3 /*break*/, 4];
116
+ return [4 /*yield*/, (0, logcheck_1.checkLog)(savedLog.logText, "".concat(flowName, "/").concat(jobLocation))];
117
+ case 3:
118
+ _e.sent();
119
+ _e.label = 4;
120
+ case 4:
109
121
  job.status = flow_1.FlowWaveJobStatus.Failure;
110
122
  if (logName) {
111
123
  (_d = process.logger) === null || _d === void 0 ? void 0 : _d.info("Log file located at: ".concat((0, _1.normalizeFilePath)(logName)));
@@ -120,20 +132,24 @@ var executeFlow = function (flow, sasjs, pollOptions, target, authConfig, csvFil
120
132
  });
121
133
  }); })];
122
134
  case 1:
123
- submittedJob = _c.sent();
124
- if (!(submittedJob === null || submittedJob === void 0 ? void 0 : submittedJob.job)) return [3 /*break*/, 5];
135
+ submittedJob = _d.sent();
136
+ if (!(submittedJob === null || submittedJob === void 0 ? void 0 : submittedJob.job)) return [3 /*break*/, 7];
125
137
  submittedJob = submittedJob.job;
126
138
  details = (0, _1.parseJobDetails)(submittedJob);
139
+ jobLogText = '';
127
140
  if (!!pollOptions.streamLog) return [3 /*break*/, 3];
128
141
  return [4 /*yield*/, (0, _1.saveLog)(submittedJob.links, flowName, jobLocation, logFolder, sasjs, target.serverUrl, authConfig.access_token, details === null || details === void 0 ? void 0 : details.lineCount).catch(function (err) {
129
142
  (0, utils_1.displayError)(err, 'Error while saving log file.');
130
143
  })];
131
144
  case 2:
132
- logName = (_c.sent());
133
- logName = logName ? "".concat(logFolder, "/").concat(logName) : '';
134
- _c.label = 3;
145
+ savedLog = _d.sent();
146
+ logName = (savedLog === null || savedLog === void 0 ? void 0 : savedLog.fileName)
147
+ ? "".concat(logFolder, "/").concat(savedLog.fileName)
148
+ : '';
149
+ jobLogText = (_a = savedLog === null || savedLog === void 0 ? void 0 : savedLog.logText) !== null && _a !== void 0 ? _a : '';
150
+ _d.label = 3;
135
151
  case 3:
136
- predecessors = ((_a = flow.predecessors) === null || _a === void 0 ? void 0 : _a.length)
152
+ predecessors = ((_b = flow.predecessors) === null || _b === void 0 ? void 0 : _b.length)
137
153
  ? flow.predecessors
138
154
  : ['none'];
139
155
  data = [
@@ -144,9 +160,18 @@ var executeFlow = function (flow, sasjs, pollOptions, target, authConfig, csvFil
144
160
  logName,
145
161
  details === null || details === void 0 ? void 0 : details.details
146
162
  ];
147
- return [4 /*yield*/, (0, _1.saveToCsv)(csvFile, data, Object.values(csvColumns_1.default), 'id').catch(function (err) { return (0, utils_1.displayError)(err, 'Error while saving CSV file.'); })];
163
+ return [4 /*yield*/, (0, _1.saveToCsv)(csvFile, data, Object.values(csvColumns_1.default), 'id').catch(function (err) { return (0, utils_1.displayError)(err, 'Error while saving CSV file.'); })
164
+ // after the report and the log file: a check that fails is still a job
165
+ // someone has to find in the CSV, with its log on disk
166
+ ];
148
167
  case 4:
149
- _c.sent();
168
+ _d.sent();
169
+ if (!jobLogText) return [3 /*break*/, 6];
170
+ return [4 /*yield*/, (0, logcheck_1.checkLog)(jobLogText, "".concat(flowName, "/").concat(jobLocation))];
171
+ case 5:
172
+ _d.sent();
173
+ _d.label = 6;
174
+ case 6:
150
175
  job.status =
151
176
  submittedJob.state === 'completed'
152
177
  ? 'success'
@@ -160,7 +185,7 @@ var executeFlow = function (flow, sasjs, pollOptions, target, authConfig, csvFil
160
185
  : ''));
161
186
  }
162
187
  if (logName) {
163
- (_b = process.logger) === null || _b === void 0 ? void 0 : _b.info("Log file located at: ".concat((0, _1.normalizeFilePath)(logName)));
188
+ (_c = process.logger) === null || _c === void 0 ? void 0 : _c.info("Log file located at: ".concat((0, _1.normalizeFilePath)(logName)));
164
189
  }
165
190
  if (flow.jobs.filter(function (j) { return j.status === 'success'; })
166
191
  .length === flow.jobs.length) {
@@ -171,8 +196,8 @@ var executeFlow = function (flow, sasjs, pollOptions, target, authConfig, csvFil
171
196
  (0, utils_1.displayError)({}, "'".concat(flowName, "' flow failed!"));
172
197
  jobStatus = false;
173
198
  }
174
- _c.label = 5;
175
- case 5: return [2 /*return*/];
199
+ _d.label = 7;
200
+ case 7: return [2 /*return*/];
176
201
  }
177
202
  });
178
203
  }); }))];
@@ -1,2 +1,13 @@
1
1
  import SASjs from '@sasjs/adapter/node';
2
- export declare const saveLog: (links: any[], flowName: string, jobLocation: string, logFolder: string, sasjs: SASjs, serverUrl: string, access_token: string, lineCount?: number) => Promise<string | null>;
2
+ /**
3
+ * A job's log, both as written and as text.
4
+ *
5
+ * The caller checks the text after the file is on disk: a job whose log fails
6
+ * the check is exactly the job whose log is wanted for reading, so writing it
7
+ * first is the difference between a diagnosable failure and an empty folder.
8
+ */
9
+ export interface SavedFlowLog {
10
+ fileName: string | null;
11
+ logText: string;
12
+ }
13
+ export declare const saveLog: (links: any[], flowName: string, jobLocation: string, logFolder: string, sasjs: SASjs, serverUrl: string, access_token: string, lineCount?: number) => Promise<SavedFlowLog>;
@@ -108,8 +108,8 @@ var saveLog = function (links_1, flowName_1, jobLocation_1, logFolder_1, sasjs_1
108
108
  })];
109
109
  case 5:
110
110
  _a.sent();
111
- return [2 /*return*/, logName];
112
- case 6: return [2 /*return*/, null];
111
+ return [2 /*return*/, { fileName: logName, logText: logParsed }];
112
+ case 6: return [2 /*return*/, { fileName: null, logText: '' }];
113
113
  }
114
114
  });
115
115
  });
@@ -78,6 +78,7 @@ var executeFlow_1 = require("../executeFlow");
78
78
  var saveLogModule = __importStar(require("../saveLog"));
79
79
  var saveToCsvModule = __importStar(require("../saveToCsv"));
80
80
  var normalizeFilePath = __importStar(require("../normalizeFilePath"));
81
+ var logcheckModule = __importStar(require("../../../../utils/logcheck"));
81
82
  var csvColumns_1 = __importDefault(require("../csvColumns"));
82
83
  var pollOptions = {
83
84
  maxPollCount: 0,
@@ -103,7 +104,9 @@ describe('executeFlow', function () {
103
104
  beforeEach(function () {
104
105
  jest
105
106
  .spyOn(saveLogModule, 'saveLog')
106
- .mockImplementation(function () { return Promise.resolve(fakeLogFileName); });
107
+ .mockImplementation(function () {
108
+ return Promise.resolve({ fileName: fakeLogFileName, logText: '' });
109
+ });
107
110
  jest
108
111
  .spyOn(saveToCsvModule, 'saveToCsv')
109
112
  .mockImplementation(function () { return Promise.resolve(); });
@@ -244,6 +247,46 @@ describe('executeFlow', function () {
244
247
  }
245
248
  });
246
249
  }); });
250
+ it('writes the report and the log before a failing check stops the flow', function () { return __awaiter(void 0, void 0, void 0, function () {
251
+ var flowName, flow, saveLogSpy, csvSpy, checkSpy;
252
+ return __generator(this, function (_a) {
253
+ switch (_a.label) {
254
+ case 0:
255
+ flowName = 'checkedFlow';
256
+ flow = {
257
+ name: flowName,
258
+ jobs: [{ location: 'jobs/testJob/job' }],
259
+ predecessors: []
260
+ };
261
+ jest
262
+ .spyOn(sasjs, 'startComputeJob')
263
+ .mockResolvedValue({ job: { state: 'success' } });
264
+ saveLogSpy = jest
265
+ .spyOn(saveLogModule, 'saveLog')
266
+ .mockImplementation(function () {
267
+ return Promise.resolve({ fileName: fakeLogFileName, logText: 'ERROR 22-322' });
268
+ });
269
+ csvSpy = jest
270
+ .spyOn(saveToCsvModule, 'saveToCsv')
271
+ .mockImplementation(function () { return Promise.resolve(); });
272
+ checkSpy = jest
273
+ .spyOn(logcheckModule, 'checkLog')
274
+ .mockRejectedValue(new Error('SAS log check failed'));
275
+ return [4 /*yield*/, expect((0, executeFlow_1.executeFlow)(flow, sasjs, pollOptions, target, {}, '')).rejects.toThrow('SAS log check failed')
276
+ // the job whose log fails the check is the job a reader has to find in the
277
+ // CSV, with its log on disk
278
+ ];
279
+ case 1:
280
+ _a.sent();
281
+ // the job whose log fails the check is the job a reader has to find in the
282
+ // CSV, with its log on disk
283
+ expect(saveLogSpy.mock.invocationCallOrder[0]).toBeLessThan(checkSpy.mock.invocationCallOrder[0]);
284
+ expect(csvSpy.mock.invocationCallOrder[0]).toBeLessThan(checkSpy.mock.invocationCallOrder[0]);
285
+ checkSpy.mockRestore();
286
+ return [2 /*return*/];
287
+ }
288
+ });
289
+ }); });
247
290
  });
248
291
  var validateSaveToCsv = function (jobExecutionInOrder) {
249
292
  expect(saveToCsvModule.saveToCsv).toHaveBeenCalledTimes(jobExecutionInOrder.length);
@@ -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',
@@ -115,6 +116,49 @@ describe('FlowCommand', function () {
115
116
  beforeEach(function () {
116
117
  setupMocks();
117
118
  });
119
+ afterEach(function () { return (0, logcheck_1.setLogCheckMode)('auto'); });
120
+ it('should return the logcheck code when the log check failed the flow', function () { return __awaiter(void 0, void 0, void 0, function () {
121
+ var command, _a;
122
+ return __generator(this, function (_b) {
123
+ switch (_b.label) {
124
+ case 0:
125
+ jest
126
+ .spyOn(executeModule, 'execute')
127
+ .mockImplementation(function () { return Promise.reject(new logcheck_1.LogCheckFailure('3 errors')); });
128
+ command = new flowCommand_1.FlowCommand(__spreadArray(__spreadArray([], __read(defaultArgs), false), [
129
+ 'flow',
130
+ 'execute',
131
+ '--source',
132
+ source
133
+ ], false));
134
+ _a = expect;
135
+ return [4 /*yield*/, command.execute()];
136
+ case 1:
137
+ _a.apply(void 0, [_b.sent()]).toEqual(command_1.ReturnCode.LogCheckError);
138
+ return [2 /*return*/];
139
+ }
140
+ });
141
+ }); });
142
+ it('should switch the log check off for --no-logcheck', function () { return __awaiter(void 0, void 0, void 0, function () {
143
+ var command;
144
+ return __generator(this, function (_a) {
145
+ switch (_a.label) {
146
+ case 0:
147
+ command = new flowCommand_1.FlowCommand(__spreadArray(__spreadArray([], __read(defaultArgs), false), [
148
+ 'flow',
149
+ 'execute',
150
+ '--source',
151
+ source,
152
+ '--no-logcheck'
153
+ ], false));
154
+ return [4 /*yield*/, command.execute()];
155
+ case 1:
156
+ _a.sent();
157
+ expect((0, logcheck_1.getLogCheckMode)()).toEqual('off');
158
+ return [2 /*return*/];
159
+ }
160
+ });
161
+ }); });
118
162
  it('should parse sasjs flow execute command', function () { return __awaiter(void 0, void 0, void 0, function () {
119
163
  var args, command, targetInfo;
120
164
  return __generator(this, function (_a) {
@@ -7,6 +7,6 @@ export declare class FSCommand extends TargetCommand {
7
7
  getOutputPath(): Promise<string>;
8
8
  execute(): Promise<ReturnCode>;
9
9
  compile(): Promise<ReturnCode>;
10
- deploy(): Promise<ReturnCode.Success | ReturnCode.InternalError>;
11
- sync(): Promise<ReturnCode.Success | ReturnCode.InternalError>;
10
+ deploy(): Promise<ReturnCode.Success | ReturnCode.InternalError | ReturnCode.LogCheckError>;
11
+ sync(): Promise<ReturnCode.Success | ReturnCode.InternalError | ReturnCode.LogCheckError>;
12
12
  }
@@ -113,6 +113,7 @@ var utils_2 = require("@sasjs/utils");
113
113
  // bundle that imported anything from the barrel (sasjs/utils#275, #283).
114
114
  var fs_1 = require("@sasjs/utils/fs");
115
115
  var executeCode_1 = require("./internal/executeCode");
116
+ var logcheck_1 = require("../../utils/logcheck");
116
117
  var FsSubCommand;
117
118
  (function (FsSubCommand) {
118
119
  FsSubCommand["Compile"] = "compile";
@@ -123,7 +124,12 @@ var syntax = 'fs <subCommand> [options]';
123
124
  var usage = 'Usage: sasjs fs <subCommand> [options]';
124
125
  var description = 'Handles operations around file system synchronisation';
125
126
  var parseOptions = {
126
- output: { type: 'string', alias: 'o' }
127
+ output: { type: 'string', alias: 'o' },
128
+ logcheck: {
129
+ type: 'boolean',
130
+ default: undefined,
131
+ 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.'
132
+ }
127
133
  };
128
134
  var compileCommandSyntax = 'fs <subCommand> <local> [options]';
129
135
  var compileCommandUsage = 'sasjs fs compile <localFolder> [options]';
@@ -243,6 +249,7 @@ var FSCommand = /** @class */ (function (_super) {
243
249
  return __generator(this, function (_a) {
244
250
  switch (_a.label) {
245
251
  case 0:
252
+ (0, logcheck_1.setLogCheckMode)((0, logcheck_1.logCheckModeOf)(this.parsed.logcheck));
246
253
  if (!(this.parsed.subCommand === FsSubCommand.Compile)) return [3 /*break*/, 2];
247
254
  return [4 /*yield*/, this.compile()];
248
255
  case 1: return [2 /*return*/, _a.sent()];
@@ -324,8 +331,18 @@ var FSCommand = /** @class */ (function (_super) {
324
331
  var log = _b.log;
325
332
  return __generator(this, function (_c) {
326
333
  switch (_c.label) {
327
- case 0: return [4 /*yield*/, (0, utils_1.saveLog)(log, path_1.default.join(outputFolder, 'deploy.log'), '', false)];
334
+ case 0: return [4 /*yield*/, (0, utils_1.saveLog)(log, path_1.default.join(outputFolder, 'deploy.log'), '', false)
335
+ // after the log file is written: a deploy whose log fails the check is
336
+ // the deploy whose record someone needs to read
337
+ ];
328
338
  case 1:
339
+ _c.sent();
340
+ // after the log file is written: a deploy whose log fails the check is
341
+ // the deploy whose record someone needs to read
342
+ return [4 /*yield*/, (0, logcheck_1.checkLog)(log, 'fs deploy')];
343
+ case 2:
344
+ // after the log file is written: a deploy whose log fails the check is
345
+ // the deploy whose record someone needs to read
329
346
  _c.sent();
330
347
  return [2 /*return*/];
331
348
  }
@@ -337,6 +354,10 @@ var FSCommand = /** @class */ (function (_super) {
337
354
  return [2 /*return*/, command_1.ReturnCode.Success];
338
355
  case 7:
339
356
  error_1 = _f.sent();
357
+ // the verdict the caller asked for, not an internal failure: it carries
358
+ // its own exit code and has already printed the report
359
+ if ((0, logcheck_1.isLogCheckFailure)(error_1))
360
+ return [2 /*return*/, command_1.ReturnCode.LogCheckError];
340
361
  logPath = path_1.default.join(outputFolder, 'error.log');
341
362
  return [4 /*yield*/, createErrorLogFile(error_1, logPath)];
342
363
  case 8:
@@ -386,7 +407,7 @@ var FSCommand = /** @class */ (function (_super) {
386
407
  outputFolder = _v.sent();
387
408
  _v.label = 2;
388
409
  case 2:
389
- _v.trys.push([2, 15, , 17]);
410
+ _v.trys.push([2, 17, , 19]);
390
411
  (_h = process.logger) === null || _h === void 0 ? void 0 : _h.info('generating program to get remote hash');
391
412
  return [4 /*yield*/, (0, fs_1.generateProgramToGetRemoteHash)(obj.remote)];
392
413
  case 3:
@@ -400,15 +421,18 @@ var FSCommand = /** @class */ (function (_super) {
400
421
  log = (_v.sent()).log;
401
422
  return [4 /*yield*/, (0, utils_1.saveLog)(log, path_1.default.join(outputFolder, 'getRemoteHash.log'), '', false)];
402
423
  case 6:
424
+ _v.sent();
425
+ return [4 /*yield*/, (0, logcheck_1.checkLog)(log, 'fs sync')];
426
+ case 7:
403
427
  _v.sent();
404
428
  (_k = process.logger) === null || _k === void 0 ? void 0 : _k.info('extracting hashes from log');
405
429
  remoteHashes = (0, fs_1.extractHashArray)(log);
406
430
  return [4 /*yield*/, createHashFile(JSON.stringify(remoteHashes, null, 2), path_1.default.join(outputFolder, 'hashesBeforeSync.json'))];
407
- case 7:
431
+ case 8:
408
432
  _v.sent();
409
433
  (_l = process.logger) === null || _l === void 0 ? void 0 : _l.info('creating the hash of local folder');
410
434
  return [4 /*yield*/, (0, fs_1.getHash)((0, utils_2.getAbsolutePath)(obj.local, process.projectDir))];
411
- case 8:
435
+ case 9:
412
436
  localHash = _v.sent();
413
437
  remoteHashMap_1 = remoteHashes.reduce(function (map, item) {
414
438
  var from = obj.remote.replace(/\//g, path_1.default.sep);
@@ -424,25 +448,28 @@ var FSCommand = /** @class */ (function (_super) {
424
448
  (_o = process.logger) === null || _o === void 0 ? void 0 : _o.info('Extract differences from local and remote hash');
425
449
  hashedDiff = (0, fs_1.compareHashes)(localHash, remoteHashMap_1);
426
450
  return [4 /*yield*/, createHashFile(JSON.stringify(hashedDiff, null, 2), path_1.default.join(outputFolder, 'hashesDiff.json'))];
427
- case 9:
451
+ case 10:
428
452
  _v.sent();
429
453
  (_p = process.logger) === null || _p === void 0 ? void 0 : _p.info('generating program to sync differences');
430
454
  return [4 /*yield*/, (0, fs_1.generateProgramToSyncHashDiff)(hashedDiff, obj.remote)];
431
- case 10:
455
+ case 11:
432
456
  syncProgram = _v.sent();
433
457
  return [4 /*yield*/, createProgramFile(syncProgram, path_1.default.join(outputFolder, 'syncProgram.sas'))];
434
- case 11:
458
+ case 12:
435
459
  _v.sent();
436
460
  (_q = process.logger) === null || _q === void 0 ? void 0 : _q.info('executing program to sync differences');
437
461
  return [4 /*yield*/, (0, executeCode_1.executeCode)(target, syncProgram)];
438
- case 12:
462
+ case 13:
439
463
  syncLog = (_v.sent()).log;
440
464
  return [4 /*yield*/, (0, utils_1.saveLog)(syncLog, path_1.default.join(outputFolder, 'sync.log'), '', false)];
441
- case 13:
465
+ case 14:
466
+ _v.sent();
467
+ return [4 /*yield*/, (0, logcheck_1.checkLog)(syncLog, 'fs sync')];
468
+ case 15:
442
469
  _v.sent();
443
470
  syncedHash = (0, fs_1.extractHashArray)(syncLog);
444
471
  return [4 /*yield*/, createHashFile(JSON.stringify(syncedHash, null, 2), path_1.default.join(outputFolder, 'hashesAfterSync.json'))];
445
- case 14:
472
+ case 16:
446
473
  _v.sent();
447
474
  syncedHashMap = syncedHash.reduce(function (map, item) {
448
475
  var from = obj.remote.replace(/\//g, path_1.default.sep);
@@ -472,16 +499,18 @@ var FSCommand = /** @class */ (function (_super) {
472
499
  (_a = process.logger) === null || _a === void 0 ? void 0 : _a.log("* ".concat(item));
473
500
  });
474
501
  }
475
- return [3 /*break*/, 17];
476
- case 15:
502
+ return [3 /*break*/, 19];
503
+ case 17:
477
504
  error_2 = _v.sent();
505
+ if ((0, logcheck_1.isLogCheckFailure)(error_2))
506
+ return [2 /*return*/, { value: command_1.ReturnCode.LogCheckError }];
478
507
  logPath = path_1.default.join(outputFolder, 'error.log');
479
508
  return [4 /*yield*/, createErrorLogFile(error_2, logPath)];
480
- case 16:
509
+ case 18:
481
510
  _v.sent();
482
511
  (_t = process.logger) === null || _t === void 0 ? void 0 : _t.error("An error has occurred. For more info see ".concat(logPath));
483
512
  return [2 /*return*/, { value: command_1.ReturnCode.InternalError }];
484
- case 17: return [2 /*return*/];
513
+ case 19: return [2 /*return*/];
485
514
  }
486
515
  });
487
516
  };
@@ -1,4 +1,10 @@
1
1
  import { Target } from '@sasjs/utils';
2
+ /**
3
+ * Runs generated SAS and returns the log it came back with.
4
+ *
5
+ * The caller saves that log and then checks it - not here, because a check that
6
+ * throws before the log file is written is a failed run nobody can read.
7
+ */
2
8
  export declare const executeCode: (target: Target, code: string) => Promise<{
3
9
  log: any;
4
10
  }>;
@@ -39,6 +39,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.executeCode = void 0;
40
40
  var utils_1 = require("@sasjs/utils");
41
41
  var utils_2 = require("../../../utils/");
42
+ /**
43
+ * Runs generated SAS and returns the log it came back with.
44
+ *
45
+ * The caller saves that log and then checks it - not here, because a check that
46
+ * throws before the log file is written is a failed run nobody can read.
47
+ */
42
48
  var executeCode = function (target, code) { return __awaiter(void 0, void 0, void 0, function () {
43
49
  return __generator(this, function (_a) {
44
50
  switch (_a.label) {
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.executeJobSas9 = executeJobSas9;
40
40
  var utils_1 = require("../utils");
41
41
  var parseSourceFile_1 = require("../../../../utils/parseSourceFile");
42
+ var logcheck_1 = require("../../../../utils/logcheck");
42
43
  /**
43
44
  * Triggers an existing Stored Process for execution as a "job". See online documentation here: https://cli.sasjs.io/job/
44
45
  * @param {object} sasjs - Configuration object of the SASJS adapter.
@@ -70,30 +71,32 @@ function executeJobSas9(sasjs, config, jobPath, logFile, output, source) {
70
71
  case 3:
71
72
  result = _a.sent();
72
73
  endTime = new Date().getTime();
73
- if (!result) return [3 /*break*/, 9];
74
- if (!(result.status === 200)) return [3 /*break*/, 8];
74
+ if (!result) return [3 /*break*/, 10];
75
+ if (!(result.status === 200)) return [3 /*break*/, 9];
75
76
  // handle success
76
77
  process.logger.success("Job executed successfully! in ".concat((endTime - startTime) / 1000, " seconds"));
77
78
  if (!(!!logFile && result.log)) return [3 /*break*/, 5];
78
- return [4 /*yield*/, (0, utils_1.saveLog)(result.log, logFile, jobPath)
79
- // save output if it is present
80
- ];
79
+ return [4 /*yield*/, (0, utils_1.saveLog)(result.log, logFile, jobPath)];
81
80
  case 4:
82
81
  _a.sent();
83
82
  _a.label = 5;
84
- case 5:
85
- if (!(!!output && result.result)) return [3 /*break*/, 7];
86
- return [4 /*yield*/, (0, utils_1.saveOutput)(result.result, output)];
83
+ case 5: return [4 /*yield*/, (0, logcheck_1.checkLog)(result.log, jobPath)
84
+ // save output if it is present
85
+ ];
87
86
  case 6:
88
87
  _a.sent();
89
- _a.label = 7;
90
- case 7: return [3 /*break*/, 9];
91
- case 8:
88
+ if (!(!!output && result.result)) return [3 /*break*/, 8];
89
+ return [4 /*yield*/, (0, utils_1.saveOutput)(result.result, output)];
90
+ case 7:
91
+ _a.sent();
92
+ _a.label = 8;
93
+ case 8: return [3 /*break*/, 10];
94
+ case 9:
92
95
  // handle failure
93
96
  process.logger.error(result.message);
94
97
  process.logger.error(JSON.stringify(result.error, null, 2));
95
- _a.label = 9;
96
- case 9: return [2 /*return*/, result];
98
+ _a.label = 10;
99
+ case 10: return [2 /*return*/, result];
97
100
  }
98
101
  });
99
102
  });
@@ -38,6 +38,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.executeJobSasjs = executeJobSasjs;
40
40
  var utils_1 = require("../utils");
41
+ var logcheck_1 = require("../../../../utils/logcheck");
41
42
  /**
42
43
  * Triggers existing job for execution on SASJS server.
43
44
  * @param {object} sasjs - configuration object of SAS adapter.
@@ -58,23 +59,25 @@ function executeJobSasjs(sasjs, target, jobPath, logFile, output, authConfig) {
58
59
  }, target.appLoc, authConfig)];
59
60
  case 1:
60
61
  response = _b.sent();
61
- if (!response) return [3 /*break*/, 5];
62
+ if (!response) return [3 /*break*/, 6];
62
63
  // handle success
63
64
  (_a = process.logger) === null || _a === void 0 ? void 0 : _a.success('Job executed successfully!');
64
65
  if (!(!!logFile && response.log)) return [3 /*break*/, 3];
65
- return [4 /*yield*/, (0, utils_1.saveLog)(response.log, logFile, jobPath)
66
- // save output if it is present
67
- ];
66
+ return [4 /*yield*/, (0, utils_1.saveLog)(response.log, logFile, jobPath)];
68
67
  case 2:
69
68
  _b.sent();
70
69
  _b.label = 3;
71
- case 3:
72
- if (!(!!output && response.result)) return [3 /*break*/, 5];
73
- return [4 /*yield*/, (0, utils_1.saveOutput)(response.result, output)];
70
+ case 3: return [4 /*yield*/, (0, logcheck_1.checkLog)(response.log, jobPath)
71
+ // save output if it is present
72
+ ];
74
73
  case 4:
75
74
  _b.sent();
76
- _b.label = 5;
77
- case 5: return [2 /*return*/, response];
75
+ if (!(!!output && response.result)) return [3 /*break*/, 6];
76
+ return [4 /*yield*/, (0, utils_1.saveOutput)(response.result, output)];
77
+ case 5:
78
+ _b.sent();
79
+ _b.label = 6;
80
+ case 6: return [2 /*return*/, response];
78
81
  }
79
82
  });
80
83
  });