@sap-ux/deploy-tooling 0.9.16 → 0.9.17

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.
@@ -15,6 +15,7 @@ const fs_1 = require("fs");
15
15
  const config_1 = require("./config");
16
16
  const prompt_1 = require("./prompt");
17
17
  const system_access_1 = require("@sap-ux/system-access");
18
+ const validate_1 = require("./validate");
18
19
  const deploymentCommands = { tryUndeploy, tryDeploy };
19
20
  /**
20
21
  * Handle exceptions thrown, in some cases we to retry them.
@@ -198,6 +199,7 @@ function runCommand(command, config, logger, archive = Buffer.from('')) {
198
199
  * @param archive - archive file that is to be deployed
199
200
  */
200
201
  function tryDeploy(provider, config, logger, archive) {
202
+ var _a, _b, _c, _d, _e;
201
203
  return __awaiter(this, void 0, void 0, function* () {
202
204
  try {
203
205
  if (config.createTransport) {
@@ -205,6 +207,17 @@ function tryDeploy(provider, config, logger, archive) {
205
207
  // Reset as we dont want other flows kicking it off again!
206
208
  config.createTransport = false;
207
209
  }
210
+ if (config.test === true) {
211
+ const validateOutput = yield (0, validate_1.validateBeforeDeploy)({
212
+ appName: config.app.name,
213
+ description: (_a = config.app.description) !== null && _a !== void 0 ? _a : '',
214
+ package: (_b = config.app.package) !== null && _b !== void 0 ? _b : '',
215
+ transport: (_c = config.app.transport) !== null && _c !== void 0 ? _c : '',
216
+ client: (_d = config.target.client) !== null && _d !== void 0 ? _d : '',
217
+ url: (_e = config.target.url) !== null && _e !== void 0 ? _e : ''
218
+ }, provider, logger);
219
+ logger.info((0, validate_1.formatSummary)(validateOutput.summary));
220
+ }
208
221
  const service = getUi5AbapRepositoryService(provider, config, logger);
209
222
  yield service.deploy({ archive, bsp: config.app, testMode: config.test, safeMode: config.safe });
210
223
  if (config.test === true) {
@@ -0,0 +1,52 @@
1
+ import type { AbapServiceProvider } from '@sap-ux/axios-extension';
2
+ import type { Logger } from '@sap-ux/logger';
3
+ export type ValidationInputs = {
4
+ appName: string;
5
+ description: string;
6
+ package: string;
7
+ transport: string;
8
+ client: string;
9
+ url: string;
10
+ };
11
+ export type ValidationOutput = {
12
+ summary: SummaryRecord[];
13
+ result: boolean;
14
+ };
15
+ export type SummaryRecord = {
16
+ message: string;
17
+ status: SummaryStatus;
18
+ };
19
+ export declare enum SummaryStatus {
20
+ Valid = 0,
21
+ Invalid = 1,
22
+ Unknown = 2
23
+ }
24
+ export declare const summaryMessage: {
25
+ allClientCheckPass: string;
26
+ adtServiceUndefined: string;
27
+ packageCheckPass: string;
28
+ packageNotFound: string;
29
+ pacakgeAdtAccessError: string;
30
+ transportCheckPass: string;
31
+ transportNotFound: string;
32
+ transportAdtAccessError: string;
33
+ transportNotRequired: string;
34
+ atoAdtAccessError: string;
35
+ };
36
+ /**
37
+ * Validation of deploy configuration before running deploy-test.
38
+ *
39
+ * @param input Deploy configuration that needs to be validated
40
+ * @param provider AbapServiceProvider
41
+ * @param logger Logger used by deploy tooling
42
+ * @returns Validation result and a summary report of identified issues.
43
+ */
44
+ export declare function validateBeforeDeploy(input: ValidationInputs, provider: AbapServiceProvider, logger: Logger): Promise<ValidationOutput>;
45
+ /**
46
+ * Format a list of summary records that is ready to be printed on the console.
47
+ *
48
+ * @param summary A list of summary records
49
+ * @returns Formatted summary string
50
+ */
51
+ export declare function formatSummary(summary: SummaryRecord[]): string;
52
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1,289 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.formatSummary = exports.validateBeforeDeploy = exports.summaryMessage = exports.SummaryStatus = void 0;
13
+ const axios_extension_1 = require("@sap-ux/axios-extension");
14
+ const chalk_1 = require("chalk");
15
+ const project_input_validator_1 = require("@sap-ux/project-input-validator");
16
+ const os_1 = require("os");
17
+ var SummaryStatus;
18
+ (function (SummaryStatus) {
19
+ SummaryStatus[SummaryStatus["Valid"] = 0] = "Valid";
20
+ SummaryStatus[SummaryStatus["Invalid"] = 1] = "Invalid";
21
+ SummaryStatus[SummaryStatus["Unknown"] = 2] = "Unknown";
22
+ })(SummaryStatus = exports.SummaryStatus || (exports.SummaryStatus = {}));
23
+ exports.summaryMessage = {
24
+ allClientCheckPass: 'SAPUI5 ABAP Repository follows the rules of creating BSP application',
25
+ adtServiceUndefined: 'AdtService cannot be instantiated',
26
+ packageCheckPass: 'Package is found on ABAP system',
27
+ packageNotFound: 'Package does not exist on ABAP system',
28
+ pacakgeAdtAccessError: 'Package could not be validated. Please check manually.',
29
+ transportCheckPass: 'Transport Request is found on ABAP system',
30
+ transportNotFound: 'Transport Request does not exist on ABAP system',
31
+ transportAdtAccessError: 'Transport Request could not be validated. Please check manually.',
32
+ transportNotRequired: 'Transport Request is not required for local package',
33
+ atoAdtAccessError: 'Development prefix could not be validated. Please check manually.'
34
+ };
35
+ /**
36
+ * Validation of deploy configuration before running deploy-test.
37
+ *
38
+ * @param input Deploy configuration that needs to be validated
39
+ * @param provider AbapServiceProvider
40
+ * @param logger Logger used by deploy tooling
41
+ * @returns Validation result and a summary report of identified issues.
42
+ */
43
+ function validateBeforeDeploy(input, provider, logger) {
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ const output = {
46
+ summary: [],
47
+ result: true
48
+ };
49
+ // output is passed by reference and status updated during the internal pipeline below.
50
+ yield validateInputTextFormat(input, output, provider, logger);
51
+ yield validatePackageWithAdt(input, output, provider, logger);
52
+ yield validateTransportRequestWithAdt(input, output, provider, logger);
53
+ return output;
54
+ });
55
+ }
56
+ exports.validateBeforeDeploy = validateBeforeDeploy;
57
+ /**
58
+ * Format a list of summary records that is ready to be printed on the console.
59
+ *
60
+ * @param summary A list of summary records
61
+ * @returns Formatted summary string
62
+ */
63
+ function formatSummary(summary) {
64
+ const summaryStr = summary
65
+ .map((next) => {
66
+ let statusSymbol = '';
67
+ switch (next.status) {
68
+ case SummaryStatus.Valid:
69
+ statusSymbol = (0, chalk_1.green)('√');
70
+ break;
71
+ case SummaryStatus.Invalid:
72
+ statusSymbol = (0, chalk_1.red)('×');
73
+ break;
74
+ case SummaryStatus.Unknown:
75
+ default:
76
+ statusSymbol = (0, chalk_1.yellow)('?');
77
+ break;
78
+ }
79
+ return `${statusSymbol} ${next.message}`;
80
+ })
81
+ .reduce((aggregated, current) => {
82
+ return `${aggregated}${os_1.EOL}${current}`;
83
+ }, '');
84
+ return summaryStr;
85
+ }
86
+ exports.formatSummary = formatSummary;
87
+ /**
88
+ * Client-side validation on the deploy configuration based on the
89
+ * known input format constraints.
90
+ *
91
+ * @param input Deploy config that needs to be validated
92
+ * @param output Validation output
93
+ * @param provider AbapServiceProvider
94
+ * @param logger Logger from the calling context
95
+ */
96
+ function validateInputTextFormat(input, output, provider, logger) {
97
+ return __awaiter(this, void 0, void 0, function* () {
98
+ // Prepare backend info for validation
99
+ const prefix = yield getSystemPrefix(output, provider, logger);
100
+ // A sequence of client-side validations. No early termination of detecting invalid inputs.
101
+ // Setting output.result to false if any of the checks is invalid.
102
+ // Add individual error messages into output.summary array if validation failed.
103
+ let result = (0, project_input_validator_1.validateAppName)(input.appName, prefix);
104
+ processInputValidationResult(result, output);
105
+ result = (0, project_input_validator_1.validateAppDescription)(input.description);
106
+ processInputValidationResult(result, output);
107
+ result = (0, project_input_validator_1.validateTransportRequestNumber)(input.transport, input.package);
108
+ processInputValidationResult(result, output);
109
+ result = (0, project_input_validator_1.validatePackage)(input.package);
110
+ processInputValidationResult(result, output);
111
+ result = (0, project_input_validator_1.validateClient)(input.client);
112
+ processInputValidationResult(result, output);
113
+ result = (0, project_input_validator_1.validateUrl)(input.url);
114
+ processInputValidationResult(result, output);
115
+ // If all the text validation passed, only show the following success message.
116
+ if (output.result) {
117
+ output.summary.push({
118
+ message: exports.summaryMessage.allClientCheckPass,
119
+ status: SummaryStatus.Valid
120
+ });
121
+ }
122
+ });
123
+ }
124
+ /**
125
+ * Helper function that calls ADT service to retrieve system specific prefix
126
+ * requirement for Fiori app name.
127
+ *
128
+ * @param output Validation output
129
+ * @param provider AbapServiceProvider
130
+ * @param logger Logger from the calling context
131
+ * @returns System specific development prefix constraint for Fiori app name
132
+ */
133
+ function getSystemPrefix(output, provider, logger) {
134
+ return __awaiter(this, void 0, void 0, function* () {
135
+ try {
136
+ const adtService = yield provider.getAdtService(axios_extension_1.AtoService);
137
+ if (!adtService) {
138
+ output.summary.push({
139
+ message: `${exports.summaryMessage.adtServiceUndefined} for AtoService`,
140
+ status: SummaryStatus.Unknown
141
+ });
142
+ output.result = false;
143
+ return undefined;
144
+ }
145
+ const atoSettings = yield adtService.getAtoInfo();
146
+ return atoSettings === null || atoSettings === void 0 ? void 0 : atoSettings.developmentPrefix;
147
+ }
148
+ catch (e) {
149
+ logger.error(e);
150
+ output.summary.push({
151
+ message: exports.summaryMessage.atoAdtAccessError,
152
+ status: SummaryStatus.Unknown
153
+ });
154
+ output.result = false;
155
+ return undefined;
156
+ }
157
+ });
158
+ }
159
+ /**
160
+ * Helper function to proces input validation result. Avoids sonarqube warning about
161
+ * increasing complexity.
162
+ *
163
+ * @param validationResult Validation result is either true or error message
164
+ * @param output validation output
165
+ */
166
+ function processInputValidationResult(validationResult, output) {
167
+ if (typeof validationResult === 'string') {
168
+ output.summary.push({
169
+ message: validationResult,
170
+ status: SummaryStatus.Invalid
171
+ });
172
+ output.result = false;
173
+ }
174
+ else if (validationResult !== true) {
175
+ // Strict check for validator functions that return false instead of error message.
176
+ throw new Error('Expect error message string returned from validation function instead of false');
177
+ }
178
+ }
179
+ /**
180
+ * Query ADT backend service to verify input package name is valid.
181
+ *
182
+ * @param input Inputs to query ADT service
183
+ * @param output Output to be updated during this function call
184
+ * @param provider AbapServiceProvider
185
+ * @param logger Logger from the calling context
186
+ */
187
+ function validatePackageWithAdt(input, output, provider, logger) {
188
+ return __awaiter(this, void 0, void 0, function* () {
189
+ if (output.result === false) {
190
+ return;
191
+ }
192
+ try {
193
+ const adtService = yield provider.getAdtService(axios_extension_1.ListPackageService);
194
+ if (!adtService) {
195
+ output.summary.push({
196
+ message: `${exports.summaryMessage.adtServiceUndefined} for ListPackageService`,
197
+ status: SummaryStatus.Unknown
198
+ });
199
+ output.result = false;
200
+ return;
201
+ }
202
+ const packages = yield adtService.listPackages({ phrase: input.package });
203
+ const isValidPackage = packages.findIndex((packageName) => packageName === input.package) >= 0;
204
+ if (isValidPackage) {
205
+ output.summary.push({
206
+ message: exports.summaryMessage.packageCheckPass,
207
+ status: SummaryStatus.Valid
208
+ });
209
+ }
210
+ else {
211
+ output.summary.push({
212
+ message: exports.summaryMessage.packageNotFound,
213
+ status: SummaryStatus.Invalid
214
+ });
215
+ output.result = false;
216
+ }
217
+ }
218
+ catch (e) {
219
+ logger.error(e);
220
+ output.summary.push({
221
+ message: exports.summaryMessage.pacakgeAdtAccessError,
222
+ status: SummaryStatus.Unknown
223
+ });
224
+ output.result = false;
225
+ }
226
+ });
227
+ }
228
+ /**
229
+ * Query ADT backend service to verify input transport request.
230
+ *
231
+ * @param input Inputs to query ADT service
232
+ * @param output Output to be updated during this function call
233
+ * @param provider AbapServiceProvider
234
+ * @param logger Logger from the calling context
235
+ */
236
+ function validateTransportRequestWithAdt(input, output, provider, logger) {
237
+ return __awaiter(this, void 0, void 0, function* () {
238
+ if (output.result === false) {
239
+ return;
240
+ }
241
+ try {
242
+ const adtService = yield provider.getAdtService(axios_extension_1.TransportChecksService);
243
+ if (!adtService) {
244
+ output.summary.push({
245
+ message: `${exports.summaryMessage.adtServiceUndefined} for TransportChecksService`,
246
+ status: SummaryStatus.Unknown
247
+ });
248
+ output.result = false;
249
+ return;
250
+ }
251
+ const trList = yield adtService.getTransportRequests(input.package, input.appName);
252
+ const isValidTrList = trList.findIndex((tr) => tr.transportNumber === input.transport) >= 0;
253
+ if (isValidTrList) {
254
+ output.summary.push({
255
+ message: exports.summaryMessage.transportCheckPass,
256
+ status: SummaryStatus.Valid
257
+ });
258
+ }
259
+ else {
260
+ output.summary.push({
261
+ message: exports.summaryMessage.transportNotFound,
262
+ status: SummaryStatus.Invalid
263
+ });
264
+ output.result = false;
265
+ }
266
+ }
267
+ catch (e) {
268
+ // TransportChecksService.getTransportRequests() API is used to provide valid
269
+ // transport request list. If input packge is local package, no transport request
270
+ // is returned and LocalPackageError is thrown as exception.
271
+ // LocalPackageError is acceptable for validation purpose here.
272
+ if (e.message === axios_extension_1.TransportChecksService.LocalPackageError) {
273
+ output.summary.push({
274
+ message: exports.summaryMessage.transportNotRequired,
275
+ status: SummaryStatus.Valid
276
+ });
277
+ }
278
+ else {
279
+ logger.error(e);
280
+ output.summary.push({
281
+ message: exports.summaryMessage.transportAdtAccessError,
282
+ status: SummaryStatus.Unknown
283
+ });
284
+ output.result = false;
285
+ }
286
+ }
287
+ });
288
+ }
289
+ //# sourceMappingURL=validate.js.map
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "bugs": {
10
10
  "url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Adeploy-tooling"
11
11
  },
12
- "version": "0.9.16",
12
+ "version": "0.9.17",
13
13
  "license": "Apache-2.0",
14
14
  "author": "@SAP/ux-tools-team",
15
15
  "main": "dist/index.js",
@@ -31,11 +31,13 @@
31
31
  "dotenv": "16.3.1",
32
32
  "prompts": "2.4.2",
33
33
  "yazl": "2.5.1",
34
- "@sap-ux/axios-extension": "1.4.8",
34
+ "chalk": "4.1.2",
35
+ "@sap-ux/axios-extension": "1.5.0",
35
36
  "@sap-ux/btp-utils": "0.11.9",
36
37
  "@sap-ux/logger": "0.3.8",
37
- "@sap-ux/system-access": "0.2.8",
38
- "@sap-ux/ui5-config": "0.19.4"
38
+ "@sap-ux/system-access": "0.2.9",
39
+ "@sap-ux/ui5-config": "0.19.4",
40
+ "@sap-ux/project-input-validator": "0.1.0"
39
41
  },
40
42
  "devDependencies": {
41
43
  "@types/prompts": "2.4.4",