utils.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import * as Sentry from '@sentry/react';
  2. import {t} from 'sentry/locale';
  3. import {
  4. getAppStoreValidationErrorMessage,
  5. unexpectedErrorMessage,
  6. } from 'sentry/utils/appStoreValidationErrorMessage';
  7. import {StepOneData} from './types';
  8. // since translations are done on the front-end we need to map back-end error messages to front-end messages
  9. const fieldErrorMessageMapping = {
  10. appconnectIssuer: {
  11. issuer: {
  12. 'Ensure this field has at least 36 characters.': t(
  13. 'This field should be exactly 36 characters.'
  14. ),
  15. 'Ensure this field has no more than 36 characters.': t(
  16. 'This field should be exactly 36 characters.'
  17. ),
  18. },
  19. },
  20. appconnectKey: {
  21. keyId: {
  22. 'Ensure this field has at least 2 characters.': t(
  23. 'This field should be between 2 and 20 characters.'
  24. ),
  25. 'Ensure this field has no more than 20 characters.': t(
  26. 'This field should be between 2 and 20 characters.'
  27. ),
  28. },
  29. },
  30. };
  31. type ResponseJSONDetailed = {
  32. detail: Parameters<typeof getAppStoreValidationErrorMessage>[0] & {
  33. extra: Record<string, any>;
  34. message: string;
  35. };
  36. };
  37. type AppStoreConnectField = keyof typeof fieldErrorMessageMapping;
  38. type ResponseJSON = Record<AppStoreConnectField, string[]>;
  39. type Error = {
  40. status: number;
  41. responseJSON?: ResponseJSON | ResponseJSONDetailed;
  42. };
  43. export function getAppStoreErrorMessage(
  44. error: Error | string
  45. ): string | Record<keyof StepOneData, string> {
  46. if (typeof error === 'string') {
  47. return error;
  48. }
  49. const detailedErrorResponse = (error.responseJSON as undefined | ResponseJSONDetailed)
  50. ?.detail;
  51. if (detailedErrorResponse) {
  52. return getAppStoreValidationErrorMessage(detailedErrorResponse) as string;
  53. }
  54. const errorResponse = error.responseJSON as undefined | ResponseJSON;
  55. if (!errorResponse) {
  56. return unexpectedErrorMessage;
  57. }
  58. return (Object.keys(errorResponse) as AppStoreConnectField[]).reduce(
  59. (acc, serverSideField) => {
  60. const fieldErrorMessage = fieldErrorMessageMapping[serverSideField] ?? {};
  61. const field = Object.keys(fieldErrorMessage)[0];
  62. const errorMessages: string[] = errorResponse[serverSideField].map(errorMessage => {
  63. if (fieldErrorMessage[field][errorMessage]) {
  64. return fieldErrorMessage[field][errorMessage];
  65. }
  66. // This will be difficult to happen,
  67. // but if it happens we will be able to see which message is not being mapped on the fron-tend
  68. Sentry.withScope(scope => {
  69. scope.setExtra('serverSideField', serverSideField);
  70. scope.setExtra('message', errorMessage);
  71. Sentry.captureException(
  72. new Error('App Store Connect - Untranslated error message')
  73. );
  74. });
  75. return errorMessage;
  76. });
  77. // the UI only displays one error message at a time
  78. return {...acc, [field]: errorMessages[0]};
  79. },
  80. {}
  81. ) as Record<keyof StepOneData, string>;
  82. }