@sap-ux/odata-service-inquirer 0.5.59 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/error-handler/error-handler.d.ts +25 -3
- package/dist/error-handler/error-handler.js +154 -52
- package/dist/i18n.js +15 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -3
- package/dist/prompts/connectionValidator.d.ts +63 -12
- package/dist/prompts/connectionValidator.js +196 -38
- package/dist/prompts/datasources/sap-system/abap-on-btp/questions.d.ts +2 -1
- package/dist/prompts/datasources/sap-system/abap-on-btp/questions.js +3 -1
- package/dist/prompts/datasources/sap-system/abap-on-prem/questions.d.ts +9 -11
- package/dist/prompts/datasources/sap-system/abap-on-prem/questions.js +21 -55
- package/dist/prompts/datasources/sap-system/credentials/questions.d.ts +21 -0
- package/dist/prompts/datasources/sap-system/credentials/questions.js +123 -0
- package/dist/prompts/datasources/sap-system/new-system/questions.d.ts +4 -26
- package/dist/prompts/datasources/sap-system/new-system/questions.js +18 -168
- package/dist/prompts/datasources/sap-system/new-system/types.d.ts +0 -10
- package/dist/prompts/datasources/sap-system/service-selection/index.d.ts +3 -0
- package/dist/prompts/datasources/sap-system/service-selection/index.js +19 -0
- package/dist/prompts/datasources/sap-system/service-selection/questions.d.ts +15 -0
- package/dist/prompts/datasources/sap-system/service-selection/questions.js +146 -0
- package/dist/prompts/datasources/sap-system/service-selection/service-helper.d.ts +71 -0
- package/dist/prompts/datasources/sap-system/service-selection/service-helper.js +281 -0
- package/dist/prompts/datasources/sap-system/service-selection/types.d.ts +11 -0
- package/dist/prompts/datasources/sap-system/service-selection/types.js +3 -0
- package/dist/prompts/datasources/sap-system/system-selection/index.d.ts +2 -0
- package/dist/prompts/datasources/sap-system/system-selection/index.js +18 -0
- package/dist/prompts/datasources/sap-system/system-selection/prompt-helpers.d.ts +44 -0
- package/dist/prompts/datasources/sap-system/system-selection/prompt-helpers.js +190 -0
- package/dist/prompts/datasources/sap-system/system-selection/questions.d.ts +40 -0
- package/dist/prompts/datasources/sap-system/system-selection/questions.js +181 -0
- package/dist/prompts/datasources/sap-system/validators.d.ts +8 -0
- package/dist/prompts/datasources/sap-system/validators.js +17 -0
- package/dist/prompts/datasources/service-url/questions.js +2 -2
- package/dist/prompts/datasources/service-url/validators.js +5 -6
- package/dist/prompts/prompt-helpers.d.ts +1 -2
- package/dist/prompts/prompt-helpers.js +2 -11
- package/dist/prompts/prompts.js +5 -34
- package/dist/prompts/validators.d.ts +1 -1
- package/dist/prompts/validators.js +1 -1
- package/dist/translations/odata-service-inquirer.i18n.json +36 -15
- package/dist/types.d.ts +57 -10
- package/dist/types.js +7 -1
- package/dist/utils/index.d.ts +12 -3
- package/dist/utils/index.js +27 -5
- package/package.json +6 -6
- package/dist/prompts/datasources/sap-system/new-system/service-helper.d.ts +0 -43
- package/dist/prompts/datasources/sap-system/new-system/service-helper.js +0 -175
|
@@ -23,8 +23,12 @@ const ignorableCertErrors = [error_handler_1.ERROR_TYPE.CERT_SELF_SIGNED, error_
|
|
|
23
23
|
*/
|
|
24
24
|
class ConnectionValidator {
|
|
25
25
|
validity = {};
|
|
26
|
-
// The current valid url (not necessarily authenticated but the url is in a valid format)
|
|
26
|
+
// The current valid url (not necessarily authenticated but the url is in a valid format), for destination connections this will be in the form: <protocol>://<destinationName>.dest
|
|
27
27
|
_validatedUrl;
|
|
28
|
+
// Only in the case of destination connections does this store the destination `url` value, this is the fully expanded destination url and any user specified service paths
|
|
29
|
+
_destinationUrl;
|
|
30
|
+
// The full destination object
|
|
31
|
+
_destination;
|
|
28
32
|
// The current client code used for requests, the client code has been validated by a successful request
|
|
29
33
|
_validatedClient;
|
|
30
34
|
_odataService;
|
|
@@ -135,6 +139,22 @@ class ConnectionValidator {
|
|
|
135
139
|
get refreshToken() {
|
|
136
140
|
return this._refreshToken;
|
|
137
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Get the full expanded destination url as defined in the destination configuration. This should not be used to connect from App Studio (use the .dest form).
|
|
144
|
+
*
|
|
145
|
+
* @returns the connected destination 'URL' attribute value
|
|
146
|
+
*/
|
|
147
|
+
get destinationUrl() {
|
|
148
|
+
return this._destinationUrl;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get the destination.
|
|
152
|
+
*
|
|
153
|
+
* @returns the connected destination 'URL' attribute value
|
|
154
|
+
*/
|
|
155
|
+
get destination() {
|
|
156
|
+
return this._destination;
|
|
157
|
+
}
|
|
138
158
|
/**
|
|
139
159
|
* Get the connected system name. If previously set this will be used, otherwise the name is determined
|
|
140
160
|
* by the system auth type, or the validated url.
|
|
@@ -168,8 +188,9 @@ class ConnectionValidator {
|
|
|
168
188
|
* @param options.isSystem if true, the url will be treated as a system url rather than a service url
|
|
169
189
|
* @param options.odataVersion if specified will restrict catalog requests to only the specified odata version
|
|
170
190
|
* @returns the status code or error returned by the connection attempt
|
|
191
|
+
* @throws an error if the connection attempt fails and the error is a 500 on App Studio or a non-axios error is caught
|
|
171
192
|
*/
|
|
172
|
-
async
|
|
193
|
+
async checkUrl(url, username, password, { ignoreCertError = false, isSystem = false, odataVersion } = {}) {
|
|
173
194
|
const isBAS = (0, btp_utils_1.isAppStudio)();
|
|
174
195
|
try {
|
|
175
196
|
// Auto add trailing '/' to path
|
|
@@ -196,8 +217,8 @@ class ConnectionValidator {
|
|
|
196
217
|
catch (e) {
|
|
197
218
|
logger_helper_1.default.logger.debug(`ConnectionValidator.checkSapService() - error: ${e.message}`);
|
|
198
219
|
if (e?.isAxiosError) {
|
|
199
|
-
//
|
|
200
|
-
if (e?.response?.status
|
|
220
|
+
// Error handling for BAS specific 500 errors
|
|
221
|
+
if (e?.response?.status.toString().match(/5\d\d/) && isBAS) {
|
|
201
222
|
throw e;
|
|
202
223
|
}
|
|
203
224
|
return e?.response?.status || e?.code;
|
|
@@ -251,7 +272,11 @@ class ConnectionValidator {
|
|
|
251
272
|
logger_helper_1.default.attachAxiosLogger(this._serviceProvider.interceptors);
|
|
252
273
|
await this._odataService.get('');
|
|
253
274
|
}
|
|
254
|
-
|
|
275
|
+
/**
|
|
276
|
+
*
|
|
277
|
+
* @param resetValidity
|
|
278
|
+
*/
|
|
279
|
+
resetConnectionState(resetValidity = false) {
|
|
255
280
|
this._serviceProvider = undefined;
|
|
256
281
|
this._odataService = undefined;
|
|
257
282
|
this._catalogV2 = undefined;
|
|
@@ -260,6 +285,9 @@ class ConnectionValidator {
|
|
|
260
285
|
this._connectedUserName = undefined;
|
|
261
286
|
this._refreshToken = undefined;
|
|
262
287
|
this._connectedSystemName = undefined;
|
|
288
|
+
if (resetValidity) {
|
|
289
|
+
this.resetValidity();
|
|
290
|
+
}
|
|
263
291
|
}
|
|
264
292
|
/**
|
|
265
293
|
* Create the connection for a system url, the specified axios config or the specified service info.
|
|
@@ -269,12 +297,21 @@ class ConnectionValidator {
|
|
|
269
297
|
* @param connectConfig.url the system url
|
|
270
298
|
* @param connectConfig.serviceInfo the service info
|
|
271
299
|
* @param connectConfig.odataVersion the odata version to restrict the catalog requests if only a specific version is required
|
|
300
|
+
* @param connectConfig.destination the destination to connect with
|
|
301
|
+
* @throws an error if the connection attempt fails, callers should handle the error
|
|
272
302
|
*/
|
|
273
|
-
async createSystemConnection({ axiosConfig, url, serviceInfo, odataVersion }) {
|
|
303
|
+
async createSystemConnection({ axiosConfig, url, serviceInfo, destination, odataVersion }) {
|
|
274
304
|
this.resetConnectionState();
|
|
305
|
+
this.resetValidity();
|
|
275
306
|
if (this.systemAuthType === 'reentranceTicket' || this.systemAuthType === 'serviceKey') {
|
|
276
307
|
this._serviceProvider = this.getAbapOnCloudServiceProvider(url, serviceInfo);
|
|
277
308
|
}
|
|
309
|
+
else if (destination) {
|
|
310
|
+
// Assumption: the destination configured URL is a valid URL, will be needed later for basic auth error handling
|
|
311
|
+
this._validatedUrl = (0, btp_utils_1.getDestinationUrlForAppStudio)(destination.Name);
|
|
312
|
+
this._destinationUrl = destination.Host;
|
|
313
|
+
this._serviceProvider = (0, axios_extension_1.createForDestination)({}, destination);
|
|
314
|
+
}
|
|
278
315
|
else if (axiosConfig) {
|
|
279
316
|
this._axiosConfig = axiosConfig;
|
|
280
317
|
this._serviceProvider = (0, axios_extension_1.createForAbap)(axiosConfig);
|
|
@@ -307,6 +344,8 @@ class ConnectionValidator {
|
|
|
307
344
|
await this._catalogV4.listServices();
|
|
308
345
|
}
|
|
309
346
|
else {
|
|
347
|
+
// Either the v2 or v4 catalog request failed for a specific odata version, or both failed where no odata verison was specified
|
|
348
|
+
// Do some root cause analysis to determine the end user help message
|
|
310
349
|
throw error;
|
|
311
350
|
}
|
|
312
351
|
}
|
|
@@ -325,7 +364,7 @@ class ConnectionValidator {
|
|
|
325
364
|
return errorType === error_handler_1.ERROR_TYPE.NOT_FOUND || errorType === error_handler_1.ERROR_TYPE.AUTH;
|
|
326
365
|
}
|
|
327
366
|
/**
|
|
328
|
-
* Callback for when the refresh token changes.
|
|
367
|
+
* Callback for when the Abap cloud system refresh token changes.
|
|
329
368
|
*
|
|
330
369
|
* @param refreshToken the new refresh token
|
|
331
370
|
*/
|
|
@@ -358,10 +397,12 @@ class ConnectionValidator {
|
|
|
358
397
|
}
|
|
359
398
|
/**
|
|
360
399
|
* Validate the system connectivity with the specified service info (containing UAA details).
|
|
400
|
+
* This will create a connection to the system, updating the service provider reference.
|
|
401
|
+
* The connected user name will be cached for later use.
|
|
361
402
|
*
|
|
362
403
|
* @param serviceInfo the service info containing the UAA details
|
|
363
404
|
* @param odataVersion the odata version to restrict the catalog requests if only a specific version is required
|
|
364
|
-
* @returns true if the system is reachable, false if not, or an error message string
|
|
405
|
+
* @returns true if the system is reachable and authenticated, if required, false if not, or an error message string
|
|
365
406
|
*/
|
|
366
407
|
async validateServiceInfo(serviceInfo, odataVersion) {
|
|
367
408
|
if (!serviceInfo) {
|
|
@@ -385,7 +426,85 @@ class ConnectionValidator {
|
|
|
385
426
|
}
|
|
386
427
|
}
|
|
387
428
|
/**
|
|
388
|
-
*
|
|
429
|
+
* Validate the specified destination connectivity, determining if authentication is required or if the destination is misconfigured.
|
|
430
|
+
*
|
|
431
|
+
* @param destination the destination to validate
|
|
432
|
+
* @param odataVersion the odata version to restrict the catalog requests if only a specific version is required
|
|
433
|
+
* @param servicePath the service path to validate, if specified will be appended to the destination URL for validation, if not specified the destination url will be used
|
|
434
|
+
* @returns true if the system is reachable and authenticated, if required, false if not, or an error message string
|
|
435
|
+
*/
|
|
436
|
+
async validateDestination(destination, odataVersion, servicePath) {
|
|
437
|
+
try {
|
|
438
|
+
// The only supported authentication mechanism for destinations set to Authentication 'NO_AUTHENTICATION' is basic (i.e. to the target Abap system)
|
|
439
|
+
// So while we actually dont know we assume its basic for now since thats the only supported mechanism
|
|
440
|
+
this.systemAuthType = destination.Authentication === btp_utils_1.Authentication.NO_AUTHENTICATION ? 'basic' : 'unknown';
|
|
441
|
+
// Since a destination may be a system or a service connection, we need to determine the connection request (catalog or service)
|
|
442
|
+
if ((0, btp_utils_1.isFullUrlDestination)(destination) || (0, btp_utils_1.isPartialUrlDestination)(destination)) {
|
|
443
|
+
return await this.validateOdataServiceDestination(destination, servicePath);
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
await this.createSystemConnection({ destination, odataVersion });
|
|
447
|
+
}
|
|
448
|
+
return {
|
|
449
|
+
valResult: this.getValidationResultFromStatusCode(200)
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
catch (error) {
|
|
453
|
+
if (error?.isAxiosError) {
|
|
454
|
+
this.getValidationResultFromStatusCode(error?.response?.status || error?.code);
|
|
455
|
+
}
|
|
456
|
+
// Log the network error
|
|
457
|
+
const errorLog = prompt_helpers_1.errorHandler.logErrorMsgs(error);
|
|
458
|
+
// Return a more helpful error message
|
|
459
|
+
return {
|
|
460
|
+
valResult: prompt_helpers_1.errorHandler.getValidationErrorHelp(error, false, destination) ?? errorLog,
|
|
461
|
+
errorType: prompt_helpers_1.errorHandler.getCurrentErrorType() ?? error_handler_1.ERROR_TYPE.DESTINATION_CONNECTION_ERROR
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Validate the connectivity to a destination odata service (for generic odata service destinations), determining, for example, if authentication is required.
|
|
467
|
+
*
|
|
468
|
+
* @param destination a destination that is a generic odata service destination
|
|
469
|
+
* @param servicePath additional service path to append to the destination URL
|
|
470
|
+
* @param requiredOdataVersion the required odata version, if specified will validate the odata service version satisfies the specified version
|
|
471
|
+
* @returns validation result and error type if validation fails
|
|
472
|
+
*/
|
|
473
|
+
async validateOdataServiceDestination(destination, servicePath, requiredOdataVersion) {
|
|
474
|
+
this.resetConnectionState();
|
|
475
|
+
this.resetValidity();
|
|
476
|
+
// Get the destination URL in the BAS specific form <protocol>://<destinationName>.dest
|
|
477
|
+
const destUrl = (0, btp_utils_1.getDestinationUrlForAppStudio)(destination.Name, servicePath);
|
|
478
|
+
// Get the destination URL in the portable form <protocol>://<host>:<port>
|
|
479
|
+
this._destinationUrl = servicePath ? new URL(`${destination.Host}${servicePath}`).toString() : destination.Host;
|
|
480
|
+
this._destination = destination;
|
|
481
|
+
// No need to apply sap-client as this happens automatically (from destination config) when going through the BAS proxy
|
|
482
|
+
const status = await this.checkUrl(new URL(destUrl), undefined, undefined, {
|
|
483
|
+
odataVersion: requiredOdataVersion
|
|
484
|
+
});
|
|
485
|
+
this._validatedUrl = destUrl;
|
|
486
|
+
const validationResult = this.getValidationResultFromStatusCode(status);
|
|
487
|
+
if (!this.validity.reachable) {
|
|
488
|
+
// Log the error
|
|
489
|
+
const errorLog = prompt_helpers_1.errorHandler.logErrorMsgs(status);
|
|
490
|
+
return {
|
|
491
|
+
valResult: prompt_helpers_1.errorHandler.getValidationErrorHelp(status, false, destination) ?? errorLog,
|
|
492
|
+
errorType: prompt_helpers_1.errorHandler.getCurrentErrorType() ?? error_handler_1.ERROR_TYPE.DESTINATION_CONNECTION_ERROR
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
if (this.validity.authRequired) {
|
|
496
|
+
return {
|
|
497
|
+
valResult: error_handler_1.ErrorHandler.getErrorMsgFromType(error_handler_1.ERROR_TYPE.AUTH),
|
|
498
|
+
errorType: error_handler_1.ERROR_TYPE.AUTH
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
return {
|
|
502
|
+
valResult: validationResult
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Validates an odata service url format as well as its reachability. Note if this function returns true, this is only an indication that the system is reachable
|
|
507
|
+
* not that a connection has been established. The connection may require separate authentication or other steps (it may be reachable but a cert error was returned).
|
|
389
508
|
*
|
|
390
509
|
* @param serviceUrl the url to validate, may be a system or service url.
|
|
391
510
|
* Note that if systemAuthType is specified, the url will be treated as a system url (only the origin will be considered)
|
|
@@ -394,46 +513,61 @@ class ConnectionValidator {
|
|
|
394
513
|
* @param options.forceReValidation force re-validation of the url
|
|
395
514
|
* @param options.isSystem if true, the url will be treated as a system url rather than a service url, this value is retained for subsequent calls
|
|
396
515
|
* @param options.odataVersion if specified will restrict catalog requests to only the specified odata version
|
|
516
|
+
* @param options.systemAuthType the system auth type used to create system connections, if not specified or `isSystem` is false or undefined, `basic` is assumed
|
|
397
517
|
* @returns true if the url is reachable, false if not, or an error message string
|
|
398
518
|
*/
|
|
399
|
-
async validateUrl(serviceUrl, { ignoreCertError = false, forceReValidation = false, isSystem = false, odataVersion } = {}) {
|
|
519
|
+
async validateUrl(serviceUrl, { ignoreCertError = false, forceReValidation = false, isSystem = false, odataVersion, systemAuthType } = {}) {
|
|
400
520
|
if (this.isEmptyString(serviceUrl)) {
|
|
401
521
|
this.resetValidity();
|
|
522
|
+
this.validity.urlFormat = false;
|
|
402
523
|
return false;
|
|
403
524
|
}
|
|
525
|
+
let url;
|
|
526
|
+
try {
|
|
527
|
+
// Check if the url is valid
|
|
528
|
+
url = new URL(serviceUrl);
|
|
529
|
+
if (url.origin === 'null') {
|
|
530
|
+
return (0, i18n_1.t)('errors.invalidUrl', { input: serviceUrl });
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
catch (error) {
|
|
534
|
+
return (0, i18n_1.t)('errors.invalidUrl', { input: serviceUrl });
|
|
535
|
+
}
|
|
536
|
+
if (systemAuthType) {
|
|
537
|
+
this.systemAuthType = systemAuthType;
|
|
538
|
+
}
|
|
404
539
|
try {
|
|
405
|
-
const url = new URL(serviceUrl);
|
|
406
540
|
if (!forceReValidation && this.isUrlValidated(serviceUrl)) {
|
|
407
541
|
return this.validity.reachable ?? false;
|
|
408
542
|
}
|
|
409
|
-
|
|
410
|
-
|
|
543
|
+
else {
|
|
544
|
+
// New URL so reset the validity
|
|
545
|
+
this.resetValidity();
|
|
411
546
|
}
|
|
412
547
|
// Ignore path if a system url
|
|
413
|
-
const status = await this.
|
|
548
|
+
const status = await this.checkUrl(url, undefined, undefined, {
|
|
414
549
|
ignoreCertError,
|
|
415
550
|
isSystem,
|
|
416
551
|
odataVersion
|
|
417
552
|
});
|
|
418
|
-
logger_helper_1.default.logger.debug(`ConnectionValidator.
|
|
553
|
+
logger_helper_1.default.logger.debug(`ConnectionValidator.checkSapServiceUrl() - status: ${status}; url: ${serviceUrl}`);
|
|
419
554
|
this.validity.urlFormat = true;
|
|
420
555
|
this._validatedUrl = serviceUrl;
|
|
421
556
|
return this.getValidationResultFromStatusCode(status);
|
|
422
557
|
}
|
|
423
558
|
catch (error) {
|
|
559
|
+
this.resetValidity();
|
|
424
560
|
// More helpful context specific error
|
|
425
561
|
if (error_handler_1.ErrorHandler.getErrorType(error) === error_handler_1.ERROR_TYPE.CONNECTION) {
|
|
426
562
|
this.validity.reachable = false;
|
|
427
563
|
return prompt_helpers_1.errorHandler.logErrorMsgs((0, i18n_1.t)('errors.systemOrServiceUrlNotFound', { url: serviceUrl }));
|
|
428
564
|
}
|
|
429
|
-
this.resetValidity();
|
|
430
565
|
const errorMsg = prompt_helpers_1.errorHandler.getErrorMsg(error);
|
|
431
|
-
return errorMsg ??
|
|
566
|
+
return errorMsg ?? error.message;
|
|
432
567
|
}
|
|
433
568
|
}
|
|
434
569
|
/**
|
|
435
|
-
*
|
|
436
|
-
* Sets the instance validity state based on the status code.
|
|
570
|
+
* Sets the instance validity state based on the status code and returns true if the status code indicates that the URL was reachable.
|
|
437
571
|
*
|
|
438
572
|
* @param status a http request status code used to determine the validation result
|
|
439
573
|
* @returns true, if the status code indicates the url is reachable, false if not, or an error message string
|
|
@@ -442,6 +576,7 @@ class ConnectionValidator {
|
|
|
442
576
|
if (status === 200) {
|
|
443
577
|
this.validity.reachable = true;
|
|
444
578
|
this.validity.authenticated = true;
|
|
579
|
+
return true;
|
|
445
580
|
}
|
|
446
581
|
else if (error_handler_1.ErrorHandler.getErrorType(status) === error_handler_1.ERROR_TYPE.NOT_FOUND) {
|
|
447
582
|
this.validity.reachable = false;
|
|
@@ -450,12 +585,14 @@ class ConnectionValidator {
|
|
|
450
585
|
else if (error_handler_1.ErrorHandler.isCertError(status)) {
|
|
451
586
|
this.validity.reachable = true;
|
|
452
587
|
this.validity.canSkipCertError = ignorableCertErrors.includes(error_handler_1.ErrorHandler.getErrorType(status));
|
|
588
|
+
this.validity.authenticated = false;
|
|
453
589
|
return prompt_helpers_1.errorHandler.getValidationErrorHelp(status, false) ?? false;
|
|
454
590
|
}
|
|
455
591
|
else if (error_handler_1.ErrorHandler.getErrorType(status) === error_handler_1.ERROR_TYPE.AUTH) {
|
|
456
592
|
this.validity.reachable = true;
|
|
457
593
|
this.validity.authRequired = true;
|
|
458
594
|
this.validity.authenticated = false;
|
|
595
|
+
return true;
|
|
459
596
|
}
|
|
460
597
|
else if (error_handler_1.ErrorHandler.getErrorType(status) === error_handler_1.ERROR_TYPE.REDIRECT) {
|
|
461
598
|
this.validity.reachable = true;
|
|
@@ -463,10 +600,18 @@ class ConnectionValidator {
|
|
|
463
600
|
}
|
|
464
601
|
else if (error_handler_1.ErrorHandler.getErrorType(status) === error_handler_1.ERROR_TYPE.CONNECTION) {
|
|
465
602
|
this.validity.reachable = false;
|
|
466
|
-
return error_handler_1.ErrorHandler.getErrorMsgFromType(error_handler_1.ERROR_TYPE.CONNECTION,
|
|
603
|
+
return (error_handler_1.ErrorHandler.getErrorMsgFromType(error_handler_1.ERROR_TYPE.CONNECTION, (0, i18n_1.t)('texts.httpStatus', { httpStatus: status })) ?? false);
|
|
604
|
+
}
|
|
605
|
+
else if (error_handler_1.ErrorHandler.getErrorType(status) === error_handler_1.ERROR_TYPE.BAD_REQUEST) {
|
|
606
|
+
this.validity.reachable = true;
|
|
607
|
+
return (error_handler_1.ErrorHandler.getErrorMsgFromType(error_handler_1.ERROR_TYPE.BAD_REQUEST, (0, i18n_1.t)('texts.httpStatus', { httpStatus: status })) ?? false);
|
|
608
|
+
}
|
|
609
|
+
else if (error_handler_1.ErrorHandler.getErrorType(status) === error_handler_1.ERROR_TYPE.BAD_GATEWAY) {
|
|
610
|
+
this.validity.reachable = false;
|
|
611
|
+
return (error_handler_1.ErrorHandler.getErrorMsgFromType(error_handler_1.ERROR_TYPE.BAD_GATEWAY, (0, i18n_1.t)('texts.httpStatus', { httpStatus: status })) ?? false);
|
|
467
612
|
}
|
|
468
613
|
this.validity.reachable = true;
|
|
469
|
-
return true;
|
|
614
|
+
return (error_handler_1.ErrorHandler.getErrorMsgFromType(error_handler_1.ErrorHandler.getErrorType(status), (0, i18n_1.t)('texts.httpStatus', { httpStatus: status })) ?? true);
|
|
470
615
|
}
|
|
471
616
|
/**
|
|
472
617
|
* Is a string nil or whitespace only.
|
|
@@ -487,12 +632,11 @@ class ConnectionValidator {
|
|
|
487
632
|
if (this._validatedUrl === url) {
|
|
488
633
|
return true;
|
|
489
634
|
}
|
|
490
|
-
this.resetValidity();
|
|
491
635
|
return false;
|
|
492
636
|
}
|
|
493
637
|
/**
|
|
494
638
|
* Check whether basic auth is required for the given url, or for the previously validated url if none specified.
|
|
495
|
-
* This will also set the validity state for the url.
|
|
639
|
+
* This will also set the validity state for the url.
|
|
496
640
|
*
|
|
497
641
|
* @param urlString the url to validate, if not provided the previously validated url will be used
|
|
498
642
|
* @param client optional, sap client code, if not provided the previously validated client will be used
|
|
@@ -503,11 +647,15 @@ class ConnectionValidator {
|
|
|
503
647
|
if (!urlString) {
|
|
504
648
|
return false;
|
|
505
649
|
}
|
|
506
|
-
// Dont re-request if already
|
|
507
|
-
if (this._validatedUrl === urlString &&
|
|
508
|
-
this.
|
|
509
|
-
|
|
510
|
-
|
|
650
|
+
// Dont re-request if we have already determined the auth requirement or we are authenticated
|
|
651
|
+
if (this._validatedUrl === urlString && this._validatedClient === client) {
|
|
652
|
+
if (this.validity.authenticated) {
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
if (this.validity.authRequired !== undefined) {
|
|
656
|
+
return this.validity.authRequired;
|
|
657
|
+
}
|
|
658
|
+
// Not determined yet, continue
|
|
511
659
|
}
|
|
512
660
|
// New URL or client so we need to re-request
|
|
513
661
|
try {
|
|
@@ -515,13 +663,16 @@ class ConnectionValidator {
|
|
|
515
663
|
if (client) {
|
|
516
664
|
url.searchParams.append(types_1.SAP_CLIENT_KEY, client);
|
|
517
665
|
}
|
|
518
|
-
const authError = error_handler_1.ErrorHandler.getErrorType(await this.
|
|
666
|
+
const authError = error_handler_1.ErrorHandler.getErrorType(await this.checkUrl(url, undefined, undefined, { ignoreCertError })) ===
|
|
667
|
+
error_handler_1.ERROR_TYPE.AUTH;
|
|
519
668
|
// Only if we get the specific auth error so we know that auth is required, otherwise we cannot determine so leave as undefined
|
|
520
669
|
if (authError) {
|
|
521
670
|
this.validity.authRequired = true;
|
|
522
671
|
this.validity.reachable = true;
|
|
523
672
|
}
|
|
524
|
-
//
|
|
673
|
+
// Since an exception was not thrown, this is a valid url
|
|
674
|
+
this.validity.urlFormat = true;
|
|
675
|
+
this._validatedUrl = urlString;
|
|
525
676
|
return this.validity.authRequired;
|
|
526
677
|
}
|
|
527
678
|
catch (error) {
|
|
@@ -544,7 +695,7 @@ class ConnectionValidator {
|
|
|
544
695
|
*/
|
|
545
696
|
async validateAuth(url, username, password, { ignoreCertError = false, sapClient, odataVersion, isSystem = false } = {}) {
|
|
546
697
|
if (!url) {
|
|
547
|
-
return false;
|
|
698
|
+
return { valResult: false };
|
|
548
699
|
}
|
|
549
700
|
try {
|
|
550
701
|
const urlObject = new URL(url);
|
|
@@ -552,40 +703,47 @@ class ConnectionValidator {
|
|
|
552
703
|
urlObject.searchParams.append(types_1.SAP_CLIENT_KEY, sapClient);
|
|
553
704
|
}
|
|
554
705
|
this.systemAuthType = 'basic';
|
|
555
|
-
const status = await this.
|
|
706
|
+
const status = await this.checkUrl(urlObject, username, password, {
|
|
556
707
|
ignoreCertError,
|
|
557
708
|
isSystem,
|
|
558
709
|
odataVersion
|
|
559
710
|
});
|
|
560
|
-
logger_helper_1.default.logger.debug(`ConnectionValidator.
|
|
711
|
+
logger_helper_1.default.logger.debug(`ConnectionValidator.checkSapServiceUrl() - status: ${status}; url: ${url}`);
|
|
561
712
|
// Since an exception was not thrown, this is a valid url
|
|
562
713
|
this.validity.urlFormat = true;
|
|
563
714
|
this._validatedUrl = url;
|
|
564
715
|
const valResult = this.getValidationResultFromStatusCode(status);
|
|
565
716
|
if (valResult === true) {
|
|
566
717
|
if (this.validity.authenticated === true) {
|
|
567
|
-
return true;
|
|
718
|
+
return { valResult: true };
|
|
568
719
|
}
|
|
569
720
|
else if (this.validity.authenticated === false) {
|
|
570
|
-
return (0, i18n_1.t)('errors.authenticationFailed');
|
|
721
|
+
return { valResult: (0, i18n_1.t)('errors.authenticationFailed'), errorType: error_handler_1.ERROR_TYPE.AUTH };
|
|
571
722
|
}
|
|
572
723
|
}
|
|
573
|
-
return valResult;
|
|
724
|
+
return { valResult };
|
|
574
725
|
}
|
|
575
726
|
catch (error) {
|
|
576
|
-
return
|
|
727
|
+
return {
|
|
728
|
+
valResult: prompt_helpers_1.errorHandler.getValidationErrorHelp(error, false, this.destination) ??
|
|
729
|
+
prompt_helpers_1.errorHandler.getErrorMsg(error) ??
|
|
730
|
+
(0, i18n_1.t)('errors.unknownError'),
|
|
731
|
+
errorType: prompt_helpers_1.errorHandler.getCurrentErrorType() ?? undefined
|
|
732
|
+
};
|
|
577
733
|
}
|
|
578
734
|
}
|
|
579
735
|
/**
|
|
580
736
|
* Reset the validity state.
|
|
581
737
|
*/
|
|
582
738
|
resetValidity() {
|
|
583
|
-
this.validity.urlFormat
|
|
739
|
+
delete this.validity.urlFormat;
|
|
584
740
|
delete this.validity.reachable;
|
|
585
741
|
delete this.validity.authRequired;
|
|
586
742
|
delete this.validity.authenticated;
|
|
587
743
|
delete this.validity.canSkipCertError;
|
|
588
744
|
this._validatedUrl = undefined;
|
|
745
|
+
this._destinationUrl = undefined;
|
|
746
|
+
this._destination = undefined;
|
|
589
747
|
}
|
|
590
748
|
/**
|
|
591
749
|
* Set the rejectUnauthorized option of the global https agent.
|
|
@@ -2,6 +2,7 @@ import type { ServiceInstanceInfo } from '@sap/cf-tools';
|
|
|
2
2
|
import type { Question } from 'inquirer';
|
|
3
3
|
import { type OdataServiceAnswers, type OdataServicePromptOptions } from '../../../../types';
|
|
4
4
|
import { ConnectionValidator } from '../../../connectionValidator';
|
|
5
|
+
import { type ServiceAnswer } from '../service-selection';
|
|
5
6
|
declare const systemUrlPromptName: "abapOnBtp:newSystemUrl";
|
|
6
7
|
declare const abapOnBtpPromptNames: {
|
|
7
8
|
readonly abapOnBtpAuthType: "abapOnBtpAuthType";
|
|
@@ -21,7 +22,7 @@ interface AbapOnBtpAnswers extends Partial<OdataServiceAnswers> {
|
|
|
21
22
|
* @param promptOptions The prompt options which control the service selection and system name]
|
|
22
23
|
* @returns The list of questions for the ABAP on BTP system
|
|
23
24
|
*/
|
|
24
|
-
export declare function getAbapOnBTPSystemQuestions(promptOptions?: OdataServicePromptOptions): Question<AbapOnBtpAnswers>[];
|
|
25
|
+
export declare function getAbapOnBTPSystemQuestions(promptOptions?: OdataServicePromptOptions): Question<AbapOnBtpAnswers & ServiceAnswer>[];
|
|
25
26
|
/**
|
|
26
27
|
* Get the Cloud Foundry Abap system discovery prompt. This prompt will list all available ABAP environments in the connected Cloud Foundry space.
|
|
27
28
|
* If the Cloud Foundry connection fails, a warning message will be displayed.
|
|
@@ -18,6 +18,7 @@ const questions_1 = require("../new-system/questions");
|
|
|
18
18
|
const types_2 = require("../new-system/types");
|
|
19
19
|
const validators_1 = require("../validators");
|
|
20
20
|
const cf_helper_1 = require("./cf-helper");
|
|
21
|
+
const service_selection_1 = require("../service-selection");
|
|
21
22
|
const abapOnBtpPromptNamespace = 'abapOnBtp';
|
|
22
23
|
const systemUrlPromptName = `${abapOnBtpPromptNamespace}:${types_2.newSystemPromptNames.newSystemUrl}`;
|
|
23
24
|
const cliCfAbapServicePromptName = 'cliCfAbapService';
|
|
@@ -72,11 +73,12 @@ function getAbapOnBTPSystemQuestions(promptOptions) {
|
|
|
72
73
|
(connectValidator.validity.authenticated ?? connectValidator.validity.authRequired !== true))[0]);
|
|
73
74
|
}
|
|
74
75
|
// Service selection prompt
|
|
75
|
-
questions.push(...(0,
|
|
76
|
+
questions.push(...(0, service_selection_1.getSystemServiceQuestion)(connectValidator, abapOnBtpPromptNamespace, promptOptions));
|
|
76
77
|
return questions;
|
|
77
78
|
}
|
|
78
79
|
/**
|
|
79
80
|
* Validate the service info for the ABAP on BTP system. This function will validate the service key file and the connection to the ABAP system.
|
|
81
|
+
* Updates the prompt state with the connected system.
|
|
80
82
|
*
|
|
81
83
|
* @param abapService the abap service as provided by CF tools
|
|
82
84
|
* @param connectionValidator connection validator instance
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import type { OdataVersion } from '@sap-ux/odata-service-writer';
|
|
2
2
|
import type { Question } from 'inquirer';
|
|
3
|
-
import type
|
|
3
|
+
import { type OdataServiceAnswers, type OdataServicePromptOptions, type SystemNamePromptOptions } from '../../../../types';
|
|
4
4
|
import { ConnectionValidator } from '../../../connectionValidator';
|
|
5
|
-
import { type ServiceAnswer } from '../
|
|
5
|
+
import { type ServiceAnswer } from '../service-selection';
|
|
6
6
|
declare const systemUrlPromptName: "abapOnPrem:newSystemUrl";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
declare const usernamePromptName: "abapOnPrem:systemUsername";
|
|
8
|
+
declare const passwordPromptName: "abapOnPrem:systemPassword";
|
|
9
|
+
interface AbabpOnPremCredentialsAnswers {
|
|
10
|
+
[usernamePromptName]?: string;
|
|
11
|
+
[passwordPromptName]?: string;
|
|
11
12
|
}
|
|
12
|
-
interface AbapOnPremAnswers extends Partial<OdataServiceAnswers
|
|
13
|
+
export interface AbapOnPremAnswers extends Partial<OdataServiceAnswers>, AbabpOnPremCredentialsAnswers {
|
|
13
14
|
[systemUrlPromptName]?: string;
|
|
14
|
-
[abapOnPremInternalPromptNames.systemUsername]?: string;
|
|
15
|
-
[abapOnPremInternalPromptNames.systemPassword]?: string;
|
|
16
|
-
[promptNames.serviceSelection]?: ServiceAnswer;
|
|
17
15
|
}
|
|
18
16
|
/**
|
|
19
17
|
* Get the Abap on-premise datasource questions.
|
|
@@ -21,7 +19,7 @@ interface AbapOnPremAnswers extends Partial<OdataServiceAnswers> {
|
|
|
21
19
|
* @param promptOptions options for prompts. Applicable options are: {@link ServiceSelectionPromptOptions}, {@link SystemNamePromptOptions}
|
|
22
20
|
* @returns property questions for the Abap on-premise datasource
|
|
23
21
|
*/
|
|
24
|
-
export declare function getAbapOnPremQuestions(promptOptions?: OdataServicePromptOptions): Question<AbapOnPremAnswers>[];
|
|
22
|
+
export declare function getAbapOnPremQuestions(promptOptions?: OdataServicePromptOptions): Question<AbapOnPremAnswers & ServiceAnswer>[];
|
|
25
23
|
/**
|
|
26
24
|
* Gets the Abap on-premise system questions.
|
|
27
25
|
*
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.abapOnPremInternalPromptNames = void 0;
|
|
4
3
|
exports.getAbapOnPremQuestions = getAbapOnPremQuestions;
|
|
5
4
|
exports.getAbapOnPremSystemQuestions = getAbapOnPremSystemQuestions;
|
|
6
5
|
const inquirer_common_1 = require("@sap-ux/inquirer-common");
|
|
@@ -8,16 +7,17 @@ const project_input_validator_1 = require("@sap-ux/project-input-validator");
|
|
|
8
7
|
const i18n_1 = require("../../../../i18n");
|
|
9
8
|
const utils_1 = require("../../../../utils");
|
|
10
9
|
const connectionValidator_1 = require("../../../connectionValidator");
|
|
11
|
-
const questions_1 = require("../
|
|
10
|
+
const questions_1 = require("../credentials/questions");
|
|
11
|
+
const questions_2 = require("../new-system/questions");
|
|
12
12
|
const types_1 = require("../new-system/types");
|
|
13
|
+
const service_selection_1 = require("../service-selection");
|
|
13
14
|
const abapOnPremPromptNamespace = 'abapOnPrem';
|
|
14
15
|
const systemUrlPromptName = `${abapOnPremPromptNamespace}:${types_1.newSystemPromptNames.newSystemUrl}`;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
})(abapOnPremInternalPromptNames || (exports.abapOnPremInternalPromptNames = abapOnPremInternalPromptNames = {}));
|
|
16
|
+
const usernamePromptName = `${abapOnPremPromptNamespace}:${questions_1.BasicCredentialsPromptNames.systemUsername}`;
|
|
17
|
+
const passwordPromptName = `${abapOnPremPromptNamespace}:${questions_1.BasicCredentialsPromptNames.systemPassword}`;
|
|
18
|
+
const abapOnPremPromptNames = {
|
|
19
|
+
sapClient: 'sapClient'
|
|
20
|
+
};
|
|
21
21
|
/**
|
|
22
22
|
* Get the Abap on-premise datasource questions.
|
|
23
23
|
*
|
|
@@ -30,7 +30,7 @@ function getAbapOnPremQuestions(promptOptions) {
|
|
|
30
30
|
// Prompt options
|
|
31
31
|
const requiredOdataVersion = promptOptions?.serviceSelection?.requiredOdataVersion;
|
|
32
32
|
const questions = getAbapOnPremSystemQuestions(promptOptions?.userSystemName, connectValidator, requiredOdataVersion);
|
|
33
|
-
questions.push(...(0,
|
|
33
|
+
questions.push(...(0, service_selection_1.getSystemServiceQuestion)(connectValidator, abapOnPremPromptNamespace, promptOptions));
|
|
34
34
|
return questions;
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
@@ -43,12 +43,14 @@ function getAbapOnPremQuestions(promptOptions) {
|
|
|
43
43
|
*/
|
|
44
44
|
function getAbapOnPremSystemQuestions(systemNamePromptOptions, connectionValidator, requiredOdataVersion) {
|
|
45
45
|
const connectValidator = connectionValidator ?? new connectionValidator_1.ConnectionValidator();
|
|
46
|
-
|
|
46
|
+
// Object reference to access dynamic sapClient value in prompts where the previous answers are not available.
|
|
47
|
+
// This allows re-usability of the credentials prompts where a client prompt was not used (client was loaded from store).
|
|
48
|
+
const sapClientRef = { sapClient: undefined, isValid: true };
|
|
47
49
|
const questions = [
|
|
48
|
-
(0,
|
|
50
|
+
(0, questions_2.getSystemUrlQuestion)(connectValidator, abapOnPremPromptNamespace, requiredOdataVersion),
|
|
49
51
|
{
|
|
50
52
|
type: 'input',
|
|
51
|
-
name:
|
|
53
|
+
name: abapOnPremPromptNames.sapClient,
|
|
52
54
|
message: (0, i18n_1.t)('prompts.sapClient.message'),
|
|
53
55
|
guiOptions: {
|
|
54
56
|
breadcrumb: (0, i18n_1.t)('prompts.sapClient.breadcrumb')
|
|
@@ -56,58 +58,22 @@ function getAbapOnPremSystemQuestions(systemNamePromptOptions, connectionValidat
|
|
|
56
58
|
validate: (client) => {
|
|
57
59
|
const valRes = (0, project_input_validator_1.validateClient)(client);
|
|
58
60
|
if (valRes === true) {
|
|
59
|
-
|
|
61
|
+
sapClientRef.sapClient = client;
|
|
62
|
+
sapClientRef.isValid = true;
|
|
60
63
|
return true;
|
|
61
64
|
}
|
|
62
|
-
|
|
65
|
+
sapClientRef.sapClient = undefined;
|
|
66
|
+
sapClientRef.isValid = false;
|
|
63
67
|
return valRes;
|
|
64
68
|
}
|
|
65
69
|
},
|
|
66
|
-
|
|
67
|
-
when: () => connectValidator.isAuthRequired(),
|
|
68
|
-
type: 'input',
|
|
69
|
-
name: abapOnPremInternalPromptNames.systemUsername,
|
|
70
|
-
message: (0, i18n_1.t)('prompts.systemUsername.message'),
|
|
71
|
-
guiOptions: {
|
|
72
|
-
mandatory: true
|
|
73
|
-
},
|
|
74
|
-
default: '',
|
|
75
|
-
validate: (user) => user?.length > 0
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
when: () => connectValidator.isAuthRequired(),
|
|
79
|
-
type: 'password',
|
|
80
|
-
guiOptions: {
|
|
81
|
-
mandatory: true
|
|
82
|
-
},
|
|
83
|
-
name: abapOnPremInternalPromptNames.systemPassword,
|
|
84
|
-
message: (0, i18n_1.t)('prompts.systemPassword.message'),
|
|
85
|
-
guiType: 'login',
|
|
86
|
-
mask: '*',
|
|
87
|
-
default: '',
|
|
88
|
-
validate: async (password, answers) => {
|
|
89
|
-
if (!(connectValidator.validatedUrl && answers.abapSystemUsername && password && validClient)) {
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
const valResult = await connectValidator.validateAuth(connectValidator.validatedUrl, answers.abapSystemUsername, password, {
|
|
93
|
-
sapClient: answers.sapClient,
|
|
94
|
-
isSystem: true
|
|
95
|
-
});
|
|
96
|
-
if (valResult === true && connectValidator.serviceProvider) {
|
|
97
|
-
utils_1.PromptState.odataService.connectedSystem = {
|
|
98
|
-
serviceProvider: connectValidator.serviceProvider
|
|
99
|
-
};
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
102
|
-
return valResult;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
70
|
+
...(0, questions_1.getCredentialsPrompts)(connectValidator, abapOnPremPromptNamespace, sapClientRef)
|
|
105
71
|
];
|
|
106
72
|
if (systemNamePromptOptions?.hide !== true) {
|
|
107
73
|
// New system question will allow user to give the system a user friendly name
|
|
108
|
-
questions.push((0, inquirer_common_1.withCondition)([(0,
|
|
74
|
+
questions.push((0, inquirer_common_1.withCondition)([(0, questions_2.getUserSystemNameQuestion)(connectValidator, abapOnPremPromptNamespace)], (answers) => !!answers?.[systemUrlPromptName] &&
|
|
109
75
|
connectValidator.validity.reachable === true &&
|
|
110
|
-
(connectValidator.validity.authenticated || connectValidator.validity.authRequired
|
|
76
|
+
(connectValidator.validity.authenticated || connectValidator.validity.authRequired === false))[0]);
|
|
111
77
|
}
|
|
112
78
|
return questions;
|
|
113
79
|
}
|