@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.
Files changed (47) hide show
  1. package/dist/error-handler/error-handler.d.ts +25 -3
  2. package/dist/error-handler/error-handler.js +154 -52
  3. package/dist/i18n.js +15 -3
  4. package/dist/index.d.ts +2 -2
  5. package/dist/index.js +1 -3
  6. package/dist/prompts/connectionValidator.d.ts +63 -12
  7. package/dist/prompts/connectionValidator.js +196 -38
  8. package/dist/prompts/datasources/sap-system/abap-on-btp/questions.d.ts +2 -1
  9. package/dist/prompts/datasources/sap-system/abap-on-btp/questions.js +3 -1
  10. package/dist/prompts/datasources/sap-system/abap-on-prem/questions.d.ts +9 -11
  11. package/dist/prompts/datasources/sap-system/abap-on-prem/questions.js +21 -55
  12. package/dist/prompts/datasources/sap-system/credentials/questions.d.ts +21 -0
  13. package/dist/prompts/datasources/sap-system/credentials/questions.js +123 -0
  14. package/dist/prompts/datasources/sap-system/new-system/questions.d.ts +4 -26
  15. package/dist/prompts/datasources/sap-system/new-system/questions.js +18 -168
  16. package/dist/prompts/datasources/sap-system/new-system/types.d.ts +0 -10
  17. package/dist/prompts/datasources/sap-system/service-selection/index.d.ts +3 -0
  18. package/dist/prompts/datasources/sap-system/service-selection/index.js +19 -0
  19. package/dist/prompts/datasources/sap-system/service-selection/questions.d.ts +15 -0
  20. package/dist/prompts/datasources/sap-system/service-selection/questions.js +146 -0
  21. package/dist/prompts/datasources/sap-system/service-selection/service-helper.d.ts +71 -0
  22. package/dist/prompts/datasources/sap-system/service-selection/service-helper.js +281 -0
  23. package/dist/prompts/datasources/sap-system/service-selection/types.d.ts +11 -0
  24. package/dist/prompts/datasources/sap-system/service-selection/types.js +3 -0
  25. package/dist/prompts/datasources/sap-system/system-selection/index.d.ts +2 -0
  26. package/dist/prompts/datasources/sap-system/system-selection/index.js +18 -0
  27. package/dist/prompts/datasources/sap-system/system-selection/prompt-helpers.d.ts +44 -0
  28. package/dist/prompts/datasources/sap-system/system-selection/prompt-helpers.js +190 -0
  29. package/dist/prompts/datasources/sap-system/system-selection/questions.d.ts +40 -0
  30. package/dist/prompts/datasources/sap-system/system-selection/questions.js +181 -0
  31. package/dist/prompts/datasources/sap-system/validators.d.ts +8 -0
  32. package/dist/prompts/datasources/sap-system/validators.js +17 -0
  33. package/dist/prompts/datasources/service-url/questions.js +2 -2
  34. package/dist/prompts/datasources/service-url/validators.js +5 -6
  35. package/dist/prompts/prompt-helpers.d.ts +1 -2
  36. package/dist/prompts/prompt-helpers.js +2 -11
  37. package/dist/prompts/prompts.js +5 -34
  38. package/dist/prompts/validators.d.ts +1 -1
  39. package/dist/prompts/validators.js +1 -1
  40. package/dist/translations/odata-service-inquirer.i18n.json +36 -15
  41. package/dist/types.d.ts +57 -10
  42. package/dist/types.js +7 -1
  43. package/dist/utils/index.d.ts +12 -3
  44. package/dist/utils/index.js +27 -5
  45. package/package.json +6 -6
  46. package/dist/prompts/datasources/sap-system/new-system/service-helper.d.ts +0 -43
  47. 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 checkSapServiceUrl(url, username, password, { ignoreCertError = false, isSystem = false, odataVersion } = {}) {
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
- // Only throw for 500 on App Studio
200
- if (e?.response?.status === 500 && isBAS) {
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
- resetConnectionState() {
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
- * Validates the system or service url format as well as its reachability.
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
- if (url.origin === 'null') {
410
- return (0, i18n_1.t)('errors.invalidUrl');
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.checkSapServiceUrl(url, undefined, undefined, {
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.validateUrl() - status: ${status}; url: ${serviceUrl}`);
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 ?? (0, i18n_1.t)('errors.invalidUrl');
566
+ return errorMsg ?? error.message;
432
567
  }
433
568
  }
434
569
  /**
435
- * Converts the http status code into 'validty' and returns true if the status code indicates that the URL was reachable.
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, `http code: ${status}`) ?? false;
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. This will not validate 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 validated
507
- if (this._validatedUrl === urlString &&
508
- this._validatedClient === client &&
509
- this.validity.authRequired !== undefined) {
510
- return this.validity.authRequired;
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.checkSapServiceUrl(url, undefined, undefined, { ignoreCertError })) === error_handler_1.ERROR_TYPE.AUTH;
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
- // Returning undefined if we cannot determine if auth is required
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.checkSapServiceUrl(urlObject, username, password, {
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.validateUrl() - status: ${status}; url: ${url}`);
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 prompt_helpers_1.errorHandler.getErrorMsg(error) ?? false;
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 = false;
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, questions_1.getSystemServiceQuestion)(connectValidator, abapOnBtpPromptNamespace, promptOptions));
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 { OdataServiceAnswers, OdataServicePromptOptions, SystemNamePromptOptions, promptNames } from '../../../../types';
3
+ import { type OdataServiceAnswers, type OdataServicePromptOptions, type SystemNamePromptOptions } from '../../../../types';
4
4
  import { ConnectionValidator } from '../../../connectionValidator';
5
- import { type ServiceAnswer } from '../new-system/types';
5
+ import { type ServiceAnswer } from '../service-selection';
6
6
  declare const systemUrlPromptName: "abapOnPrem:newSystemUrl";
7
- export declare enum abapOnPremInternalPromptNames {
8
- sapClient = "sapClient",
9
- systemUsername = "abapSystemUsername",
10
- systemPassword = "abapSystemPassword"
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("../new-system/questions");
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
- var abapOnPremInternalPromptNames;
16
- (function (abapOnPremInternalPromptNames) {
17
- abapOnPremInternalPromptNames["sapClient"] = "sapClient";
18
- abapOnPremInternalPromptNames["systemUsername"] = "abapSystemUsername";
19
- abapOnPremInternalPromptNames["systemPassword"] = "abapSystemPassword";
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, questions_1.getSystemServiceQuestion)(connectValidator, abapOnPremPromptNamespace, promptOptions));
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
- let validClient = true;
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, questions_1.getSystemUrlQuestion)(connectValidator, abapOnPremPromptNamespace, requiredOdataVersion),
50
+ (0, questions_2.getSystemUrlQuestion)(connectValidator, abapOnPremPromptNamespace, requiredOdataVersion),
49
51
  {
50
52
  type: 'input',
51
- name: abapOnPremInternalPromptNames.sapClient,
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
- validClient = true;
61
+ sapClientRef.sapClient = client;
62
+ sapClientRef.isValid = true;
60
63
  return true;
61
64
  }
62
- validClient = false;
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, questions_1.getUserSystemNameQuestion)(connectValidator, abapOnPremPromptNamespace)], (answers) => !!answers?.[systemUrlPromptName] &&
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 !== true))[0]);
76
+ (connectValidator.validity.authenticated || connectValidator.validity.authRequired === false))[0]);
111
77
  }
112
78
  return questions;
113
79
  }