@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.
- package/README.md +1 -1
- package/build/commands/flow/flowCommand.js +11 -1
- package/build/commands/flow/internal/executeFlow.js +44 -19
- package/build/commands/flow/internal/saveLog.d.ts +12 -1
- package/build/commands/flow/internal/saveLog.js +2 -2
- package/build/commands/flow/internal/spec/executeFlow.spec.js +44 -1
- package/build/commands/flow/spec/flowCommand.spec.js +44 -0
- package/build/commands/fs/fsCommand.d.ts +2 -2
- package/build/commands/fs/fsCommand.js +44 -15
- package/build/commands/fs/internal/executeCode.d.ts +6 -0
- package/build/commands/fs/internal/executeCode.js +6 -0
- package/build/commands/job/internal/execute/sas9.js +16 -13
- package/build/commands/job/internal/execute/sasjs.js +12 -9
- package/build/commands/job/internal/execute/viya.js +46 -36
- package/build/commands/job/jobCommand.js +13 -0
- package/build/commands/job/spec/execute.spec.js +29 -0
- package/build/commands/job/spec/executeLogOrder.spec.d.ts +1 -0
- package/build/commands/job/spec/executeLogOrder.spec.js +119 -0
- package/build/commands/job/spec/jobCommand.spec.js +32 -0
- package/build/commands/logcheck/logCheckConfig.d.ts +11 -0
- package/build/commands/logcheck/logCheckConfig.js +86 -26
- package/build/commands/logcheck/renderLogCheck.d.ts +1 -0
- package/build/commands/logcheck/renderLogCheck.js +1 -0
- package/build/commands/logcheck/spec/logCheckConfig.spec.js +63 -2
- package/build/commands/logcheck/types.d.ts +8 -0
- package/build/commands/request/request.js +66 -27
- package/build/commands/request/requestCommand.js +11 -1
- package/build/commands/request/spec/requestCommand.spec.js +40 -0
- package/build/commands/run/run.d.ts +1 -3
- package/build/commands/run/run.js +20 -5
- package/build/commands/run/runCommand.js +9 -0
- package/build/commands/run/spec/runCommand.spec.js +50 -0
- package/build/utils/index.d.ts +1 -0
- package/build/utils/index.js +1 -0
- package/build/utils/logcheck.d.ts +44 -0
- package/build/utils/logcheck.js +224 -0
- package/build/utils/spec/logcheck.spec.d.ts +1 -0
- package/build/utils/spec/logcheck.spec.js +297 -0
- 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:
|
|
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 (
|
|
61
|
-
switch (
|
|
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
|
|
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
|
-
|
|
92
|
-
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 =
|
|
124
|
-
if (!(submittedJob === null || submittedJob === void 0 ? void 0 : submittedJob.job)) return [3 /*break*/,
|
|
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
|
-
|
|
133
|
-
logName =
|
|
134
|
-
|
|
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 = ((
|
|
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
|
-
|
|
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
|
-
(
|
|
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
|
-
|
|
175
|
-
case
|
|
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
|
-
|
|
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 () {
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
455
|
+
case 11:
|
|
432
456
|
syncProgram = _v.sent();
|
|
433
457
|
return [4 /*yield*/, createProgramFile(syncProgram, path_1.default.join(outputFolder, 'syncProgram.sas'))];
|
|
434
|
-
case
|
|
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
|
|
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
|
|
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
|
|
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*/,
|
|
476
|
-
case
|
|
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
|
|
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
|
|
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*/,
|
|
74
|
-
if (!(result.status === 200)) return [3 /*break*/,
|
|
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
|
-
|
|
86
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
case
|
|
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 =
|
|
96
|
-
case
|
|
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*/,
|
|
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
|
-
|
|
73
|
-
|
|
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
|
-
|
|
77
|
-
|
|
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
|
});
|