@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
@@ -1,3 +1,4 @@
1
+ import type { Destination } from '@sap-ux/btp-utils';
1
2
  import { type Logger } from '@sap-ux/logger';
2
3
  import { ValidationLink } from '../types';
3
4
  /**
@@ -17,7 +18,7 @@ export declare enum ERROR_TYPE {
17
18
  TIMEOUT = "TIMEOUT",
18
19
  CONNECTION = "CONNECTION",
19
20
  SERVICES_UNAVAILABLE = "SERVICES_UNAVAILABLE",// All services
20
- SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE",// Specific service
21
+ SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE",// HTTP 503 Not related to odata services
21
22
  NO_ABAP_ENVS = "NO_ABAP_ENVS",
22
23
  CATALOG_SERVICE_NOT_ACTIVE = "CATALOG_SERVICE_NOT_ACTIVE",
23
24
  NO_SUCH_HOST = "NO_SUCH_HOST",
@@ -30,7 +31,10 @@ export declare enum ERROR_TYPE {
30
31
  DESTINATION_NOT_FOUND = "DESTINATION_NOT_FOUND",
31
32
  DESTINATION_MISCONFIGURED = "DESTINATION_MISCONFIGURED",
32
33
  NO_V2_SERVICES = "NO_V2_SERVICES",
33
- NO_V4_SERVICES = "NO_V4_SERVICES"
34
+ NO_V4_SERVICES = "NO_V4_SERVICES",
35
+ BAD_REQUEST = "BAD_REQUEST",
36
+ DESTINATION_CONNECTION_ERROR = "DESTINATION_CONNECTION_ERROR",// General destination connection error where a specific root cause cannot be determined e.g. In the case of an internal server error
37
+ SERVER_HTTP_ERROR = "SERVER_HTTP_ERROR"
34
38
  }
35
39
  export declare const ERROR_MAP: Record<ERROR_TYPE, RegExp[]>;
36
40
  type ValidationLinkOrString = string | ValidationLink;
@@ -45,6 +49,14 @@ export declare class ErrorHandler {
45
49
  private currentErrorType;
46
50
  private static _guidedAnswersEnabled;
47
51
  private static _logger;
52
+ private static readonly getMessageFromError;
53
+ private static readonly _errorTypeToMsg;
54
+ /**
55
+ *
56
+ * @param errorType
57
+ * @param error can be any object that will get stringified and passed to the specific error message for the error type entry, e.g. where the error message is parameterized
58
+ * @returns an error message for the specified error type
59
+ */
48
60
  private static readonly _errorMsg;
49
61
  /**
50
62
  * Get the Guided Answers (help) node for the specified error type.
@@ -134,12 +146,22 @@ export declare class ErrorHandler {
134
146
  * Used by validate functions to report in-line user friendly errors messages with help links.
135
147
  * If the error type is unknown, this will find a mapped error type and return the help (ValidationLink) if it exists.
136
148
  * If an error is not provided the current error state will be used. This does not log the message to the console.
149
+ * If a system is provided, the error type may be refined to provide a more specific error message for the system which generatd the error.
137
150
  *
138
151
  * @param error optional, if provided get the help link message that it maps to, otherwise get the previously logged error message help link
139
152
  * @param reset optional, resets the previous error state if true
153
+ * @param destination optional, if provided the destination may be used to determine a more relevant error message, specific to the system properties
140
154
  * @returns An instance of @see {ValidationLink}
141
155
  */
142
- getValidationErrorHelp(error?: any, reset?: boolean): ValidationLinkOrString | undefined;
156
+ getValidationErrorHelp(error?: any, reset?: boolean, destination?: Destination): ValidationLinkOrString | undefined;
157
+ /**
158
+ * Get a more specific error type for the specified destination.
159
+ *
160
+ * @param errorType
161
+ * @param destination
162
+ * @returns
163
+ */
164
+ private static getDestinationSpecificError;
143
165
  /**
144
166
  * Get the error message for the specified error type.
145
167
  *
@@ -2,12 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ErrorHandler = exports.ERROR_MAP = exports.ERROR_TYPE = void 0;
4
4
  const btp_utils_1 = require("@sap-ux/btp-utils");
5
+ const guided_answers_helper_1 = require("@sap-ux/guided-answers-helper");
5
6
  const logger_1 = require("@sap-ux/logger");
6
7
  const i18n_1 = require("../i18n");
7
8
  const types_1 = require("../types");
8
9
  const utils_1 = require("../utils");
9
- const guided_answers_helper_1 = require("@sap-ux/guided-answers-helper");
10
- const teleEventGALinkCreated = 'GA_LINK_CREATED';
10
+ // Telemetry event names specific to odata service error handling
11
+ const telemEventGALinkCreated = 'GA_LINK_CREATED';
12
+ const telemBasError = 'SERVICE_INQUIRER_BAS_ERROR';
11
13
  /**
12
14
  * Constants specific to error handling
13
15
  */
@@ -40,6 +42,9 @@ var ERROR_TYPE;
40
42
  ERROR_TYPE["DESTINATION_MISCONFIGURED"] = "DESTINATION_MISCONFIGURED";
41
43
  ERROR_TYPE["NO_V2_SERVICES"] = "NO_V2_SERVICES";
42
44
  ERROR_TYPE["NO_V4_SERVICES"] = "NO_V4_SERVICES";
45
+ ERROR_TYPE["BAD_REQUEST"] = "BAD_REQUEST";
46
+ ERROR_TYPE["DESTINATION_CONNECTION_ERROR"] = "DESTINATION_CONNECTION_ERROR";
47
+ ERROR_TYPE["SERVER_HTTP_ERROR"] = "SERVER_HTTP_ERROR";
43
48
  })(ERROR_TYPE || (exports.ERROR_TYPE = ERROR_TYPE = {}));
44
49
  // Used to match regex expressions to error messages, etc. providing a way to return a consistent
45
50
  // single error and error msg for multiple errors
@@ -83,7 +88,10 @@ exports.ERROR_MAP = {
83
88
  [ERROR_TYPE.DESTINATION_NOT_FOUND]: [],
84
89
  [ERROR_TYPE.DESTINATION_MISCONFIGURED]: [],
85
90
  [ERROR_TYPE.NO_V2_SERVICES]: [],
86
- [ERROR_TYPE.NO_V4_SERVICES]: []
91
+ [ERROR_TYPE.NO_V4_SERVICES]: [],
92
+ [ERROR_TYPE.BAD_REQUEST]: [/400/],
93
+ [ERROR_TYPE.DESTINATION_CONNECTION_ERROR]: [],
94
+ [ERROR_TYPE.SERVER_HTTP_ERROR]: [/5\d\d/] // catch all for 5xx server errors
87
95
  };
88
96
  /**
89
97
  * Maps errors to end-user messages using some basic root cause analysis based on regex matching.
@@ -96,48 +104,87 @@ class ErrorHandler {
96
104
  currentErrorType;
97
105
  static _guidedAnswersEnabled;
98
106
  static _logger;
99
- // Get the required localized parameterized error message
100
- // Note that these are general fallback end-user error messages.
101
- // More specific error messages can be used at the point of error generation.
102
- static _errorMsg = (error) => ({
103
- [ERROR_TYPE.CERT]: (0, i18n_1.t)('errors.certificateError', { error }),
104
- [ERROR_TYPE.CERT_EXPIRED]: (0, i18n_1.t)('errors.urlCertValidationError', { certErrorReason: (0, i18n_1.t)('texts.anExpiredCert') }),
105
- [ERROR_TYPE.CERT_SELF_SIGNED]: (0, i18n_1.t)('errors.urlCertValidationError', {
107
+ static getMessageFromError = (error) => {
108
+ return (error?.message ||
109
+ error?.status?.toString() ||
110
+ error?.response?.status?.toString() ||
111
+ (typeof error === 'string' ? error : JSON.stringify(error)));
112
+ };
113
+ // Get the localized parameterized error message for the specified error type
114
+ static _errorTypeToMsg = {
115
+ [ERROR_TYPE.CERT]: (error) => (0, i18n_1.t)('errors.certificateError', { error: typeof error === 'string' ? error : JSON.stringify(error) }),
116
+ [ERROR_TYPE.CERT_EXPIRED]: () => (0, i18n_1.t)('errors.urlCertValidationError', { certErrorReason: (0, i18n_1.t)('texts.anExpiredCert') }),
117
+ [ERROR_TYPE.CERT_SELF_SIGNED]: () => (0, i18n_1.t)('errors.urlCertValidationError', {
106
118
  certErrorReason: (0, i18n_1.t)('texts.aSelfSignedCert')
107
119
  }),
108
- [ERROR_TYPE.CERT_UKNOWN_OR_INVALID]: (0, i18n_1.t)('errors.urlCertValidationError', {
120
+ [ERROR_TYPE.CERT_UKNOWN_OR_INVALID]: () => (0, i18n_1.t)('errors.urlCertValidationError', {
109
121
  certErrorReason: (0, i18n_1.t)('texts.anUnknownOrInvalidCert')
110
122
  }),
111
- [ERROR_TYPE.CERT_SELF_SIGNED_CERT_IN_CHAIN]: (0, i18n_1.t)('errors.urlCertValidationError', {
123
+ [ERROR_TYPE.CERT_SELF_SIGNED_CERT_IN_CHAIN]: () => (0, i18n_1.t)('errors.urlCertValidationError', {
112
124
  certErrorReason: (0, i18n_1.t)('texts.anUntrustedRootCert')
113
125
  }),
114
- [ERROR_TYPE.AUTH]: (0, i18n_1.t)('errors.authenticationFailed', { error }),
115
- [ERROR_TYPE.AUTH_TIMEOUT]: (0, i18n_1.t)('errors.authenticationTimeout'),
116
- [ERROR_TYPE.TIMEOUT]: (0, i18n_1.t)('errors.timeout, { error }'),
117
- [ERROR_TYPE.INVALID_URL]: (0, i18n_1.t)('errors.invalidUrl'),
118
- [ERROR_TYPE.CONNECTION]: (0, i18n_1.t)('errors.connectionError', {
119
- error: error?.message || JSON.stringify(error)
126
+ [ERROR_TYPE.AUTH]: (error) => (0, i18n_1.t)('errors.authenticationFailed', {
127
+ error: ErrorHandler.getMessageFromError(error)
128
+ }),
129
+ [ERROR_TYPE.AUTH_TIMEOUT]: () => (0, i18n_1.t)('errors.authenticationTimeout'),
130
+ [ERROR_TYPE.TIMEOUT]: (error) => (0, i18n_1.t)('errors.timeout', { error: typeof error === 'string' ? error : JSON.stringify(error) }),
131
+ [ERROR_TYPE.INVALID_URL]: () => (0, i18n_1.t)('errors.invalidUrl'),
132
+ [ERROR_TYPE.CONNECTION]: (error) => (0, i18n_1.t)('errors.connectionError', {
133
+ error: ErrorHandler.getMessageFromError(error)
120
134
  }),
121
- [ERROR_TYPE.UNKNOWN]: (0, i18n_1.t)('errors.unknownError', {
122
- error: error?.message || JSON.stringify(error)
135
+ [ERROR_TYPE.UNKNOWN]: (error) => (0, i18n_1.t)('errors.unknownError', {
136
+ error: ErrorHandler.getMessageFromError(error)
137
+ }),
138
+ [ERROR_TYPE.SERVICES_UNAVAILABLE]: () => (0, i18n_1.t)('errors.servicesUnavailable'),
139
+ [ERROR_TYPE.SERVICE_UNAVAILABLE]: (error) => (0, i18n_1.t)('errors.serverReturnedAnError', {
140
+ errorMsg: ErrorHandler.getMessageFromError(error)
141
+ }),
142
+ [ERROR_TYPE.CATALOG_SERVICE_NOT_ACTIVE]: () => (0, i18n_1.t)('errors.catalogServiceNotActive'),
143
+ [ERROR_TYPE.INTERNAL_SERVER_ERROR]: (error) => {
144
+ const errorMsg = ErrorHandler.getMessageFromError(error);
145
+ return (0, i18n_1.t)('errors.serverReturnedAnError', {
146
+ errorDesc: (0, i18n_1.t)('errors.internalServerError', { errorMsg })
147
+ });
148
+ },
149
+ [ERROR_TYPE.NOT_FOUND]: () => (0, i18n_1.t)('errors.urlNotFound'),
150
+ [ERROR_TYPE.ODATA_URL_NOT_FOUND]: () => (0, i18n_1.t)('errors.odataServiceUrlNotFound'),
151
+ [ERROR_TYPE.BAD_GATEWAY]: (error) => {
152
+ const errorMsg = ErrorHandler.getMessageFromError(error);
153
+ return (0, i18n_1.t)('errors.serverReturnedAnError', {
154
+ errorDesc: (0, i18n_1.t)('errors.badGateway', { errorMsg })
155
+ });
156
+ },
157
+ [ERROR_TYPE.DESTINATION_UNAVAILABLE]: () => (0, i18n_1.t)('errors.destination.unavailable'),
158
+ [ERROR_TYPE.DESTINATION_NOT_FOUND]: () => (0, i18n_1.t)('errors.destination.notFound'),
159
+ [ERROR_TYPE.DESTINATION_MISCONFIGURED]: (error) => (0, i18n_1.t)('errors.destination.misconfigured', {
160
+ destinationProperty: typeof error === 'string' ? error : JSON.stringify(error)
123
161
  }),
124
- [ERROR_TYPE.SERVICES_UNAVAILABLE]: (0, i18n_1.t)('errors.servicesUnavailable'),
125
- [ERROR_TYPE.SERVICE_UNAVAILABLE]: (0, i18n_1.t)('errors.serviceUnavailable'),
126
- [ERROR_TYPE.CATALOG_SERVICE_NOT_ACTIVE]: (0, i18n_1.t)('errors.catalogServiceNotActive'),
127
- [ERROR_TYPE.INTERNAL_SERVER_ERROR]: (0, i18n_1.t)('errors.internalServerError', { error: error?.message }),
128
- [ERROR_TYPE.NOT_FOUND]: (0, i18n_1.t)('errors.urlNotFound'),
129
- [ERROR_TYPE.ODATA_URL_NOT_FOUND]: (0, i18n_1.t)('errors.odataServiceUrlNotFound'),
130
- [ERROR_TYPE.BAD_GATEWAY]: (0, i18n_1.t)('errors.badGateway'),
131
- [ERROR_TYPE.DESTINATION_UNAVAILABLE]: (0, i18n_1.t)('errors.destinationUnavailable'),
132
- [ERROR_TYPE.DESTINATION_NOT_FOUND]: (0, i18n_1.t)('errors.destinationNotFound'),
133
- [ERROR_TYPE.DESTINATION_MISCONFIGURED]: (0, i18n_1.t)('errors.destinationMisconfigured'),
134
- [ERROR_TYPE.NO_V2_SERVICES]: (0, i18n_1.t)('errors.noServicesAvailable', { version: '2' }),
135
- [ERROR_TYPE.NO_V4_SERVICES]: (0, i18n_1.t)('errors.noServicesAvailable', { version: '4' }),
136
- [ERROR_TYPE.DESTINATION_BAD_GATEWAY_503]: (0, i18n_1.t)('errors.destinationUnavailable'),
137
- [ERROR_TYPE.REDIRECT]: (0, i18n_1.t)('errors.redirectError'),
138
- [ERROR_TYPE.NO_SUCH_HOST]: (0, i18n_1.t)('errors.noSuchHostError'),
139
- [ERROR_TYPE.NO_ABAP_ENVS]: (0, i18n_1.t)('errors.abapEnvsUnavailable')
140
- });
162
+ [ERROR_TYPE.NO_V2_SERVICES]: () => (0, i18n_1.t)('errors.noServicesAvailable', { version: '2' }),
163
+ [ERROR_TYPE.NO_V4_SERVICES]: () => (0, i18n_1.t)('errors.noServicesAvailable', { version: '4' }),
164
+ [ERROR_TYPE.DESTINATION_BAD_GATEWAY_503]: () => (0, i18n_1.t)('errors.destination.unavailable'),
165
+ [ERROR_TYPE.REDIRECT]: () => (0, i18n_1.t)('errors.redirectError'),
166
+ [ERROR_TYPE.NO_SUCH_HOST]: () => (0, i18n_1.t)('errors.noSuchHostError'),
167
+ [ERROR_TYPE.NO_ABAP_ENVS]: () => (0, i18n_1.t)('errors.abapEnvsUnavailable'),
168
+ [ERROR_TYPE.BAD_REQUEST]: (error) => {
169
+ const errorMsg = ErrorHandler.getMessageFromError(error);
170
+ return (0, i18n_1.t)('errors.serverReturnedAnError', {
171
+ errorDesc: (0, i18n_1.t)('errors.badRequest', { errorMsg })
172
+ });
173
+ },
174
+ [ERROR_TYPE.DESTINATION_CONNECTION_ERROR]: () => (0, i18n_1.t)('errors.systemConnectionValidationFailed'),
175
+ [ERROR_TYPE.SERVER_HTTP_ERROR]: (error) => (0, i18n_1.t)('errors.serverReturnedAnError', {
176
+ errorDesc: ErrorHandler.getMessageFromError(error)
177
+ })
178
+ };
179
+ /**
180
+ *
181
+ * @param errorType
182
+ * @param error can be any object that will get stringified and passed to the specific error message for the error type entry, e.g. where the error message is parameterized
183
+ * @returns an error message for the specified error type
184
+ */
185
+ static _errorMsg = (errorType, error) => {
186
+ return ErrorHandler._errorTypeToMsg[errorType](error);
187
+ };
141
188
  /**
142
189
  * Get the Guided Answers (help) node for the specified error type.
143
190
  *
@@ -174,7 +221,10 @@ class ErrorHandler {
174
221
  [ERROR_TYPE.ODATA_URL_NOT_FOUND]: undefined,
175
222
  [ERROR_TYPE.INTERNAL_SERVER_ERROR]: undefined,
176
223
  [ERROR_TYPE.NO_V2_SERVICES]: undefined,
177
- [ERROR_TYPE.TIMEOUT]: undefined
224
+ [ERROR_TYPE.TIMEOUT]: undefined,
225
+ [ERROR_TYPE.BAD_REQUEST]: undefined,
226
+ [ERROR_TYPE.DESTINATION_CONNECTION_ERROR]: guided_answers_helper_1.HELP_NODES.DESTINATION_CONNECTION_ERRORS,
227
+ [ERROR_TYPE.SERVER_HTTP_ERROR]: undefined
178
228
  };
179
229
  return errorToHelp[errorType];
180
230
  };
@@ -310,7 +360,7 @@ class ErrorHandler {
310
360
  errorType = ErrorHandler.getErrorType(this.findErrorValueForMapping(error)) ?? ERROR_TYPE.UNKNOWN;
311
361
  }
312
362
  return {
313
- errorMsg: ErrorHandler._errorMsg(error)[errorType],
363
+ errorMsg: ErrorHandler._errorMsg(errorType, error),
314
364
  errorType
315
365
  };
316
366
  }
@@ -342,31 +392,83 @@ class ErrorHandler {
342
392
  * Used by validate functions to report in-line user friendly errors messages with help links.
343
393
  * If the error type is unknown, this will find a mapped error type and return the help (ValidationLink) if it exists.
344
394
  * If an error is not provided the current error state will be used. This does not log the message to the console.
395
+ * If a system is provided, the error type may be refined to provide a more specific error message for the system which generatd the error.
345
396
  *
346
397
  * @param error optional, if provided get the help link message that it maps to, otherwise get the previously logged error message help link
347
398
  * @param reset optional, resets the previous error state if true
399
+ * @param destination optional, if provided the destination may be used to determine a more relevant error message, specific to the system properties
348
400
  * @returns An instance of @see {ValidationLink}
349
401
  */
350
- getValidationErrorHelp(error, reset = false) {
402
+ getValidationErrorHelp(error, reset = false, destination) {
351
403
  let errorHelp;
352
- let errorMsg;
404
+ let resolvedErrorMsg;
405
+ let resolvedErrorType;
353
406
  if (error) {
354
- const resolvedError = ErrorHandler.mapErrorToMsg(error);
355
- if (resolvedError.errorType !== ERROR_TYPE.UNKNOWN) {
356
- errorHelp = ErrorHandler.getHelpForError(resolvedError.errorType, resolvedError.errorMsg);
357
- }
407
+ ({ errorMsg: resolvedErrorMsg, errorType: resolvedErrorType } = ErrorHandler.mapErrorToMsg(error));
358
408
  }
359
- else if (!error) {
360
- errorMsg = this.currentErrorMsg ?? '';
409
+ else {
410
+ // Use the existing error if we have it
411
+ resolvedErrorMsg = this.currentErrorMsg ?? undefined;
361
412
  if (this.currentErrorType) {
362
- errorHelp = ErrorHandler.getHelpForError(this.currentErrorType, errorMsg);
413
+ resolvedErrorType = this.currentErrorType;
414
+ }
415
+ }
416
+ if (resolvedErrorType) {
417
+ // If the destination is provided, we can refine the error type and therefore the generated help message, to be more specific
418
+ if (destination) {
419
+ const { errorType: destErrorType, errorMsg: destErrorMsg } = ErrorHandler.getDestinationSpecificError(resolvedErrorType, destination);
420
+ resolvedErrorMsg = destErrorMsg ?? resolvedErrorMsg;
421
+ resolvedErrorType = destErrorType ?? resolvedErrorType;
422
+ }
423
+ if (resolvedErrorType) {
424
+ errorHelp = ErrorHandler.getHelpForError(resolvedErrorType, resolvedErrorMsg);
363
425
  }
364
426
  }
365
427
  if (reset) {
366
428
  this.currentErrorMsg = null;
367
429
  this.currentErrorType = null;
368
430
  }
369
- return errorHelp ?? errorMsg;
431
+ return errorHelp ?? resolvedErrorMsg; // We mau not have a help link, so return the resolvedend user message
432
+ }
433
+ /**
434
+ * Get a more specific error type for the specified destination.
435
+ *
436
+ * @param errorType
437
+ * @param destination
438
+ * @returns
439
+ */
440
+ static getDestinationSpecificError(errorType, destination) {
441
+ let destErrorType;
442
+ let destErrorMsg;
443
+ // Add more specific error types for destinations here
444
+ if (!(0, btp_utils_1.isHTML5DynamicConfigured)(destination)) {
445
+ destErrorType = ERROR_TYPE.DESTINATION_MISCONFIGURED;
446
+ destErrorMsg = this.getErrorMsgFromType(destErrorType, 'HTML5.DynamicDestination');
447
+ }
448
+ else if (errorType === ERROR_TYPE.SERVICE_UNAVAILABLE) {
449
+ if ((0, btp_utils_1.isOnPremiseDestination)(destination)) {
450
+ destErrorType = ERROR_TYPE.DESTINATION_BAD_GATEWAY_503; // Remap to specific gateway to allow GA link to be associated
451
+ }
452
+ else {
453
+ destErrorType = ERROR_TYPE.DESTINATION_CONNECTION_ERROR; // General destination connection error, GA link to connection page
454
+ }
455
+ }
456
+ else if (errorType === ERROR_TYPE.NOT_FOUND) {
457
+ destErrorType = ERROR_TYPE.DESTINATION_NOT_FOUND;
458
+ }
459
+ else if (ERROR_TYPE.INTERNAL_SERVER_ERROR === errorType || ERROR_TYPE.SERVER_HTTP_ERROR === errorType) {
460
+ // We cannot tell in BAS what this means, so we will just say the connection failed
461
+ destErrorType = ERROR_TYPE.DESTINATION_CONNECTION_ERROR;
462
+ }
463
+ // Always raise a telemetry event for destination related errors
464
+ (0, utils_1.sendTelemetryEvent)(telemBasError, {
465
+ basErrorType: destErrorType ?? errorType,
466
+ destODataType: (0, utils_1.getTelemPropertyDestinationType)(destination)
467
+ });
468
+ return {
469
+ errorType: destErrorType ?? errorType,
470
+ errorMsg: destErrorMsg
471
+ };
370
472
  }
371
473
  /**
372
474
  * Get the error message for the specified error type.
@@ -377,7 +479,7 @@ class ErrorHandler {
377
479
  */
378
480
  static getErrorMsgFromType(errorType, error) {
379
481
  if (ERROR_TYPE[errorType]) {
380
- return ErrorHandler._errorMsg(error)[ERROR_TYPE[errorType]];
482
+ return ErrorHandler._errorMsg(ERROR_TYPE[errorType], error);
381
483
  }
382
484
  return undefined;
383
485
  }
@@ -402,7 +504,7 @@ class ErrorHandler {
402
504
  * @param error - the original error, if any
403
505
  */
404
506
  setCurrentError(errorType, error) {
405
- this.currentErrorMsg = ErrorHandler._errorMsg(error)[ERROR_TYPE[errorType]];
507
+ this.currentErrorMsg = ErrorHandler._errorMsg(ERROR_TYPE[errorType], error);
406
508
  this.currentErrorType = errorType;
407
509
  }
408
510
  /**
@@ -451,7 +553,7 @@ class ErrorHandler {
451
553
  };
452
554
  }
453
555
  // Report the GA link created event
454
- (0, utils_1.sendTelemetryEvent)(teleEventGALinkCreated, {
556
+ (0, utils_1.sendTelemetryEvent)(telemEventGALinkCreated, {
455
557
  errorType,
456
558
  isGuidedAnswersEnabled: this.guidedAnswersEnabled,
457
559
  nodeIdPath: `${helpNode}`
package/dist/i18n.js CHANGED
@@ -14,16 +14,28 @@ exports.defaultProjectNumber = 1;
14
14
  * Initialize i18next with the translations for this module.
15
15
  */
16
16
  async function initI18nOdataServiceInquirer() {
17
+ const t0 = performance.now();
17
18
  await i18next_1.default.init({
18
19
  lng: 'en',
19
20
  fallbackLng: 'en',
20
21
  missingInterpolationHandler: () => '',
21
22
  interpolation: {
22
- format: function odataVersionFormatter(odataVersion) {
23
- return odataVersion ? ` V${odataVersion}` : '';
23
+ format: function (value, format) {
24
+ // OData version formatter
25
+ if (format === 'odataVersionFormatter') {
26
+ return value ? ` V${value}` : '';
27
+ }
28
+ // If we have a value add a colon before outputting
29
+ if (format === 'addMsgWithColonFormatter') {
30
+ return value ? `: ${value}` : '';
31
+ }
32
+ return value;
24
33
  }
25
34
  }
26
- }, () => i18next_1.default.addResourceBundle('en', odataServiceInquirerNamespace, odata_service_inquirer_i18n_json_1.default));
35
+ });
36
+ i18next_1.default.addResourceBundle('en', odataServiceInquirerNamespace, odata_service_inquirer_i18n_json_1.default);
37
+ const t1 = performance.now();
38
+ console.log(`i18n load time: ${Math.round(t1 - t0)} milliseconds`);
27
39
  }
28
40
  /**
29
41
  * Helper function facading the call to i18next. Unless a namespace option is provided the local namespace will be used.
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import { type Logger } from '@sap-ux/logger';
3
3
  import { OdataVersion } from '@sap-ux/odata-service-writer';
4
4
  import { type ToolsSuiteTelemetryClient } from '@sap-ux/telemetry';
5
5
  import { ERROR_TYPE, ErrorHandler } from './error-handler/error-handler';
6
- import { newSystemChoiceValue } from './prompts/datasources/sap-system/new-system/questions';
6
+ import { SystemSelectionAnswerType } from './prompts/datasources/sap-system/system-selection';
7
7
  import { DatasourceType, promptNames, type CapRuntime, type CapService, type OdataServiceAnswers, type OdataServicePromptOptions, type OdataServiceQuestion, type SapSystemType } from './types';
8
8
  /**
9
9
  * Get the inquirer prompts for odata service.
@@ -31,5 +31,5 @@ declare function getPrompts(promptOptions?: OdataServicePromptOptions, logger?:
31
31
  * @returns the prompt answers
32
32
  */
33
33
  declare function prompt(adapter: InquirerAdapter, promptOptions?: OdataServicePromptOptions, logger?: Logger, enableGuidedAnswers?: boolean, telemetryClient?: ToolsSuiteTelemetryClient, isYUI?: boolean): Promise<OdataServiceAnswers>;
34
- export { DatasourceType, ERROR_TYPE, ErrorHandler, OdataVersion, getPrompts, newSystemChoiceValue, prompt, promptNames, type CapRuntime, type CapService, type InquirerAdapter, type OdataServiceAnswers, type OdataServicePromptOptions, type SapSystemType };
34
+ export { DatasourceType, ERROR_TYPE, ErrorHandler, getPrompts, OdataVersion, prompt, promptNames, SystemSelectionAnswerType, type CapRuntime, type CapService, type InquirerAdapter, type OdataServiceAnswers, type OdataServicePromptOptions, type SapSystemType };
35
35
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.promptNames = exports.newSystemChoiceValue = exports.OdataVersion = exports.ErrorHandler = exports.ERROR_TYPE = exports.DatasourceType = void 0;
6
+ exports.promptNames = exports.OdataVersion = exports.ErrorHandler = exports.ERROR_TYPE = exports.DatasourceType = void 0;
7
7
  exports.getPrompts = getPrompts;
8
8
  exports.prompt = prompt;
9
9
  const odata_service_writer_1 = require("@sap-ux/odata-service-writer");
@@ -14,8 +14,6 @@ Object.defineProperty(exports, "ERROR_TYPE", { enumerable: true, get: function (
14
14
  Object.defineProperty(exports, "ErrorHandler", { enumerable: true, get: function () { return error_handler_1.ErrorHandler; } });
15
15
  const i18n_1 = require("./i18n");
16
16
  const prompts_1 = require("./prompts");
17
- const questions_1 = require("./prompts/datasources/sap-system/new-system/questions");
18
- Object.defineProperty(exports, "newSystemChoiceValue", { enumerable: true, get: function () { return questions_1.newSystemChoiceValue; } });
19
17
  const logger_helper_1 = __importDefault(require("./prompts/logger-helper"));
20
18
  const types_1 = require("./types");
21
19
  Object.defineProperty(exports, "DatasourceType", { enumerable: true, get: function () { return types_1.DatasourceType; } });
@@ -1,6 +1,8 @@
1
1
  import type { IValidationLink } from '@sap-devx/yeoman-ui-types';
2
2
  import type { AxiosRequestConfig, CatalogService, ODataService, ServiceInfo, ServiceProvider } from '@sap-ux/axios-extension';
3
3
  import { ODataVersion } from '@sap-ux/axios-extension';
4
+ import { type Destination } from '@sap-ux/btp-utils';
5
+ import { ERROR_TYPE } from '../error-handler/error-handler';
4
6
  /**
5
7
  * Structure to store validity information about url to be validated.
6
8
  */
@@ -11,7 +13,7 @@ interface Validity {
11
13
  authenticated?: boolean;
12
14
  canSkipCertError?: boolean;
13
15
  }
14
- type ValidationResult = string | boolean | IValidationLink;
16
+ export type ValidationResult = string | boolean | IValidationLink;
15
17
  export type SystemAuthType = 'serviceKey' | 'reentranceTicket' | 'basic' | 'unknown';
16
18
  /**
17
19
  * Class that can be used to determine the connectivity using a service url, system url, or service info (UAA Key details) or reentrance ticket.
@@ -23,6 +25,8 @@ export type SystemAuthType = 'serviceKey' | 'reentranceTicket' | 'basic' | 'unkn
23
25
  export declare class ConnectionValidator {
24
26
  readonly validity: Validity;
25
27
  private _validatedUrl;
28
+ private _destinationUrl;
29
+ private _destination;
26
30
  private _validatedClient;
27
31
  private _odataService;
28
32
  private _serviceProvider;
@@ -105,6 +109,18 @@ export declare class ConnectionValidator {
105
109
  * @returns the refresh token
106
110
  */
107
111
  get refreshToken(): string | undefined;
112
+ /**
113
+ * 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).
114
+ *
115
+ * @returns the connected destination 'URL' attribute value
116
+ */
117
+ get destinationUrl(): string | undefined;
118
+ /**
119
+ * Get the destination.
120
+ *
121
+ * @returns the connected destination 'URL' attribute value
122
+ */
123
+ get destination(): Destination | undefined;
108
124
  /**
109
125
  * Get the connected system name. If previously set this will be used, otherwise the name is determined
110
126
  * by the system auth type, or the validated url.
@@ -128,8 +144,9 @@ export declare class ConnectionValidator {
128
144
  * @param options.isSystem if true, the url will be treated as a system url rather than a service url
129
145
  * @param options.odataVersion if specified will restrict catalog requests to only the specified odata version
130
146
  * @returns the status code or error returned by the connection attempt
147
+ * @throws an error if the connection attempt fails and the error is a 500 on App Studio or a non-axios error is caught
131
148
  */
132
- private checkSapServiceUrl;
149
+ private checkUrl;
133
150
  /**
134
151
  * Create the axios configuration object for the service or system connection.
135
152
  *
@@ -147,7 +164,11 @@ export declare class ConnectionValidator {
147
164
  * @param servicePath the service path without the origin
148
165
  */
149
166
  private createOdataServiceConnection;
150
- resetConnectionState(): void;
167
+ /**
168
+ *
169
+ * @param resetValidity
170
+ */
171
+ resetConnectionState(resetValidity?: boolean): void;
151
172
  /**
152
173
  * Create the connection for a system url, the specified axios config or the specified service info.
153
174
  *
@@ -156,6 +177,8 @@ export declare class ConnectionValidator {
156
177
  * @param connectConfig.url the system url
157
178
  * @param connectConfig.serviceInfo the service info
158
179
  * @param connectConfig.odataVersion the odata version to restrict the catalog requests if only a specific version is required
180
+ * @param connectConfig.destination the destination to connect with
181
+ * @throws an error if the connection attempt fails, callers should handle the error
159
182
  */
160
183
  private createSystemConnection;
161
184
  /**
@@ -166,7 +189,7 @@ export declare class ConnectionValidator {
166
189
  */
167
190
  private shouldAttemptV4Catalog;
168
191
  /**
169
- * Callback for when the refresh token changes.
192
+ * Callback for when the Abap cloud system refresh token changes.
170
193
  *
171
194
  * @param refreshToken the new refresh token
172
195
  */
@@ -181,14 +204,38 @@ export declare class ConnectionValidator {
181
204
  private getAbapOnCloudServiceProvider;
182
205
  /**
183
206
  * Validate the system connectivity with the specified service info (containing UAA details).
207
+ * This will create a connection to the system, updating the service provider reference.
208
+ * The connected user name will be cached for later use.
184
209
  *
185
210
  * @param serviceInfo the service info containing the UAA details
186
211
  * @param odataVersion the odata version to restrict the catalog requests if only a specific version is required
187
- * @returns true if the system is reachable, false if not, or an error message string
212
+ * @returns true if the system is reachable and authenticated, if required, false if not, or an error message string
188
213
  */
189
214
  validateServiceInfo(serviceInfo: ServiceInfo, odataVersion?: ODataVersion): Promise<ValidationResult>;
190
215
  /**
191
- * Validates the system or service url format as well as its reachability.
216
+ * Validate the specified destination connectivity, determining if authentication is required or if the destination is misconfigured.
217
+ *
218
+ * @param destination the destination to validate
219
+ * @param odataVersion the odata version to restrict the catalog requests if only a specific version is required
220
+ * @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
221
+ * @returns true if the system is reachable and authenticated, if required, false if not, or an error message string
222
+ */
223
+ validateDestination(destination: Destination, odataVersion?: ODataVersion, servicePath?: string): Promise<{
224
+ valResult: ValidationResult;
225
+ errorType?: ERROR_TYPE;
226
+ }>;
227
+ /**
228
+ * Validate the connectivity to a destination odata service (for generic odata service destinations), determining, for example, if authentication is required.
229
+ *
230
+ * @param destination a destination that is a generic odata service destination
231
+ * @param servicePath additional service path to append to the destination URL
232
+ * @param requiredOdataVersion the required odata version, if specified will validate the odata service version satisfies the specified version
233
+ * @returns validation result and error type if validation fails
234
+ */
235
+ private validateOdataServiceDestination;
236
+ /**
237
+ * 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
238
+ * 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).
192
239
  *
193
240
  * @param serviceUrl the url to validate, may be a system or service url.
194
241
  * Note that if systemAuthType is specified, the url will be treated as a system url (only the origin will be considered)
@@ -197,17 +244,18 @@ export declare class ConnectionValidator {
197
244
  * @param options.forceReValidation force re-validation of the url
198
245
  * @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
199
246
  * @param options.odataVersion if specified will restrict catalog requests to only the specified odata version
247
+ * @param options.systemAuthType the system auth type used to create system connections, if not specified or `isSystem` is false or undefined, `basic` is assumed
200
248
  * @returns true if the url is reachable, false if not, or an error message string
201
249
  */
202
- validateUrl(serviceUrl: string, { ignoreCertError, forceReValidation, isSystem, odataVersion }?: {
250
+ validateUrl(serviceUrl: string, { ignoreCertError, forceReValidation, isSystem, odataVersion, systemAuthType }?: {
203
251
  ignoreCertError?: boolean;
204
252
  forceReValidation?: boolean;
205
253
  isSystem?: boolean;
206
254
  odataVersion?: ODataVersion;
255
+ systemAuthType?: SystemAuthType;
207
256
  }): Promise<ValidationResult>;
208
257
  /**
209
- * Converts the http status code into 'validty' and returns true if the status code indicates that the URL was reachable.
210
- * Sets the instance validity state based on the status code.
258
+ * Sets the instance validity state based on the status code and returns true if the status code indicates that the URL was reachable.
211
259
  *
212
260
  * @param status a http request status code used to determine the validation result
213
261
  * @returns true, if the status code indicates the url is reachable, false if not, or an error message string
@@ -229,7 +277,7 @@ export declare class ConnectionValidator {
229
277
  private isUrlValidated;
230
278
  /**
231
279
  * Check whether basic auth is required for the given url, or for the previously validated url if none specified.
232
- * This will also set the validity state for the url. This will not validate the URL.
280
+ * This will also set the validity state for the url.
233
281
  *
234
282
  * @param urlString the url to validate, if not provided the previously validated url will be used
235
283
  * @param client optional, sap client code, if not provided the previously validated client will be used
@@ -250,12 +298,15 @@ export declare class ConnectionValidator {
250
298
  * @param options.odataVersion if specified will restrict catalog requests to only the specified odata version
251
299
  * @returns true if the authentication is successful, false if not, or an error message string
252
300
  */
253
- validateAuth(url: string, username: string, password: string, { ignoreCertError, sapClient, odataVersion, isSystem }?: {
301
+ validateAuth(url: string, username?: string, password?: string, { ignoreCertError, sapClient, odataVersion, isSystem }?: {
254
302
  ignoreCertError?: boolean;
255
303
  odataVersion?: ODataVersion;
256
304
  sapClient?: string;
257
305
  isSystem?: boolean;
258
- }): Promise<ValidationResult>;
306
+ }): Promise<{
307
+ valResult: ValidationResult;
308
+ errorType?: ERROR_TYPE;
309
+ }>;
259
310
  /**
260
311
  * Reset the validity state.
261
312
  */