From 96527cb4aa3c6d774ef7d65f37530edf2d4713e3 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 2 Nov 2023 18:15:11 -0400 Subject: [PATCH 01/29] Adding new event for log rocket to track when supabase calls failed Fixing circular dep with logrocket and supabase --- package-lock.json | 95 +++++++++--- package.json | 1 + .../admin/Billing/CapturePaymentMethod.tsx | 3 +- .../admin/Billing/PaymentMethods.tsx | 3 +- src/components/admin/Billing/index.tsx | 3 +- src/components/capture/Create/index.tsx | 2 +- src/components/capture/Edit.tsx | 2 +- .../capture/useDiscoverStartSubscription.ts | 3 +- .../derivation/Create/ConfigHeader.tsx | 2 +- .../derivation/Create/CreateAlternate.tsx | 2 +- .../Bindings/FieldSelection/RefreshButton.tsx | 2 +- .../editor/Bindings/FieldSelection/index.tsx | 2 +- .../editor/Bindings/Store/create.ts | 2 +- src/components/fullPage/Error.tsx | 3 +- .../materialization/Create/index.tsx | 2 +- src/components/materialization/Edit.tsx | 2 +- src/components/shared/Entity/Actions/Save.tsx | 3 +- .../shared/Entity/Actions/SaveButton.tsx | 2 +- .../shared/Entity/Actions/SchemaEvolution.tsx | 2 +- .../shared/Entity/Actions/TestButton.tsx | 2 +- .../ExistingEntityCards/Cards/Existing.tsx | 3 +- .../Entity/ExistingEntityCards/Cards/New.tsx | 3 +- .../Entity/hooks/useEntityWorkflowHelpers.ts | 3 +- src/components/shared/ErrorBoundryWrapper.tsx | 3 +- src/directives/shared.ts | 3 +- src/services/logrocket.ts | 33 ---- src/services/shared.ts | 13 ++ src/services/supabase.ts | 145 ++++++------------ src/services/types.ts | 22 +++ 29 files changed, 193 insertions(+), 173 deletions(-) create mode 100644 src/services/shared.ts create mode 100644 src/services/types.ts diff --git a/package-lock.json b/package-lock.json index c8934390d..319c2481b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,6 +50,7 @@ "monaco-editor": "^0.34.1", "notistack": "^2.0.8", "p-limit": "^4.0.0", + "p-retry": "^6.1.0", "path-list-to-tree": "^1.1.1", "pretty-bytes": "^6.1.0", "react": "^17.0.2", @@ -6743,10 +6744,9 @@ } }, "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "dev": true + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==" }, "node_modules/@types/scheduler": { "version": "0.16.2", @@ -14569,6 +14569,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-network-error": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.0.0.tgz", + "integrity": "sha512-P3fxi10Aji2FZmHTrMPSNFbNC6nnp4U5juPAIjXPHkUNubi4+qK7vvdsaNpAUwXslhYm9oyjEYTxs1xd/+Ph0w==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -21614,16 +21625,19 @@ } }, "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "dev": true, + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.1.0.tgz", + "integrity": "sha512-fJLEQ2KqYBJRuaA/8cKMnqhulqNM+bpcjYtXNex2t3mOXKRYPitAJt9NacSf8XAFzcYahSAbKpobiWDSqHSh2g==", "dependencies": { - "@types/retry": "0.12.0", + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", "retry": "^0.13.1" }, "engines": { - "node": ">=8" + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-try": { @@ -26195,7 +26209,6 @@ "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true, "engines": { "node": ">= 4" } @@ -29415,6 +29428,25 @@ } } }, + "node_modules/webpack-dev-server/node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/webpack-dev-server/node_modules/schema-utils": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", @@ -35125,10 +35157,9 @@ } }, "@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "dev": true + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==" }, "@types/scheduler": { "version": "0.16.2", @@ -40971,6 +41002,11 @@ "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true }, + "is-network-error": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.0.0.tgz", + "integrity": "sha512-P3fxi10Aji2FZmHTrMPSNFbNC6nnp4U5juPAIjXPHkUNubi4+qK7vvdsaNpAUwXslhYm9oyjEYTxs1xd/+Ph0w==" + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -46266,12 +46302,12 @@ } }, "p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "dev": true, + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.1.0.tgz", + "integrity": "sha512-fJLEQ2KqYBJRuaA/8cKMnqhulqNM+bpcjYtXNex2t3mOXKRYPitAJt9NacSf8XAFzcYahSAbKpobiWDSqHSh2g==", "requires": { - "@types/retry": "0.12.0", + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", "retry": "^0.13.1" } }, @@ -49531,8 +49567,7 @@ "retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" }, "reusify": { "version": "1.0.4", @@ -52012,6 +52047,22 @@ "ws": "^8.4.2" }, "dependencies": { + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "requires": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + } + }, "schema-utils": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", diff --git a/package.json b/package.json index b5a46c2ae..c71b53a1b 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "monaco-editor": "^0.34.1", "notistack": "^2.0.8", "p-limit": "^4.0.0", + "p-retry": "^6.1.0", "path-list-to-tree": "^1.1.1", "pretty-bytes": "^6.1.0", "react": "^17.0.2", diff --git a/src/components/admin/Billing/CapturePaymentMethod.tsx b/src/components/admin/Billing/CapturePaymentMethod.tsx index f2639dfdb..5b3927faa 100644 --- a/src/components/admin/Billing/CapturePaymentMethod.tsx +++ b/src/components/admin/Billing/CapturePaymentMethod.tsx @@ -15,8 +15,9 @@ import { Auth } from '@supabase/ui'; import AlertBox from 'components/shared/AlertBox'; import { useCallback, useEffect, useRef, useState } from 'react'; import { useIntl } from 'react-intl'; -import { CustomEvents, logRocketEvent } from 'services/logrocket'; +import { logRocketEvent } from 'services/shared'; import { getUserDetails } from 'services/supabase'; +import { CustomEvents } from 'services/types'; export interface PaymentFormProps { onSuccess?(id?: string): Promise | void; diff --git a/src/components/admin/Billing/PaymentMethods.tsx b/src/components/admin/Billing/PaymentMethods.tsx index 44e8e82fd..b86d8190a 100644 --- a/src/components/admin/Billing/PaymentMethods.tsx +++ b/src/components/admin/Billing/PaymentMethods.tsx @@ -22,7 +22,8 @@ import TableLoadingRows from 'components/tables/Loading'; import { useEffect, useMemo, useState } from 'react'; import { FormattedMessage } from 'react-intl'; -import { CustomEvents, logRocketEvent } from 'services/logrocket'; +import { logRocketEvent } from 'services/shared'; +import { CustomEvents } from 'services/types'; import { useBilling_selectedTenant, useBilling_setPaymentMethodExists, diff --git a/src/components/admin/Billing/index.tsx b/src/components/admin/Billing/index.tsx index 96817bf6a..02cdf9cd9 100644 --- a/src/components/admin/Billing/index.tsx +++ b/src/components/admin/Billing/index.tsx @@ -22,7 +22,8 @@ import { useEffect, useMemo } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import { FormattedMessage } from 'react-intl'; import { useUnmount } from 'react-use'; -import { CustomEvents, logRocketEvent } from 'services/logrocket'; +import { logRocketEvent } from 'services/shared'; +import { CustomEvents } from 'services/types'; import { useBilling_hydrated, useBilling_invoicesInitialized, diff --git a/src/components/capture/Create/index.tsx b/src/components/capture/Create/index.tsx index 7c6d636fc..3341cd81d 100644 --- a/src/components/capture/Create/index.tsx +++ b/src/components/capture/Create/index.tsx @@ -15,7 +15,7 @@ import useConnectorWithTagDetail from 'hooks/useConnectorWithTagDetail'; import useDraftSpecs from 'hooks/useDraftSpecs'; import usePageTitle from 'hooks/usePageTitle'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { CustomEvents } from 'services/logrocket'; +import { CustomEvents } from 'services/types'; import { useDetailsForm_connectorImage, useDetailsForm_entityNameChanged, diff --git a/src/components/capture/Edit.tsx b/src/components/capture/Edit.tsx index 880714130..41e239d96 100644 --- a/src/components/capture/Edit.tsx +++ b/src/components/capture/Edit.tsx @@ -18,7 +18,7 @@ import useConnectorWithTagDetail from 'hooks/useConnectorWithTagDetail'; import useDraftSpecs from 'hooks/useDraftSpecs'; import usePageTitle from 'hooks/usePageTitle'; import { useCallback, useMemo } from 'react'; -import { CustomEvents } from 'services/logrocket'; +import { CustomEvents } from 'services/types'; import { DetailsFormHydrator } from 'stores/DetailsForm/Hydrator'; import { EndpointConfigHydrator } from 'stores/EndpointConfig/Hydrator'; import ResourceConfigHydrator from 'stores/ResourceConfig/Hydrator'; diff --git a/src/components/capture/useDiscoverStartSubscription.ts b/src/components/capture/useDiscoverStartSubscription.ts index c804a9dd1..069721734 100644 --- a/src/components/capture/useDiscoverStartSubscription.ts +++ b/src/components/capture/useDiscoverStartSubscription.ts @@ -6,7 +6,8 @@ import useEntityWorkflowHelpers from 'components/shared/Entity/hooks/useEntityWo import { useClient } from 'hooks/supabase-swr'; import useStoreDiscoveredCaptures from 'hooks/useStoreDiscoveredCaptures'; import { useCallback } from 'react'; -import { CustomEvents, logRocketEvent } from 'services/logrocket'; +import { logRocketEvent } from 'services/shared'; +import { CustomEvents } from 'services/types'; import { DEFAULT_POLLER_ERROR, JOB_STATUS_POLLER_ERROR, diff --git a/src/components/derivation/Create/ConfigHeader.tsx b/src/components/derivation/Create/ConfigHeader.tsx index 3b611a5a1..096d7fd18 100644 --- a/src/components/derivation/Create/ConfigHeader.tsx +++ b/src/components/derivation/Create/ConfigHeader.tsx @@ -4,7 +4,7 @@ import GitPodButton from 'components/transformation/create/GitPodButton'; import InitializeDraftButton from 'components/transformation/create/InitializeDraftButton'; import invariableStores from 'context/Zustand/invariableStores'; import { useMemo } from 'react'; -import { CustomEvents } from 'services/logrocket'; +import { CustomEvents } from 'services/types'; import { useTransformationCreate_catalogName, useTransformationCreate_language, diff --git a/src/components/derivation/Create/CreateAlternate.tsx b/src/components/derivation/Create/CreateAlternate.tsx index 71db6b3f5..433b8fc7f 100644 --- a/src/components/derivation/Create/CreateAlternate.tsx +++ b/src/components/derivation/Create/CreateAlternate.tsx @@ -16,7 +16,7 @@ import PatchDraftButton from 'components/transformation/create/PatchDraftButton' import DerivationSchema from 'components/transformation/create/Schema'; import usePageTitle from 'hooks/usePageTitle'; import { useUnmount } from 'react-use'; -import { CustomEvents } from 'services/logrocket'; +import { CustomEvents } from 'services/types'; import { useFormStateStore_error, useFormStateStore_logToken, diff --git a/src/components/editor/Bindings/FieldSelection/RefreshButton.tsx b/src/components/editor/Bindings/FieldSelection/RefreshButton.tsx index 45465796f..0d4549a70 100644 --- a/src/components/editor/Bindings/FieldSelection/RefreshButton.tsx +++ b/src/components/editor/Bindings/FieldSelection/RefreshButton.tsx @@ -1,7 +1,7 @@ import { useEditorStore_id } from 'components/editor/Store/hooks'; import EntityCreateSave from 'components/shared/Entity/Actions/Save'; import useEntityWorkflowHelpers from 'components/shared/Entity/hooks/useEntityWorkflowHelpers'; -import { CustomEvents } from 'services/logrocket'; +import { CustomEvents } from 'services/types'; interface Props { disabled: boolean; diff --git a/src/components/editor/Bindings/FieldSelection/index.tsx b/src/components/editor/Bindings/FieldSelection/index.tsx index 077c5d112..410085fa2 100644 --- a/src/components/editor/Bindings/FieldSelection/index.tsx +++ b/src/components/editor/Bindings/FieldSelection/index.tsx @@ -38,7 +38,7 @@ import { useState, } from 'react'; import { FormattedMessage } from 'react-intl'; -import { CustomEvents } from 'services/logrocket'; +import { CustomEvents } from 'services/types'; import { useFormStateStore_isActive, useFormStateStore_setFormState, diff --git a/src/components/editor/Bindings/Store/create.ts b/src/components/editor/Bindings/Store/create.ts index 67b63a634..f86295e3c 100644 --- a/src/components/editor/Bindings/Store/create.ts +++ b/src/components/editor/Bindings/Store/create.ts @@ -11,7 +11,7 @@ import { CollectionData } from 'components/editor/Bindings/types'; import produce from 'immer'; import { forEach, intersection, isEmpty, isPlainObject, union } from 'lodash'; import { Dispatch, SetStateAction } from 'react'; -import { logRocketEvent } from 'services/logrocket'; +import { logRocketEvent } from 'services/shared'; import { CallSupabaseResponse } from 'services/supabase'; import { BindingsEditorStoreNames } from 'stores/names'; import { checkForErrors } from 'stores/utils'; diff --git a/src/components/fullPage/Error.tsx b/src/components/fullPage/Error.tsx index fa13a9720..473259628 100644 --- a/src/components/fullPage/Error.tsx +++ b/src/components/fullPage/Error.tsx @@ -6,7 +6,8 @@ import FullPageWrapper from 'directives/FullPageWrapper'; import { ReactElement, useMemo } from 'react'; import { FormattedMessage } from 'react-intl'; import { useMount } from 'react-use'; -import { CustomEvents, logRocketEvent } from 'services/logrocket'; +import { logRocketEvent } from 'services/shared'; +import { CustomEvents } from 'services/types'; interface Props { error: ErrorDetails; diff --git a/src/components/materialization/Create/index.tsx b/src/components/materialization/Create/index.tsx index 7c079e266..07605de50 100644 --- a/src/components/materialization/Create/index.tsx +++ b/src/components/materialization/Create/index.tsx @@ -14,7 +14,7 @@ import useConnectorWithTagDetail from 'hooks/useConnectorWithTagDetail'; import useDraftSpecs from 'hooks/useDraftSpecs'; import usePageTitle from 'hooks/usePageTitle'; import { useCallback, useEffect, useMemo } from 'react'; -import { CustomEvents } from 'services/logrocket'; +import { CustomEvents } from 'services/types'; import { useDetailsForm_connectorImage } from 'stores/DetailsForm/hooks'; import { DetailsFormHydrator } from 'stores/DetailsForm/Hydrator'; import { EndpointConfigHydrator } from 'stores/EndpointConfig/Hydrator'; diff --git a/src/components/materialization/Edit.tsx b/src/components/materialization/Edit.tsx index 9698bd791..5df4536ba 100644 --- a/src/components/materialization/Edit.tsx +++ b/src/components/materialization/Edit.tsx @@ -17,7 +17,7 @@ import useConnectorWithTagDetail from 'hooks/useConnectorWithTagDetail'; import useDraftSpecs from 'hooks/useDraftSpecs'; import usePageTitle from 'hooks/usePageTitle'; import { useCallback, useMemo } from 'react'; -import { CustomEvents } from 'services/logrocket'; +import { CustomEvents } from 'services/types'; import { DetailsFormHydrator } from 'stores/DetailsForm/Hydrator'; import { EndpointConfigHydrator } from 'stores/EndpointConfig/Hydrator'; import ResourceConfigHydrator from 'stores/ResourceConfig/Hydrator'; diff --git a/src/components/shared/Entity/Actions/Save.tsx b/src/components/shared/Entity/Actions/Save.tsx index 83e56b9da..3395adb78 100644 --- a/src/components/shared/Entity/Actions/Save.tsx +++ b/src/components/shared/Entity/Actions/Save.tsx @@ -19,7 +19,8 @@ import { buttonSx } from 'components/shared/Entity/Header'; import useEntityWorkflowHelpers from 'components/shared/Entity/hooks/useEntityWorkflowHelpers'; import { useClient } from 'hooks/supabase-swr'; import { FormattedMessage, useIntl } from 'react-intl'; -import { CustomEvents, logRocketEvent } from 'services/logrocket'; +import { logRocketEvent } from 'services/shared'; +import { CustomEvents } from 'services/types'; import { DEFAULT_FILTER, JOB_STATUS_COLUMNS, diff --git a/src/components/shared/Entity/Actions/SaveButton.tsx b/src/components/shared/Entity/Actions/SaveButton.tsx index fab5ebfc1..1b4ce71d5 100644 --- a/src/components/shared/Entity/Actions/SaveButton.tsx +++ b/src/components/shared/Entity/Actions/SaveButton.tsx @@ -5,7 +5,7 @@ import LogDialogActions from 'components/shared/Entity/LogDialogActions'; import useEntityWorkflowHelpers from 'components/shared/Entity/hooks/useEntityWorkflowHelpers'; import { useEntityType } from 'context/EntityContext'; import { FormattedMessage } from 'react-intl'; -import { CustomEvents } from 'services/logrocket'; +import { CustomEvents } from 'services/types'; import { useFormStateStore_logToken, useFormStateStore_messagePrefix, diff --git a/src/components/shared/Entity/Actions/SchemaEvolution.tsx b/src/components/shared/Entity/Actions/SchemaEvolution.tsx index 63cfdee5c..8bfc2249c 100644 --- a/src/components/shared/Entity/Actions/SchemaEvolution.tsx +++ b/src/components/shared/Entity/Actions/SchemaEvolution.tsx @@ -16,7 +16,7 @@ import { useClient } from 'hooks/supabase-swr'; import useStoreDiscoveredCaptures from 'hooks/useStoreDiscoveredCaptures'; import { useCallback } from 'react'; import { FormattedMessage } from 'react-intl'; -import { logRocketEvent } from 'services/logrocket'; +import { logRocketEvent } from 'services/shared'; import { DEFAULT_POLLER_ERROR, JOB_STATUS_COLUMNS, diff --git a/src/components/shared/Entity/Actions/TestButton.tsx b/src/components/shared/Entity/Actions/TestButton.tsx index 973986dbb..499413a3b 100644 --- a/src/components/shared/Entity/Actions/TestButton.tsx +++ b/src/components/shared/Entity/Actions/TestButton.tsx @@ -4,7 +4,7 @@ import LogDialog from 'components/shared/Entity/LogDialog'; import LogDialogActions from 'components/shared/Entity/LogDialogActions'; import useEntityWorkflowHelpers from 'components/shared/Entity/hooks/useEntityWorkflowHelpers'; import { FormattedMessage } from 'react-intl'; -import { CustomEvents } from 'services/logrocket'; +import { CustomEvents } from 'services/types'; import { useFormStateStore_logToken, useFormStateStore_messagePrefix, diff --git a/src/components/shared/Entity/ExistingEntityCards/Cards/Existing.tsx b/src/components/shared/Entity/ExistingEntityCards/Cards/Existing.tsx index 349020c26..ab7c7e391 100644 --- a/src/components/shared/Entity/ExistingEntityCards/Cards/Existing.tsx +++ b/src/components/shared/Entity/ExistingEntityCards/Cards/Existing.tsx @@ -13,7 +13,8 @@ import useGlobalSearchParams, { } from 'hooks/searchParams/useGlobalSearchParams'; import { isEmpty } from 'lodash'; import { useIntl } from 'react-intl'; -import { CustomEvents, logRocketEvent } from 'services/logrocket'; +import { logRocketEvent } from 'services/shared'; +import { CustomEvents } from 'services/types'; interface Props { queryData: CaptureQueryWithSpec | MaterializationQueryWithSpec | null; diff --git a/src/components/shared/Entity/ExistingEntityCards/Cards/New.tsx b/src/components/shared/Entity/ExistingEntityCards/Cards/New.tsx index e4e393d32..f3c9aed51 100644 --- a/src/components/shared/Entity/ExistingEntityCards/Cards/New.tsx +++ b/src/components/shared/Entity/ExistingEntityCards/Cards/New.tsx @@ -9,7 +9,8 @@ import useGlobalSearchParams, { } from 'hooks/searchParams/useGlobalSearchParams'; import { Plus } from 'iconoir-react'; import { FormattedMessage } from 'react-intl'; -import { CustomEvents, logRocketEvent } from 'services/logrocket'; +import { logRocketEvent } from 'services/shared'; +import { CustomEvents } from 'services/types'; import { DEFAULT_FILTER } from 'services/supabase'; import { EntityWithCreateWorkflow } from 'types'; diff --git a/src/components/shared/Entity/hooks/useEntityWorkflowHelpers.ts b/src/components/shared/Entity/hooks/useEntityWorkflowHelpers.ts index e3c3e924b..0cdd1ebf6 100644 --- a/src/components/shared/Entity/hooks/useEntityWorkflowHelpers.ts +++ b/src/components/shared/Entity/hooks/useEntityWorkflowHelpers.ts @@ -15,7 +15,8 @@ import { useSnackbar } from 'notistack'; import { useCallback } from 'react'; import { useIntl } from 'react-intl'; import { useNavigate } from 'react-router-dom'; -import { CustomEvents, logRocketEvent } from 'services/logrocket'; +import { logRocketEvent } from 'services/shared'; +import { CustomEvents } from 'services/types'; import { useDetailsForm_resetState } from 'stores/DetailsForm/hooks'; import { useEndpointConfigStore_reset } from 'stores/EndpointConfig/hooks'; import { diff --git a/src/components/shared/ErrorBoundryWrapper.tsx b/src/components/shared/ErrorBoundryWrapper.tsx index a47dd87f0..999d75464 100644 --- a/src/components/shared/ErrorBoundryWrapper.tsx +++ b/src/components/shared/ErrorBoundryWrapper.tsx @@ -4,7 +4,8 @@ import { ReactNode, useState } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import { FormattedMessage, useIntl } from 'react-intl'; import { useMount } from 'react-use'; -import { CustomEvents, logRocketEvent } from 'services/logrocket'; +import { logRocketEvent } from 'services/shared'; +import { CustomEvents } from 'services/types'; import AlertBox from './AlertBox'; import MustReloadDialog from './MustReloadDialog'; diff --git a/src/directives/shared.ts b/src/directives/shared.ts index c0b7de260..151061a1b 100644 --- a/src/directives/shared.ts +++ b/src/directives/shared.ts @@ -1,5 +1,6 @@ import { isEmpty } from 'lodash'; -import { CustomEvents, logRocketEvent } from 'services/logrocket'; +import { logRocketEvent } from 'services/shared'; +import { CustomEvents } from 'services/types'; import { JOB_STATUS_COLUMNS, supabaseClient, TABLES } from 'services/supabase'; import { AppliedDirective } from 'types'; import { hasLength } from 'utils/misc-utils'; diff --git a/src/services/logrocket.ts b/src/services/logrocket.ts index 9a2a8c8bd..e6f978b61 100644 --- a/src/services/logrocket.ts +++ b/src/services/logrocket.ts @@ -21,28 +21,6 @@ interface Settings { type ParsedBody = any | undefined; -export enum CustomEvents { - CAPTURE_TEST = 'Capture_Test', - CAPTURE_CREATE = 'Capture_Create', - CAPTURE_CREATE_CONFIG_CREATE = 'Capture_Create_Config_Create', - CAPTURE_CREATE_CONFIG_EDIT = 'Capture_Create_Config_Edit', - CAPTURE_DISCOVER = 'Capture_Discover', - CAPTURE_EDIT = 'Capture_Edit', - CAPTURE_MATERIALIZE_FAILED = 'Capture_Materialize_Failed', - MATERIALIZATION_CREATE = 'Materialization_Create', - MATERIALIZATION_CREATE_CONFIG_CREATE = 'Materialization_Create_Config_Create', - MATERIALIZATION_CREATE_CONFIG_EDIT = 'Materialization_Create_Config_Edit', - MATERIALIZATION_TEST = 'Materialization_Test', - MATERIALIZATION_EDIT = 'Materialization_Edit', - COLLECTION_CREATE = 'Collection_Create', - DIRECTIVE = 'Directive', - ERROR_BOUNDARY_DISPLAYED = 'Error_Boundary_Displayed', - ERROR_BOUNDARY_PAYMENT_METHODS = 'Error_Boundary_Displayed:PaymentMethods', - FULL_PAGE_ERROR_DISPLAYED = 'Full_Page_Error_Displayed', - STRIPE_FORM_LOADING_FAILED = 'Stripe_Form_Loading_Failed', - SWR_LOADING_SLOW = 'SWR_Loading_Slow', -} - const logRocketSettings = getLogRocketSettings(); export const MISSING = '**MISSING**'; @@ -243,17 +221,6 @@ export const identifyUser = (user: User) => { } }; -export const logRocketEvent = ( - event: CustomEvents | string, - eventProperties?: any -) => { - // Just want to be very very safe - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (LogRocket?.track) { - LogRocket.track(event, eventProperties); - } -}; - export const logRocketConsole = (message: string, ...props: any[]) => { // Just want to be very very safe // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition diff --git a/src/services/shared.ts b/src/services/shared.ts new file mode 100644 index 000000000..35b9b2a71 --- /dev/null +++ b/src/services/shared.ts @@ -0,0 +1,13 @@ +import LogRocket from 'logrocket'; +import { CustomEvents } from './types'; + +export const logRocketEvent = ( + event: CustomEvents | string, + eventProperties?: any +) => { + // Just want to be very very safe + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (LogRocket?.track) { + LogRocket.track(event, eventProperties); + } +}; diff --git a/src/services/supabase.ts b/src/services/supabase.ts index f4f6900c2..c258910c5 100644 --- a/src/services/supabase.ts +++ b/src/services/supabase.ts @@ -5,6 +5,9 @@ import { forEach, isEmpty } from 'lodash'; import LogRocket from 'logrocket'; import { JobStatus, SortDirection } from 'types'; import { hasLength, incrementInterval, timeoutCleanUp } from 'utils/misc-utils'; +import pRetry, { AbortError } from 'p-retry'; +import { logRocketEvent } from './shared'; +import { CustomEvents } from './types'; if ( !process.env.REACT_APP_SUPABASE_URL || @@ -87,16 +90,7 @@ export const OAUTH_OPERATIONS = { export const supabaseClient = createClient( supabaseSettings.url, - supabaseSettings.anonKey, - { - // TODO (realtime) This is temporary until we figure out why some - // subscriptions just hang forever. - realtime: { - logger: (kind: string, msg: string, data?: any) => { - LogRocket.log('Realtime : ', kind, msg, data); - }, - }, - } + supabaseSettings.anonKey ); export interface SortingProps { @@ -221,6 +215,7 @@ export interface CallSupabaseResponse { } export const handleSuccess = (response: any) => { + console.log('handleSuccess', response); return response.error ? { data: null, @@ -232,23 +227,40 @@ export const handleSuccess = (response: any) => { }; export const handleFailure = (error: any) => { + console.log('handleFailure', error); return { data: null, error, }; }; +// Retry calls +const RETRY_REASONS = ['failed to fetch']; + +const shouldTryAfterFailure = (message?: string | null | undefined) => + RETRY_REASONS.some((el) => message?.toLowerCase().includes(el)); + +const callSupabaseWithRetry = (makeCall: () => any, action: string) => { + return pRetry(makeCall, { + retries: 2, + onFailedAttempt: (error) => { + logRocketEvent(CustomEvents.SUPABASE_CALL_FAILED, action); + + if (!shouldTryAfterFailure(error.message)) { + throw new AbortError(error.message); + } + }, + }).then(handleSuccess, handleFailure); +}; + export const insertSupabase = ( table: TABLES, data: any ): PromiseLike> => { - const query = supabaseClient.from(table); - - const makeCall = () => { - return query.insert([data]).then(handleSuccess, handleFailure); - }; - - return makeCall(); + return callSupabaseWithRetry( + () => supabaseClient.from(table).insert([data]).throwOnError(), + 'insert' + ); }; // Makes update calls. Mainly consumed in the src/api folder @@ -257,32 +269,26 @@ export const updateSupabase = ( data: any, matchData: any ): PromiseLike> => { - const query = supabaseClient.from(table); - - const makeCall = () => { - return query - .update(data) - .match(matchData) - .then(handleSuccess, handleFailure); - }; - - return makeCall(); + return callSupabaseWithRetry( + () => + supabaseClient + .from(table) + .update(data) + .match(matchData) + .throwOnError(), + 'update' + ); }; export const deleteSupabase = ( table: TABLES, matchData: any ): PromiseLike> => { - const query = supabaseClient.from(table); - - const makeCall = () => { - return query - .delete() - .match(matchData) - .then(handleSuccess, handleFailure); - }; - - return makeCall(); + return callSupabaseWithRetry( + () => + supabaseClient.from(table).delete().match(matchData).throwOnError(), + 'delete' + ); }; export const jobSucceeded = (jobStatus?: JobStatus) => { @@ -299,11 +305,6 @@ export const jobSucceeded = (jobStatus?: JobStatus) => { // START: Poller type PollerTimeout = number | undefined; - -const RETRY_POLLER_REASONS = ['failed to fetch']; -const shouldTryAfterFailure = (message?: string | null | undefined) => - RETRY_POLLER_REASONS.some((el) => message?.toLowerCase().includes(el)); - export const JOB_STATUS_POLLER_ERROR = 'supabase.poller.failed'; export const DEFAULT_POLLER_ERROR_TITLE_KEY = 'supabase.poller.failed.title'; export const DEFAULT_POLLER_ERROR_MESSAGE_KEY = @@ -402,62 +403,16 @@ export const jobStatusPoller = ( }; // END: Poller -// DO NOT USE WITHOUT TALKING TO SOMEONE -// TODO (Realtime) - fix the "hanging" isue where realtime does not always come back with a response -// We have found some weird issues with the RealTime stuff -// Eventually we want to go back to this but need to research the issues first. - -// export const endSubscription = (subscription: RealtimeSubscription) => { -// return supabaseClient -// .removeSubscription(subscription) -// .then(() => {}) -// .catch(() => {}); -// }; - -// export const startSubscription = ( -// query: SupabaseQueryBuilder, -// success: Function, -// failure: Function, -// keepSubscription?: boolean -// ) => { -// const subscription = query -// .on('*', async (payload: any) => { -// const response = payload.new -// ? payload.new -// : // TODO (typing) during manual testing I have seen record be in the response -// // even though the type says it is not there. Needs more research -// // eslint-disable-next-line @typescript-eslint/dot-notation -// payload['record'] -// ? // eslint-disable-next-line @typescript-eslint/dot-notation -// payload['record'] -// : null; - -// if (response) { -// if (response.job_status.type !== 'queued') { -// if (response.job_status.type === 'success') { -// success(response); -// } else { -// failure(response); -// } - -// if (!keepSubscription) { -// await endSubscription(subscription); -// } -// } -// } else { -// // TODO (error handling) Do not know how this path could happen but wanted to be safe -// failure(payload); -// } -// }) -// .subscribe(); - -// return subscription; -// }; -// DO NOT USE WITHOUT TALKING TO SOMEONE - // Invoke supabase edge functions. export function invokeSupabase(fn: FUNCTIONS, body: any) { return supabaseClient.functions.invoke(fn, { body: JSON.stringify(body), }); } +// return callSupabaseWithRetry( +// () => +// supabaseClient.functions.invoke(fn, { +// body: JSON.stringify(body), +// }), +// `fn:${fn}` +// ); diff --git a/src/services/types.ts b/src/services/types.ts new file mode 100644 index 000000000..bfaba44a1 --- /dev/null +++ b/src/services/types.ts @@ -0,0 +1,22 @@ +export enum CustomEvents { + CAPTURE_TEST = 'Capture_Test', + CAPTURE_CREATE = 'Capture_Create', + CAPTURE_CREATE_CONFIG_CREATE = 'Capture_Create_Config_Create', + CAPTURE_CREATE_CONFIG_EDIT = 'Capture_Create_Config_Edit', + CAPTURE_DISCOVER = 'Capture_Discover', + CAPTURE_EDIT = 'Capture_Edit', + CAPTURE_MATERIALIZE_FAILED = 'Capture_Materialize_Failed', + MATERIALIZATION_CREATE = 'Materialization_Create', + MATERIALIZATION_CREATE_CONFIG_CREATE = 'Materialization_Create_Config_Create', + MATERIALIZATION_CREATE_CONFIG_EDIT = 'Materialization_Create_Config_Edit', + MATERIALIZATION_TEST = 'Materialization_Test', + MATERIALIZATION_EDIT = 'Materialization_Edit', + COLLECTION_CREATE = 'Collection_Create', + DIRECTIVE = 'Directive', + ERROR_BOUNDARY_DISPLAYED = 'Error_Boundary_Displayed', + ERROR_BOUNDARY_PAYMENT_METHODS = 'Error_Boundary_Displayed:PaymentMethods', + FULL_PAGE_ERROR_DISPLAYED = 'Full_Page_Error_Displayed', + STRIPE_FORM_LOADING_FAILED = 'Stripe_Form_Loading_Failed', + SUPABASE_CALL_FAILED = 'Supabase_Call_Failed', + SWR_LOADING_SLOW = 'SWR_Loading_Slow', +} From 72e6786a21ab5a785691326820d08b2e93e5d15e Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 2 Nov 2023 18:16:18 -0400 Subject: [PATCH 02/29] More import fixes --- src/context/SWR.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/context/SWR.tsx b/src/context/SWR.tsx index 6e229d25e..91d69db6c 100644 --- a/src/context/SWR.tsx +++ b/src/context/SWR.tsx @@ -3,12 +3,10 @@ import { LRUCache } from 'lru-cache'; import { useSnackbar } from 'notistack'; import { useCallback } from 'react'; import { useIntl } from 'react-intl'; -import { - CustomEvents, - logRocketConsole, - logRocketEvent, -} from 'services/logrocket'; +import { logRocketConsole } from 'services/logrocket'; +import { logRocketEvent } from 'services/shared'; import { ERROR_MESSAGES } from 'services/supabase'; +import { CustomEvents } from 'services/types'; import { SWRConfig } from 'swr'; import { BaseComponentProps } from 'types'; From 19315f7aa94ae08eab7f237cfc679ff94e12bc44 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 2 Nov 2023 19:15:54 -0400 Subject: [PATCH 03/29] Adding a warning for when the network is down according to the browser No longer clearing the cache on tables --- src/context/NetworkWarning.tsx | 71 ++++++++++++++++++++++++++++++++++ src/context/index.tsx | 5 ++- src/lang/en-US.ts | 2 + src/services/supabase.ts | 2 - src/stores/Tables/Hydrator.tsx | 6 --- 5 files changed, 77 insertions(+), 9 deletions(-) create mode 100644 src/context/NetworkWarning.tsx diff --git a/src/context/NetworkWarning.tsx b/src/context/NetworkWarning.tsx new file mode 100644 index 000000000..62b2a7d16 --- /dev/null +++ b/src/context/NetworkWarning.tsx @@ -0,0 +1,71 @@ +import { debounce } from 'lodash'; +import { SnackbarKey, useSnackbar } from 'notistack'; +import { useEffect, useRef } from 'react'; +import { useIntl } from 'react-intl'; +import { useNetworkState } from 'react-use'; +import { BaseComponentProps } from 'types'; +import { snackbarSettings } from 'utils/notification-utils'; + +// We give the browser some wiggle room before showing a message so quick disturbances +// do not get displayed +const WAIT_TIME = 2000; + +function NetworkWarning({ children }: BaseComponentProps) { + const intl = useIntl(); + const { enqueueSnackbar, closeSnackbar } = useSnackbar(); + const { previous, online } = useNetworkState(); + + const networkDownSnackbar = useRef(null); + + const debouncedNotification = useRef( + debounce((old: boolean | undefined, curr: boolean | undefined) => { + // This is mainly for the "on load" of the page and we can ignore everything the first time + if (old === undefined) { + return; + } + + if (!curr) { + networkDownSnackbar.current = enqueueSnackbar( + intl.formatMessage({ + id: 'notifications.networkState.down', + }), + { + ...snackbarSettings, + variant: 'warning', + } + ); + } + + // If we previously showed them the network is down then go ahead and show them it came back up + // Otherwise, it went down and came back up before we could notify them so we don't need to show the good news + if (networkDownSnackbar.current && curr) { + // Close any previous messages as reset the ref + if (networkDownSnackbar.current) { + closeSnackbar(networkDownSnackbar.current); + networkDownSnackbar.current = null; + } + + enqueueSnackbar( + intl.formatMessage({ + id: 'notifications.networkState.restored', + }), + { + ...snackbarSettings, + variant: 'success', + } + ); + } + }, WAIT_TIME) + ); + + useEffect(() => { + debouncedNotification.current(previous, online); + }, [online, previous]); + + return ( + // eslint-disable-next-line react/jsx-no-useless-fragment + <>{children} + ); +} + +export default NetworkWarning; diff --git a/src/context/index.tsx b/src/context/index.tsx index fb4cd093e..fe228382c 100644 --- a/src/context/index.tsx +++ b/src/context/index.tsx @@ -4,6 +4,7 @@ import SwrConfigProvider from 'context/SWR'; import { BaseComponentProps } from 'types'; import ClientProvider from './Client'; import ContentProvider from './Content'; +import NetworkWarning from './NetworkWarning'; import ThemeProvider from './Theme'; import { UserProvider } from './User'; @@ -15,7 +16,9 @@ const AppProviders = ({ children }: BaseComponentProps) => { - {children} + + {children} + diff --git a/src/lang/en-US.ts b/src/lang/en-US.ts index 077ef258f..b509e1d0c 100644 --- a/src/lang/en-US.ts +++ b/src/lang/en-US.ts @@ -1292,6 +1292,8 @@ const Notifications: ResolvedIntlConfig['messages'] = { 'notifications.paymentMethods.missing.trialEndsToday.instructions': `Please {cta} today to continue using Estuary Flow.`, 'notifications.paymentMethods.missing.trialPast': `{tenant} is past its free trial without a payment method.`, 'notifications.paymentMethods.missing.trialPast.instructions': `Please {cta} to continue using Estuary Flow.`, + 'notifications.networkState.down': `Please check your network connection.`, + 'notifications.networkState.restored': `Network connection restored`, }; const enUSMessages: ResolvedIntlConfig['messages'] = { diff --git a/src/services/supabase.ts b/src/services/supabase.ts index c258910c5..56f77add0 100644 --- a/src/services/supabase.ts +++ b/src/services/supabase.ts @@ -215,7 +215,6 @@ export interface CallSupabaseResponse { } export const handleSuccess = (response: any) => { - console.log('handleSuccess', response); return response.error ? { data: null, @@ -227,7 +226,6 @@ export const handleSuccess = (response: any) => { }; export const handleFailure = (error: any) => { - console.log('handleFailure', error); return { data: null, error, diff --git a/src/stores/Tables/Hydrator.tsx b/src/stores/Tables/Hydrator.tsx index 173c7ec46..ade41b4a0 100644 --- a/src/stores/Tables/Hydrator.tsx +++ b/src/stores/Tables/Hydrator.tsx @@ -32,11 +32,6 @@ export const TableHydrator = ({ SelectableTableStore['hydrate'] >(selectableTableStoreName, selectableTableStoreSelectors.query.hydrate); - const resetState = useZustandStore< - SelectableTableStore, - SelectableTableStore['resetState'] - >(selectableTableStoreName, selectableTableStoreSelectors.state.reset); - const setDisableMultiSelect = useZustandStore< SelectableTableStore, SelectableTableStore['setDisableMultiSelect'] @@ -57,7 +52,6 @@ export const TableHydrator = ({ // Reset state when leaving until we work out how we want to cache table stuff useUnmount(() => { setDisableMultiSelect(false); - resetState(); }); // eslint-disable-next-line react/jsx-no-useless-fragment From 54b8176af202b6a1832a75542f94c1a146784a71 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 2 Nov 2023 19:53:41 -0400 Subject: [PATCH 04/29] Updating to latest notistack Using more custom notification styling for network --- package-lock.json | 47 +++++++++++++++++++--------------- package.json | 2 +- src/context/NetworkWarning.tsx | 37 ++++++++++++++++++++------ src/context/Notifications.tsx | 2 +- src/lang/en-US.ts | 2 +- 5 files changed, 59 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index 319c2481b..e0c76f74a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,7 +48,7 @@ "madge": "^5.0.2", "material-ui-popup-state": "^5.0.9", "monaco-editor": "^0.34.1", - "notistack": "^2.0.8", + "notistack": "^3.0.1", "p-limit": "^4.0.0", "p-retry": "^6.1.0", "path-list-to-tree": "^1.1.1", @@ -13570,6 +13570,14 @@ "node": ">=0.6.0" } }, + "node_modules/goober": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.13.tgz", + "integrity": "sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -20951,31 +20959,24 @@ } }, "node_modules/notistack": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/notistack/-/notistack-2.0.8.tgz", - "integrity": "sha512-/IY14wkFp5qjPgKNvAdfL5Jp6q90+MjgKTPh4c81r/lW70KeuX6b9pE/4f8L4FG31cNudbN9siiFS5ql1aSLRw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/notistack/-/notistack-3.0.1.tgz", + "integrity": "sha512-ntVZXXgSQH5WYfyU+3HfcXuKaapzAJ8fBLQ/G618rn3yvSzEbnOB8ZSOwhX+dAORy/lw+GC2N061JA0+gYWTVA==", "dependencies": { "clsx": "^1.1.0", - "hoist-non-react-statics": "^3.3.0" + "goober": "^2.0.33" + }, + "engines": { + "node": ">=12.0.0", + "npm": ">=6.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/notistack" }, "peerDependencies": { - "@emotion/react": "^11.4.1", - "@emotion/styled": "^11.3.0", - "@mui/material": "^5.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } } }, "node_modules/npm-bundled": { @@ -40273,6 +40274,12 @@ "minimist": "^1.2.5" } }, + "goober": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.13.tgz", + "integrity": "sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==", + "requires": {} + }, "gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -45825,12 +45832,12 @@ "dev": true }, "notistack": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/notistack/-/notistack-2.0.8.tgz", - "integrity": "sha512-/IY14wkFp5qjPgKNvAdfL5Jp6q90+MjgKTPh4c81r/lW70KeuX6b9pE/4f8L4FG31cNudbN9siiFS5ql1aSLRw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/notistack/-/notistack-3.0.1.tgz", + "integrity": "sha512-ntVZXXgSQH5WYfyU+3HfcXuKaapzAJ8fBLQ/G618rn3yvSzEbnOB8ZSOwhX+dAORy/lw+GC2N061JA0+gYWTVA==", "requires": { "clsx": "^1.1.0", - "hoist-non-react-statics": "^3.3.0" + "goober": "^2.0.33" } }, "npm-bundled": { diff --git a/package.json b/package.json index c71b53a1b..4f07a5e1f 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "madge": "^5.0.2", "material-ui-popup-state": "^5.0.9", "monaco-editor": "^0.34.1", - "notistack": "^2.0.8", + "notistack": "^3.0.1", "p-limit": "^4.0.0", "p-retry": "^6.1.0", "path-list-to-tree": "^1.1.1", diff --git a/src/context/NetworkWarning.tsx b/src/context/NetworkWarning.tsx index 62b2a7d16..7e1f014ee 100644 --- a/src/context/NetworkWarning.tsx +++ b/src/context/NetworkWarning.tsx @@ -1,7 +1,9 @@ +import { Stack, Typography } from '@mui/material'; +import { Wifi, WifiOff } from 'iconoir-react'; import { debounce } from 'lodash'; import { SnackbarKey, useSnackbar } from 'notistack'; import { useEffect, useRef } from 'react'; -import { useIntl } from 'react-intl'; +import { FormattedMessage } from 'react-intl'; import { useNetworkState } from 'react-use'; import { BaseComponentProps } from 'types'; import { snackbarSettings } from 'utils/notification-utils'; @@ -11,7 +13,6 @@ import { snackbarSettings } from 'utils/notification-utils'; const WAIT_TIME = 2000; function NetworkWarning({ children }: BaseComponentProps) { - const intl = useIntl(); const { enqueueSnackbar, closeSnackbar } = useSnackbar(); const { previous, online } = useNetworkState(); @@ -26,11 +27,21 @@ function NetworkWarning({ children }: BaseComponentProps) { if (!curr) { networkDownSnackbar.current = enqueueSnackbar( - intl.formatMessage({ - id: 'notifications.networkState.down', - }), + + + + + + + + , { ...snackbarSettings, + hideIconVariant: true, variant: 'warning', } ); @@ -46,11 +57,21 @@ function NetworkWarning({ children }: BaseComponentProps) { } enqueueSnackbar( - intl.formatMessage({ - id: 'notifications.networkState.restored', - }), + + + + + + + + , { ...snackbarSettings, + hideIconVariant: true, variant: 'success', } ); diff --git a/src/context/Notifications.tsx b/src/context/Notifications.tsx index f7ec40be4..aea20c9cb 100644 --- a/src/context/Notifications.tsx +++ b/src/context/Notifications.tsx @@ -2,7 +2,7 @@ import { SnackbarProvider } from 'notistack'; import { BaseComponentProps } from 'types'; const NotificationProvider = ({ children }: BaseComponentProps) => { - return {children}; + return {children}; }; export default NotificationProvider; diff --git a/src/lang/en-US.ts b/src/lang/en-US.ts index b509e1d0c..3a2feca10 100644 --- a/src/lang/en-US.ts +++ b/src/lang/en-US.ts @@ -1292,7 +1292,7 @@ const Notifications: ResolvedIntlConfig['messages'] = { 'notifications.paymentMethods.missing.trialEndsToday.instructions': `Please {cta} today to continue using Estuary Flow.`, 'notifications.paymentMethods.missing.trialPast': `{tenant} is past its free trial without a payment method.`, 'notifications.paymentMethods.missing.trialPast.instructions': `Please {cta} to continue using Estuary Flow.`, - 'notifications.networkState.down': `Please check your network connection.`, + 'notifications.networkState.down': `Network connection may be down`, 'notifications.networkState.restored': `Network connection restored`, }; From c7b8c9c6fbdafb2994a22c1c6fc3c1aacf6d561d Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Fri, 3 Nov 2023 11:11:53 -0400 Subject: [PATCH 05/29] Being safe --- src/context/NetworkWarning.tsx | 6 ++++++ src/services/types.ts | 1 + 2 files changed, 7 insertions(+) diff --git a/src/context/NetworkWarning.tsx b/src/context/NetworkWarning.tsx index 7e1f014ee..788015460 100644 --- a/src/context/NetworkWarning.tsx +++ b/src/context/NetworkWarning.tsx @@ -5,6 +5,8 @@ import { SnackbarKey, useSnackbar } from 'notistack'; import { useEffect, useRef } from 'react'; import { FormattedMessage } from 'react-intl'; import { useNetworkState } from 'react-use'; +import { logRocketEvent } from 'services/shared'; +import { CustomEvents } from 'services/types'; import { BaseComponentProps } from 'types'; import { snackbarSettings } from 'utils/notification-utils'; @@ -26,6 +28,10 @@ function NetworkWarning({ children }: BaseComponentProps) { } if (!curr) { + // There is a solid chance this will not actually make it to LR... but want this here + // just in case we start showing this to folks that are not having network issues. + logRocketEvent(CustomEvents.NOTIFICATION_NETWORK_WARNING); + networkDownSnackbar.current = enqueueSnackbar( Date: Fri, 3 Nov 2023 11:22:11 -0400 Subject: [PATCH 06/29] Reverting the table caching display --- src/context/NetworkWarning.tsx | 98 ---------------------------------- src/context/index.tsx | 5 +- src/lang/en-US.ts | 2 - src/stores/Tables/Hydrator.tsx | 8 +++ 4 files changed, 9 insertions(+), 104 deletions(-) delete mode 100644 src/context/NetworkWarning.tsx diff --git a/src/context/NetworkWarning.tsx b/src/context/NetworkWarning.tsx deleted file mode 100644 index 788015460..000000000 --- a/src/context/NetworkWarning.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { Stack, Typography } from '@mui/material'; -import { Wifi, WifiOff } from 'iconoir-react'; -import { debounce } from 'lodash'; -import { SnackbarKey, useSnackbar } from 'notistack'; -import { useEffect, useRef } from 'react'; -import { FormattedMessage } from 'react-intl'; -import { useNetworkState } from 'react-use'; -import { logRocketEvent } from 'services/shared'; -import { CustomEvents } from 'services/types'; -import { BaseComponentProps } from 'types'; -import { snackbarSettings } from 'utils/notification-utils'; - -// We give the browser some wiggle room before showing a message so quick disturbances -// do not get displayed -const WAIT_TIME = 2000; - -function NetworkWarning({ children }: BaseComponentProps) { - const { enqueueSnackbar, closeSnackbar } = useSnackbar(); - const { previous, online } = useNetworkState(); - - const networkDownSnackbar = useRef(null); - - const debouncedNotification = useRef( - debounce((old: boolean | undefined, curr: boolean | undefined) => { - // This is mainly for the "on load" of the page and we can ignore everything the first time - if (old === undefined) { - return; - } - - if (!curr) { - // There is a solid chance this will not actually make it to LR... but want this here - // just in case we start showing this to folks that are not having network issues. - logRocketEvent(CustomEvents.NOTIFICATION_NETWORK_WARNING); - - networkDownSnackbar.current = enqueueSnackbar( - - - - - - - - , - { - ...snackbarSettings, - hideIconVariant: true, - variant: 'warning', - } - ); - } - - // If we previously showed them the network is down then go ahead and show them it came back up - // Otherwise, it went down and came back up before we could notify them so we don't need to show the good news - if (networkDownSnackbar.current && curr) { - // Close any previous messages as reset the ref - if (networkDownSnackbar.current) { - closeSnackbar(networkDownSnackbar.current); - networkDownSnackbar.current = null; - } - - enqueueSnackbar( - - - - - - - - , - { - ...snackbarSettings, - hideIconVariant: true, - variant: 'success', - } - ); - } - }, WAIT_TIME) - ); - - useEffect(() => { - debouncedNotification.current(previous, online); - }, [online, previous]); - - return ( - // eslint-disable-next-line react/jsx-no-useless-fragment - <>{children} - ); -} - -export default NetworkWarning; diff --git a/src/context/index.tsx b/src/context/index.tsx index fe228382c..fb4cd093e 100644 --- a/src/context/index.tsx +++ b/src/context/index.tsx @@ -4,7 +4,6 @@ import SwrConfigProvider from 'context/SWR'; import { BaseComponentProps } from 'types'; import ClientProvider from './Client'; import ContentProvider from './Content'; -import NetworkWarning from './NetworkWarning'; import ThemeProvider from './Theme'; import { UserProvider } from './User'; @@ -16,9 +15,7 @@ const AppProviders = ({ children }: BaseComponentProps) => { - - {children} - + {children} diff --git a/src/lang/en-US.ts b/src/lang/en-US.ts index 3a2feca10..077ef258f 100644 --- a/src/lang/en-US.ts +++ b/src/lang/en-US.ts @@ -1292,8 +1292,6 @@ const Notifications: ResolvedIntlConfig['messages'] = { 'notifications.paymentMethods.missing.trialEndsToday.instructions': `Please {cta} today to continue using Estuary Flow.`, 'notifications.paymentMethods.missing.trialPast': `{tenant} is past its free trial without a payment method.`, 'notifications.paymentMethods.missing.trialPast.instructions': `Please {cta} to continue using Estuary Flow.`, - 'notifications.networkState.down': `Network connection may be down`, - 'notifications.networkState.restored': `Network connection restored`, }; const enUSMessages: ResolvedIntlConfig['messages'] = { diff --git a/src/stores/Tables/Hydrator.tsx b/src/stores/Tables/Hydrator.tsx index ade41b4a0..16ec85925 100644 --- a/src/stores/Tables/Hydrator.tsx +++ b/src/stores/Tables/Hydrator.tsx @@ -32,6 +32,11 @@ export const TableHydrator = ({ SelectableTableStore['hydrate'] >(selectableTableStoreName, selectableTableStoreSelectors.query.hydrate); + const resetState = useZustandStore< + SelectableTableStore, + SelectableTableStore['resetState'] + >(selectableTableStoreName, selectableTableStoreSelectors.state.reset); + const setDisableMultiSelect = useZustandStore< SelectableTableStore, SelectableTableStore['setDisableMultiSelect'] @@ -52,6 +57,9 @@ export const TableHydrator = ({ // Reset state when leaving until we work out how we want to cache table stuff useUnmount(() => { setDisableMultiSelect(false); + + // TODO (https://github.com/estuary/ui/issues/815) + resetState(); }); // eslint-disable-next-line react/jsx-no-useless-fragment From 80879475f6688487026df7e15928dc16ac72280f Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Fri, 3 Nov 2023 14:46:25 -0400 Subject: [PATCH 07/29] Making errors display with more insight for users --- .../admin/Billing/PaymentMethods.tsx | 2 +- src/components/shared/Error/Message.tsx | 30 +++++---- src/lang/en-US.ts | 14 +++- src/services/shared.ts | 6 ++ src/services/supabase.ts | 67 +++++++++++-------- src/types/index.ts | 7 ++ src/utils/sops-utils.ts | 8 +-- 7 files changed, 81 insertions(+), 53 deletions(-) diff --git a/src/components/admin/Billing/PaymentMethods.tsx b/src/components/admin/Billing/PaymentMethods.tsx index b86d8190a..b2f2c93af 100644 --- a/src/components/admin/Billing/PaymentMethods.tsx +++ b/src/components/admin/Billing/PaymentMethods.tsx @@ -91,7 +91,7 @@ const PaymentMethods = ({ showAddPayment }: AdminBillingProps) => { selectedTenant ); - if (setupResponse.data.intent_secret) { + if (setupResponse.data?.intent_secret) { setSetupIntentSecret(setupResponse.data.intent_secret); } else { setSetupIntentSecret(INTENT_SECRET_ERROR); diff --git a/src/components/shared/Error/Message.tsx b/src/components/shared/Error/Message.tsx index 4beceaa8d..eb15cc92a 100644 --- a/src/components/shared/Error/Message.tsx +++ b/src/components/shared/Error/Message.tsx @@ -1,7 +1,8 @@ import { Box, Stack, Typography } from '@mui/material'; -import MessageWithLink from 'components/content/MessageWithLink'; import { FormattedMessage, useIntl } from 'react-intl'; import { logRocketConsole } from 'services/logrocket'; +import { retryAfterFailure } from 'services/shared'; +import Instructions from './Instructions'; import { ErrorDetails } from './types'; interface Props { @@ -13,21 +14,24 @@ function Message({ error }: Props) { const intl = useIntl(); - // Check if the message object is coming from the server - // Easiest check I could think of. Our shortest keys are - // `cta.foo` which can look a lot like how Supabase returns - // errors when fetching wrong keys from a table (`tableName.col`) - // so we can not use a RegEx like originally planned. - // Code seems to almost always come back. - const messageFromServer = - typeof error === 'object' && error.hasOwnProperty('code'); + const failedAfterRetry = retryAfterFailure(error?.message ?? error); + + // Check if we have retried making the call or if we think the message object + // is from Supabase + // Initially was going to write a RegEx that detects if the mesaage + // needs translated. However, Our shortest keys (cta.foo) + // can also look a lot like how Supabase returns + // errors when fetching wrong keys from a table (tableName.col). + const displayOnlyError = + (typeof error === 'object' && error.hasOwnProperty('code')) || + failedAfterRetry; // We do not need to translate messages from Supabase as they comeback readable - const message = messageFromServer + const message = displayOnlyError ? error.message : intl.formatMessage({ id: error.message }); - if (!messageFromServer) { + if (!displayOnlyError) { return ( {message} @@ -37,9 +41,7 @@ function Message({ error }: Props) { return ( - - - + diff --git a/src/lang/en-US.ts b/src/lang/en-US.ts index 077ef258f..89a85e79c 100644 --- a/src/lang/en-US.ts +++ b/src/lang/en-US.ts @@ -98,6 +98,8 @@ const CommonMessages: ResolvedIntlConfig['messages'] = { 'filter.time.thisMonth': `This Month`, 'catalogName.limitations': `letters, numbers, periods, underscores, and hyphens`, + + 'support.email': `mailto:support@estuary.dev`, }; const CTAs: ResolvedIntlConfig['messages'] = { @@ -141,6 +143,7 @@ const CTAs: ResolvedIntlConfig['messages'] = { 'cta.showAll': `Show All`, 'cta.reload': `Reload`, 'cta.evolve': `Apply`, + 'cta.support': `contact support`, }; const Data: ResolvedIntlConfig['messages'] = { @@ -167,9 +170,14 @@ const Data: ResolvedIntlConfig['messages'] = { const Error: ResolvedIntlConfig['messages'] = { 'error.title': `Error`, - 'error.message': `This is not something you did wrong. There was a technical issue. Please {docLink}.`, - 'error.message.docLink': `contact support`, - 'error.message.docPath': `mailto:support@estuary.dev`, + 'error.reason': `The server returned an error. Please review the error and try again.`, + 'error.reason.retry': `This is not something you did wrong. There was a technical issue.`, + 'error.reason.fetchFailed': `There was a network issue while contacting our servers. Please make sure your network is available and try again.`, + + 'error.instructions': `If the issue persists please {docLink}.`, + 'error.instructions.docLink': `${CTAs['cta.support']}`, + 'error.instructions.docPath': `${CommonMessages['support.email']}`, + 'error.codeLabel': `Code:`, 'error.messageLabel': `Message:`, 'error.detailsLabel': `Details:`, diff --git a/src/services/shared.ts b/src/services/shared.ts index 35b9b2a71..907624750 100644 --- a/src/services/shared.ts +++ b/src/services/shared.ts @@ -11,3 +11,9 @@ export const logRocketEvent = ( LogRocket.track(event, eventProperties); } }; + +export const FAILED_TO_FETCH = 'failed to fetch'; +export const RETRY_REASONS = [FAILED_TO_FETCH]; + +export const retryAfterFailure = (message?: string | null | undefined) => + RETRY_REASONS.some((el) => message?.toLowerCase().includes(el)); diff --git a/src/services/supabase.ts b/src/services/supabase.ts index 56f77add0..087c3212a 100644 --- a/src/services/supabase.ts +++ b/src/services/supabase.ts @@ -6,7 +6,7 @@ import LogRocket from 'logrocket'; import { JobStatus, SortDirection } from 'types'; import { hasLength, incrementInterval, timeoutCleanUp } from 'utils/misc-utils'; import pRetry, { AbortError } from 'p-retry'; -import { logRocketEvent } from './shared'; +import { logRocketEvent, retryAfterFailure } from './shared'; import { CustomEvents } from './types'; if ( @@ -49,7 +49,7 @@ export enum TABLES { DRAFT_ERRORS = 'draft_errors', DRAFT_SPECS = 'draft_specs', DRAFT_SPECS_EXT = 'draft_specs_ext', - DRAFTS = 'drafts', + DRAFTS = 'drafts1', DRAFTS_EXT = 'drafts_ext', EVOLUTIONS = 'evolutions', INFERRED_SCHEMAS = 'inferred_schemas', @@ -226,6 +226,7 @@ export const handleSuccess = (response: any) => { }; export const handleFailure = (error: any) => { + console.log('handleFailue', error); return { data: null, error, @@ -233,24 +234,48 @@ export const handleFailure = (error: any) => { }; // Retry calls -const RETRY_REASONS = ['failed to fetch']; +const RETRY_ATTEMPTS = 2; -const shouldTryAfterFailure = (message?: string | null | undefined) => - RETRY_REASONS.some((el) => message?.toLowerCase().includes(el)); +const onFailedAttempt = (error: Error, action: string) => { + logRocketEvent(CustomEvents.SUPABASE_CALL_FAILED, action); -const callSupabaseWithRetry = (makeCall: () => any, action: string) => { + if (!retryAfterFailure(error.message)) { + throw new AbortError({ + ...error, + code: 'stopped_retry', + } as Error); + } +}; + +const callSupabaseWithRetry = (makeCall: () => any, action: string) => { return pRetry(makeCall, { - retries: 2, + retries: RETRY_ATTEMPTS, onFailedAttempt: (error) => { - logRocketEvent(CustomEvents.SUPABASE_CALL_FAILED, action); - - if (!shouldTryAfterFailure(error.message)) { - throw new AbortError(error.message); - } + onFailedAttempt(error, action); }, - }).then(handleSuccess, handleFailure); + }).then(handleSuccess, handleFailure); }; +// Invoke supabase edge functions. Does not use the he +export function invokeSupabase(fn: FUNCTIONS, body: any) { + return supabaseClient.functions.invoke(fn, { + body: JSON.stringify(body), + }); + + // return pRetry( + // () => + // supabaseClient.functions.invoke(fn, { + // body: JSON.stringify(body), + // }), + // { + // retries: RETRY_ATTEMPTS, + // onFailedAttempt: (error) => { + // onFailedAttempt(error, `fn:${fn}`); + // }, + // } + // ).then(handleSuccess, handleFailure); +} + export const insertSupabase = ( table: TABLES, data: any @@ -374,7 +399,7 @@ export const jobStatusPoller = ( if ( attempts === 0 && typeof error?.message === 'string' && - shouldTryAfterFailure(error.message) + retryAfterFailure(error.message) ) { LogRocket.log('Poller : error : trying again'); attempts += 1; @@ -400,17 +425,3 @@ export const jobStatusPoller = ( ); }; // END: Poller - -// Invoke supabase edge functions. -export function invokeSupabase(fn: FUNCTIONS, body: any) { - return supabaseClient.functions.invoke(fn, { - body: JSON.stringify(body), - }); -} -// return callSupabaseWithRetry( -// () => -// supabaseClient.functions.invoke(fn, { -// body: JSON.stringify(body), -// }), -// `fn:${fn}` -// ); diff --git a/src/types/index.ts b/src/types/index.ts index a7f769671..69a5ae06d 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -13,6 +13,13 @@ export enum MessagePrefixes { MATERIALIZATION_EDIT = 'materializationEdit', } +// TODO (typing): Consider adding a type annotation for the promise returned by +// the invokeSupabase() function (i.e., src/services/supabase.ts). +export type SupabaseInvokeResponse = + | { data: null; error: Error } + | { data: null; error: any } + | { data: any; error: null }; + // TODO (typing): The type annotation for the data property of the JsonFormsData object // mirrors the Schema interface. Consider using the Schema interface to type this property. export interface JsonFormsData extends Pick { diff --git a/src/utils/sops-utils.ts b/src/utils/sops-utils.ts index a3d3be10c..88bb5434a 100644 --- a/src/utils/sops-utils.ts +++ b/src/utils/sops-utils.ts @@ -1,13 +1,7 @@ import { encryptConfig } from 'api/oauth'; import { isPlainObject } from 'lodash'; import { createJSONFormDefaults } from 'services/ajv'; -import { JsonFormsData, Schema } from 'types'; - -// TODO (typing): Consider adding a type annotation for the promise returned by -// the invokeSupabase() function (i.e., src/services/supabase.ts). -type SupabaseInvokeResponse = - | { data: null; error: Error } - | { data: any; error: null }; +import { JsonFormsData, Schema, SupabaseInvokeResponse } from 'types'; const sopsKey = 'sops'; From cb4c620f8bcbe10a70528feb6e0695c9ae2b2618 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Fri, 3 Nov 2023 14:46:38 -0400 Subject: [PATCH 08/29] Making errors display with more insight for users : new file --- src/components/shared/Error/Instructions.tsx | 28 ++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/components/shared/Error/Instructions.tsx diff --git a/src/components/shared/Error/Instructions.tsx b/src/components/shared/Error/Instructions.tsx new file mode 100644 index 000000000..989222d3e --- /dev/null +++ b/src/components/shared/Error/Instructions.tsx @@ -0,0 +1,28 @@ +import { Stack, Typography } from '@mui/material'; +import MessageWithLink from 'components/content/MessageWithLink'; +import { FormattedMessage } from 'react-intl'; +import { FAILED_TO_FETCH, retryAfterFailure } from 'services/shared'; + +interface Props { + message: string; +} + +function Instructions({ message }: Props) { + const messageID = + message.toLowerCase() === FAILED_TO_FETCH + ? 'error.reason.fetchFailed' + : retryAfterFailure(message) + ? 'error.reason.retry' + : 'error.reason'; + + return ( + + + + + + + ); +} + +export default Instructions; From 3f34ce737c988e67be7d01bf1afee75511acec22 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Tue, 7 Nov 2023 11:35:25 -0500 Subject: [PATCH 09/29] Switching over to the retry library --- package-lock.json | 59 ++++-------------------- package.json | 3 +- src/services/supabase.ts | 98 ++++++++++++++++++++-------------------- 3 files changed, 61 insertions(+), 99 deletions(-) diff --git a/package-lock.json b/package-lock.json index e0c76f74a..dc62f3053 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,7 +50,6 @@ "monaco-editor": "^0.34.1", "notistack": "^3.0.1", "p-limit": "^4.0.0", - "p-retry": "^6.1.0", "path-list-to-tree": "^1.1.1", "pretty-bytes": "^6.1.0", "react": "^17.0.2", @@ -70,6 +69,7 @@ "react-virtualized-auto-sizer": "^1.0.20", "react-window": "^1.8.9", "readable-numbers": "^1.0.7", + "retry": "^0.13.1", "safe-stable-stringify": "^2.4.3", "serve": "^14.2.0", "stripe": "^12.1.1", @@ -92,6 +92,7 @@ "@types/react-gtm-module": "^2.0.1", "@types/react-lazylog": "^4.5.1", "@types/react-window": "^1.8.5", + "@types/retry": "^0.12.5", "compress-create-react-app": "^1.4.1", "eslint-config-kentcdodds": "^20.3.1", "eslint-plugin-jest-dom": "^4.0.3", @@ -6744,9 +6745,10 @@ } }, "node_modules/@types/retry": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", - "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==" + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.5.tgz", + "integrity": "sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw==", + "dev": true }, "node_modules/@types/scheduler": { "version": "0.16.2", @@ -14577,17 +14579,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-network-error": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.0.0.tgz", - "integrity": "sha512-P3fxi10Aji2FZmHTrMPSNFbNC6nnp4U5juPAIjXPHkUNubi4+qK7vvdsaNpAUwXslhYm9oyjEYTxs1xd/+Ph0w==", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -21625,22 +21616,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-retry": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.1.0.tgz", - "integrity": "sha512-fJLEQ2KqYBJRuaA/8cKMnqhulqNM+bpcjYtXNex2t3mOXKRYPitAJt9NacSf8XAFzcYahSAbKpobiWDSqHSh2g==", - "dependencies": { - "@types/retry": "0.12.2", - "is-network-error": "^1.0.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -35158,9 +35133,10 @@ } }, "@types/retry": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", - "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==" + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.5.tgz", + "integrity": "sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw==", + "dev": true }, "@types/scheduler": { "version": "0.16.2", @@ -41009,11 +40985,6 @@ "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true }, - "is-network-error": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.0.0.tgz", - "integrity": "sha512-P3fxi10Aji2FZmHTrMPSNFbNC6nnp4U5juPAIjXPHkUNubi4+qK7vvdsaNpAUwXslhYm9oyjEYTxs1xd/+Ph0w==" - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -46308,16 +46279,6 @@ "aggregate-error": "^3.0.0" } }, - "p-retry": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.1.0.tgz", - "integrity": "sha512-fJLEQ2KqYBJRuaA/8cKMnqhulqNM+bpcjYtXNex2t3mOXKRYPitAJt9NacSf8XAFzcYahSAbKpobiWDSqHSh2g==", - "requires": { - "@types/retry": "0.12.2", - "is-network-error": "^1.0.0", - "retry": "^0.13.1" - } - }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", diff --git a/package.json b/package.json index 4f07a5e1f..83422a4c1 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,6 @@ "monaco-editor": "^0.34.1", "notistack": "^3.0.1", "p-limit": "^4.0.0", - "p-retry": "^6.1.0", "path-list-to-tree": "^1.1.1", "pretty-bytes": "^6.1.0", "react": "^17.0.2", @@ -90,6 +89,7 @@ "react-virtualized-auto-sizer": "^1.0.20", "react-window": "^1.8.9", "readable-numbers": "^1.0.7", + "retry": "^0.13.1", "safe-stable-stringify": "^2.4.3", "serve": "^14.2.0", "stripe": "^12.1.1", @@ -112,6 +112,7 @@ "@types/react-gtm-module": "^2.0.1", "@types/react-lazylog": "^4.5.1", "@types/react-window": "^1.8.5", + "@types/retry": "^0.12.5", "compress-create-react-app": "^1.4.1", "eslint-config-kentcdodds": "^20.3.1", "eslint-plugin-jest-dom": "^4.0.3", diff --git a/src/services/supabase.ts b/src/services/supabase.ts index 087c3212a..bb621e7e5 100644 --- a/src/services/supabase.ts +++ b/src/services/supabase.ts @@ -5,7 +5,7 @@ import { forEach, isEmpty } from 'lodash'; import LogRocket from 'logrocket'; import { JobStatus, SortDirection } from 'types'; import { hasLength, incrementInterval, timeoutCleanUp } from 'utils/misc-utils'; -import pRetry, { AbortError } from 'p-retry'; +import retry from 'retry'; import { logRocketEvent, retryAfterFailure } from './shared'; import { CustomEvents } from './types'; @@ -49,7 +49,7 @@ export enum TABLES { DRAFT_ERRORS = 'draft_errors', DRAFT_SPECS = 'draft_specs', DRAFT_SPECS_EXT = 'draft_specs_ext', - DRAFTS = 'drafts1', + DRAFTS = 'drafts', DRAFTS_EXT = 'drafts_ext', EVOLUTIONS = 'evolutions', INFERRED_SCHEMAS = 'inferred_schemas', @@ -236,54 +236,60 @@ export const handleFailure = (error: any) => { // Retry calls const RETRY_ATTEMPTS = 2; -const onFailedAttempt = (error: Error, action: string) => { - logRocketEvent(CustomEvents.SUPABASE_CALL_FAILED, action); +const supabaseRetry = (makeCall: Function, action: string) => { + const operation = retry.operation({ + retries: RETRY_ATTEMPTS, + randomize: true, + }); - if (!retryAfterFailure(error.message)) { - throw new AbortError({ - ...error, - code: 'stopped_retry', - } as Error); - } -}; + return new Promise((resolve) => { + operation.attempt(async () => { + const response = await makeCall(); -const callSupabaseWithRetry = (makeCall: () => any, action: string) => { - return pRetry(makeCall, { - retries: RETRY_ATTEMPTS, - onFailedAttempt: (error) => { - onFailedAttempt(error, action); - }, - }).then(handleSuccess, handleFailure); + const error = response.error; + const shouldRetry = retryAfterFailure(error?.message); + + if (shouldRetry && operation.retry(error)) { + logRocketEvent(CustomEvents.SUPABASE_CALL_FAILED, action); + return; + } + + resolve(response); + + // if (response.data) { + // resolve(response); + // } else { + // const finalError = operation.mainError() ?? error; + // reject(finalError); + // } + }); + }); }; +export interface InvokeResponse { + data?: T; + error?: PostgrestError; +} + // Invoke supabase edge functions. Does not use the he export function invokeSupabase(fn: FUNCTIONS, body: any) { - return supabaseClient.functions.invoke(fn, { - body: JSON.stringify(body), - }); - - // return pRetry( - // () => - // supabaseClient.functions.invoke(fn, { - // body: JSON.stringify(body), - // }), - // { - // retries: RETRY_ATTEMPTS, - // onFailedAttempt: (error) => { - // onFailedAttempt(error, `fn:${fn}`); - // }, - // } - // ).then(handleSuccess, handleFailure); + return supabaseRetry>( + () => + supabaseClient.functions.invoke(fn, { + body: JSON.stringify(body), + }), + `fn:${fn}` + ); } export const insertSupabase = ( table: TABLES, data: any ): PromiseLike> => { - return callSupabaseWithRetry( - () => supabaseClient.from(table).insert([data]).throwOnError(), + return supabaseRetry( + () => supabaseClient.from(table).insert([data]), 'insert' - ); + ).then(handleSuccess, handleFailure); }; // Makes update calls. Mainly consumed in the src/api folder @@ -292,26 +298,20 @@ export const updateSupabase = ( data: any, matchData: any ): PromiseLike> => { - return callSupabaseWithRetry( - () => - supabaseClient - .from(table) - .update(data) - .match(matchData) - .throwOnError(), + return supabaseRetry( + () => supabaseClient.from(table).update(data).match(matchData), 'update' - ); + ).then(handleSuccess, handleFailure); }; export const deleteSupabase = ( table: TABLES, matchData: any ): PromiseLike> => { - return callSupabaseWithRetry( - () => - supabaseClient.from(table).delete().match(matchData).throwOnError(), + return supabaseRetry( + () => supabaseClient.from(table).delete().match(matchData), 'delete' - ); + ).then(handleSuccess, handleFailure); }; export const jobSucceeded = (jobStatus?: JobStatus) => { From 3e230b2229e1af3fb1525b71933a9c7e3e527e02 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Tue, 7 Nov 2023 13:22:01 -0500 Subject: [PATCH 10/29] Getting more functions over to retrying Cleaning up --- src/api/draftSpecs.ts | 70 ++++++++++++++++++++++++---------------- src/services/supabase.ts | 9 +----- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/src/api/draftSpecs.ts b/src/api/draftSpecs.ts index 11d3ac03d..079b59704 100644 --- a/src/api/draftSpecs.ts +++ b/src/api/draftSpecs.ts @@ -7,6 +7,7 @@ import { handleSuccess, insertSupabase, supabaseClient, + supabaseRetry, TABLES, updateSupabase, } from 'services/supabase'; @@ -88,12 +89,15 @@ export const getDraftSpecsBySpecType = async ( draftId: string, specType: Entity ) => { - return supabaseClient - .from(TABLES.DRAFT_SPECS_EXT) - .select(`catalog_name,draft_id,expect_pub_id,spec,spec_type`) - .eq('draft_id', draftId) - .eq('spec_type', specType) - .then(handleSuccess, handleFailure); + return supabaseRetry( + () => + supabaseClient + .from(TABLES.DRAFT_SPECS_EXT) + .select(`catalog_name,draft_id,expect_pub_id,spec,spec_type`) + .eq('draft_id', draftId) + .eq('spec_type', specType), + 'getDraftSpecsBySpecType' + ).then(handleSuccess, handleFailure); }; interface DraftSpecsExtQuery_BySpecTypeReduced { @@ -106,15 +110,19 @@ export const getDraftSpecsBySpecTypeReduced = async ( draftId: string, specType: Entity ) => { - const data = await supabaseClient - .from(TABLES.DRAFT_SPECS_EXT) - .select(`draft_id,catalog_name,spec_type`) - .eq('draft_id', draftId) - .eq('spec_type', specType) - .then( - handleSuccess, - handleFailure - ); + const data = await supabaseRetry( + () => + supabaseClient + .from(TABLES.DRAFT_SPECS_EXT) + .select(`draft_id,catalog_name,spec_type`) + .eq('draft_id', draftId) + .eq('spec_type', specType) + .then( + handleSuccess, + handleFailure + ), + 'getDraftSpecsBySpecTypeReduced' + ); return data; }; @@ -134,13 +142,16 @@ export const getDraftSpecsByCatalogName = async ( catalogName: string, specType: Entity ) => { - const data = await supabaseClient - .from(TABLES.DRAFT_SPECS_EXT) - .select(`draft_id,catalog_name,spec_type,spec,expect_pub_id`) - .eq('draft_id', draftId) - .eq('catalog_name', catalogName) - .eq('spec_type', specType) - .then(handleSuccess, handleFailure); + const data = await supabaseRetry( + () => + supabaseClient + .from(TABLES.DRAFT_SPECS_EXT) + .select(`draft_id,catalog_name,spec_type,spec,expect_pub_id`) + .eq('draft_id', draftId) + .eq('catalog_name', catalogName) + .eq('spec_type', specType), + 'getDraftSpecsByCatalogName' + ).then(handleSuccess, handleFailure); return data; }; @@ -208,12 +219,15 @@ export const getDraftSpecsByDraftId = async ( draftId: string, specType: Entity ) => { - const data = await supabaseClient - .from(TABLES.DRAFT_SPECS_EXT) - .select(`draft_id,catalog_name,spec_type,spec,expect_pub_id`) - .eq('draft_id', draftId) - .eq('spec_type', specType) - .then(handleSuccess, handleFailure); + const data = await supabaseRetry( + () => + supabaseClient + .from(TABLES.DRAFT_SPECS_EXT) + .select(`draft_id,catalog_name,spec_type,spec,expect_pub_id`) + .eq('draft_id', draftId) + .eq('spec_type', specType), + 'getDraftSpecsByDraftId' + ).then(handleSuccess, handleFailure); return data; }; diff --git a/src/services/supabase.ts b/src/services/supabase.ts index bb621e7e5..f8827b874 100644 --- a/src/services/supabase.ts +++ b/src/services/supabase.ts @@ -236,7 +236,7 @@ export const handleFailure = (error: any) => { // Retry calls const RETRY_ATTEMPTS = 2; -const supabaseRetry = (makeCall: Function, action: string) => { +export const supabaseRetry = (makeCall: Function, action: string) => { const operation = retry.operation({ retries: RETRY_ATTEMPTS, randomize: true, @@ -255,13 +255,6 @@ const supabaseRetry = (makeCall: Function, action: string) => { } resolve(response); - - // if (response.data) { - // resolve(response); - // } else { - // const finalError = operation.mainError() ?? error; - // reject(finalError); - // } }); }); }; From 8f2afdd46e3e48f974a98528248c0e26cd15ac38 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Tue, 7 Nov 2023 13:28:19 -0500 Subject: [PATCH 11/29] Fixing how a response is handled --- src/api/draftSpecs.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/api/draftSpecs.ts b/src/api/draftSpecs.ts index 079b59704..dcc5eb4bc 100644 --- a/src/api/draftSpecs.ts +++ b/src/api/draftSpecs.ts @@ -116,12 +116,11 @@ export const getDraftSpecsBySpecTypeReduced = async ( .from(TABLES.DRAFT_SPECS_EXT) .select(`draft_id,catalog_name,spec_type`) .eq('draft_id', draftId) - .eq('spec_type', specType) - .then( - handleSuccess, - handleFailure - ), + .eq('spec_type', specType), 'getDraftSpecsBySpecTypeReduced' + ).then( + handleSuccess, + handleFailure ); return data; From b4e7704d5be56345d40b34129d6f243b6ab5b328 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 12:38:34 -0500 Subject: [PATCH 12/29] Comments and instruction clean up --- src/components/shared/Error/Instructions.tsx | 18 +++++++++++------- src/lang/en-US.ts | 2 -- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/components/shared/Error/Instructions.tsx b/src/components/shared/Error/Instructions.tsx index 989222d3e..ffdb489e7 100644 --- a/src/components/shared/Error/Instructions.tsx +++ b/src/components/shared/Error/Instructions.tsx @@ -1,19 +1,23 @@ import { Stack, Typography } from '@mui/material'; import MessageWithLink from 'components/content/MessageWithLink'; import { FormattedMessage } from 'react-intl'; -import { FAILED_TO_FETCH, retryAfterFailure } from 'services/shared'; +import { FAILED_TO_FETCH } from 'services/shared'; interface Props { message: string; } +// We will only show specicial messaging for errors that are can actually tell the user +// what to do in hopes to fix it. So right now that is just when there are possible +// network issues Q4 2023 function Instructions({ message }: Props) { - const messageID = - message.toLowerCase() === FAILED_TO_FETCH - ? 'error.reason.fetchFailed' - : retryAfterFailure(message) - ? 'error.reason.retry' - : 'error.reason'; + const messageID = message.toLowerCase().includes(FAILED_TO_FETCH) + ? 'error.reason.fetchFailed' + : null; + + if (!messageID) { + return null; + } return ( diff --git a/src/lang/en-US.ts b/src/lang/en-US.ts index 89a85e79c..a139ef363 100644 --- a/src/lang/en-US.ts +++ b/src/lang/en-US.ts @@ -170,8 +170,6 @@ const Data: ResolvedIntlConfig['messages'] = { const Error: ResolvedIntlConfig['messages'] = { 'error.title': `Error`, - 'error.reason': `The server returned an error. Please review the error and try again.`, - 'error.reason.retry': `This is not something you did wrong. There was a technical issue.`, 'error.reason.fetchFailed': `There was a network issue while contacting our servers. Please make sure your network is available and try again.`, 'error.instructions': `If the issue persists please {docLink}.`, From 1307ecbd3e90125b9385bc5b671f14dd21d5b2d9 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 12:46:54 -0500 Subject: [PATCH 13/29] post merge fix --- src/components/shared/Entity/Actions/useSave.ts | 3 ++- src/types/index.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/shared/Entity/Actions/useSave.ts b/src/components/shared/Entity/Actions/useSave.ts index 99384ab5b..0abff95f6 100644 --- a/src/components/shared/Entity/Actions/useSave.ts +++ b/src/components/shared/Entity/Actions/useSave.ts @@ -16,13 +16,14 @@ import useEntityWorkflowHelpers from 'components/shared/Entity/hooks/useEntityWo import { useClient } from 'hooks/supabase-swr'; import { useCallback } from 'react'; import { useIntl } from 'react-intl'; -import { CustomEvents, logRocketEvent } from 'services/logrocket'; +import { logRocketEvent } from 'services/shared'; import { DEFAULT_FILTER, JOB_STATUS_COLUMNS, TABLES, jobStatusPoller, } from 'services/supabase'; +import { CustomEvents } from 'services/types'; import { useDetailsForm_details_description } from 'stores/DetailsForm/hooks'; import { useFormStateStore_messagePrefix, diff --git a/src/types/index.ts b/src/types/index.ts index 69a5ae06d..c3a072966 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -17,7 +17,7 @@ export enum MessagePrefixes { // the invokeSupabase() function (i.e., src/services/supabase.ts). export type SupabaseInvokeResponse = | { data: null; error: Error } - | { data: null; error: any } + // | { data: null; error: any } | { data: any; error: null }; // TODO (typing): The type annotation for the data property of the JsonFormsData object From 90da169b95375682aff377cde9049ccf05ab8ca4 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 12:52:40 -0500 Subject: [PATCH 14/29] Cleaning up typing for invoke --- src/services/supabase.ts | 9 ++------- src/types/index.ts | 6 +++--- src/utils/sops-utils.ts | 4 ++-- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/services/supabase.ts b/src/services/supabase.ts index f8827b874..de33ce7db 100644 --- a/src/services/supabase.ts +++ b/src/services/supabase.ts @@ -3,7 +3,7 @@ import { User, createClient } from '@supabase/supabase-js'; import { ToPostgrestFilterBuilder } from 'hooks/supabase-swr'; import { forEach, isEmpty } from 'lodash'; import LogRocket from 'logrocket'; -import { JobStatus, SortDirection } from 'types'; +import { JobStatus, SortDirection, SupabaseInvokeResponse } from 'types'; import { hasLength, incrementInterval, timeoutCleanUp } from 'utils/misc-utils'; import retry from 'retry'; import { logRocketEvent, retryAfterFailure } from './shared'; @@ -259,14 +259,9 @@ export const supabaseRetry = (makeCall: Function, action: string) => { }); }; -export interface InvokeResponse { - data?: T; - error?: PostgrestError; -} - // Invoke supabase edge functions. Does not use the he export function invokeSupabase(fn: FUNCTIONS, body: any) { - return supabaseRetry>( + return supabaseRetry>( () => supabaseClient.functions.invoke(fn, { body: JSON.stringify(body), diff --git a/src/types/index.ts b/src/types/index.ts index c3a072966..090949dae 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -15,10 +15,10 @@ export enum MessagePrefixes { // TODO (typing): Consider adding a type annotation for the promise returned by // the invokeSupabase() function (i.e., src/services/supabase.ts). -export type SupabaseInvokeResponse = +export type SupabaseInvokeResponse = | { data: null; error: Error } - // | { data: null; error: any } - | { data: any; error: null }; + | { data: null; error: PostgrestError } + | { data: T; error: null }; // TODO (typing): The type annotation for the data property of the JsonFormsData object // mirrors the Schema interface. Consider using the Schema interface to type this property. diff --git a/src/utils/sops-utils.ts b/src/utils/sops-utils.ts index 88bb5434a..6eaded01d 100644 --- a/src/utils/sops-utils.ts +++ b/src/utils/sops-utils.ts @@ -88,7 +88,7 @@ export async function encryptEndpointConfig( imageConnectorTagId: string, callFailed: Function, { overrideJsonFormDefaults }: { overrideJsonFormDefaults: boolean } -): Promise { +): Promise> { const selectedEndpointConfig = serverUpdateRequired && Object.hasOwn(endpointConfig, sopsKey) ? parseEncryptedEndpointConfig( @@ -98,7 +98,7 @@ export async function encryptEndpointConfig( ).data : endpointConfig; - let encryptedEndpointConfig: SupabaseInvokeResponse = { + let encryptedEndpointConfig: SupabaseInvokeResponse = { data: null, error: { name: 'Object Not Reassigned', From cf505485c627e5e223b12308b2402797813184ef Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 12:56:01 -0500 Subject: [PATCH 15/29] clean up --- src/services/supabase.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/services/supabase.ts b/src/services/supabase.ts index de33ce7db..b1d9e95f7 100644 --- a/src/services/supabase.ts +++ b/src/services/supabase.ts @@ -214,8 +214,8 @@ export interface CallSupabaseResponse { data: T | null; } -export const handleSuccess = (response: any) => { - return response.error +export const handleSuccess = (response: any) => + response.error ? { data: null, error: response.error, @@ -223,15 +223,11 @@ export const handleSuccess = (response: any) => { : { data: response.data as T, }; -}; -export const handleFailure = (error: any) => { - console.log('handleFailue', error); - return { - data: null, - error, - }; -}; +export const handleFailure = (error: any) => ({ + data: null, + error, +}); // Retry calls const RETRY_ATTEMPTS = 2; From 9205ca89d14cba6080cf9b169894aa54192d11a4 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 14:42:32 -0500 Subject: [PATCH 16/29] handling combined grants --- src/api/combinedGrantsExt.ts | 18 +++++++++++++----- src/stores/Tables/Store.ts | 11 +++++++++-- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/api/combinedGrantsExt.ts b/src/api/combinedGrantsExt.ts index 7153ac9c2..378e211c8 100644 --- a/src/api/combinedGrantsExt.ts +++ b/src/api/combinedGrantsExt.ts @@ -1,12 +1,15 @@ +import { PostgrestResponse } from '@supabase/postgrest-js'; import { defaultTableFilter, RPCS, SortingProps, supabaseClient, + supabaseRetry, TABLES, } from 'services/supabase'; import { AuthRoles } from 'types'; +// Retrying handled in .../src/stores/Tables/Hydrator.tsx // Used to display prefix grants in admin page const getGrants = ( pagination: any, @@ -40,6 +43,7 @@ const getGrants = ( return queryBuilder; }; +// Retrying handled in .../src/stores/Tables/Hydrator.tsx // Used to display user grants in admin page const getGrants_Users = ( pagination: any, @@ -76,11 +80,15 @@ const getGrants_Users = ( }; export const getAuthRoles = async (capability: string) => { - return supabaseClient - .rpc(RPCS.AUTH_ROLES, { - min_capability: capability, - }) - .throwOnError(); + return supabaseRetry>( + () => + supabaseClient + .rpc(RPCS.AUTH_ROLES, { + min_capability: capability, + }) + .throwOnError(), + 'getAuthRoles' + ); }; export { getGrants, getGrants_Users }; diff --git a/src/stores/Tables/Store.ts b/src/stores/Tables/Store.ts index 6ab973a33..205a5f358 100644 --- a/src/stores/Tables/Store.ts +++ b/src/stores/Tables/Store.ts @@ -1,9 +1,13 @@ -import { PostgrestFilterBuilder } from '@supabase/postgrest-js'; +import { + PostgrestFilterBuilder, + PostgrestResponse, +} from '@supabase/postgrest-js'; import { getStatsByName, StatsFilter } from 'api/stats'; import { EVERYTHING } from 'components/collection/Selector/Table/shared'; import { LiveSpecsExtQuery } from 'hooks/useLiveSpecsExt'; import produce from 'immer'; import { flatMap } from 'lodash'; +import { supabaseRetry } from 'services/supabase'; import { AsyncOperationProps, getAsyncDefault, @@ -360,7 +364,10 @@ export const getInitialState = ( 'Table Store Hydration Start' ); - const response = await fetcher.throwOnError(); + const response = await supabaseRetry>( + () => fetcher.throwOnError(), + 'tablesHydrateStore' + ); if (response.error) { set( From ff6e5657a82fc1b9e54b301b2e98a5738e64067a Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 14:47:08 -0500 Subject: [PATCH 17/29] Getting connectos to retry --- src/api/combinedGrantsExt.ts | 2 -- src/api/connectors.ts | 16 ++++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/api/combinedGrantsExt.ts b/src/api/combinedGrantsExt.ts index 378e211c8..1b7ceaea5 100644 --- a/src/api/combinedGrantsExt.ts +++ b/src/api/combinedGrantsExt.ts @@ -9,7 +9,6 @@ import { } from 'services/supabase'; import { AuthRoles } from 'types'; -// Retrying handled in .../src/stores/Tables/Hydrator.tsx // Used to display prefix grants in admin page const getGrants = ( pagination: any, @@ -43,7 +42,6 @@ const getGrants = ( return queryBuilder; }; -// Retrying handled in .../src/stores/Tables/Hydrator.tsx // Used to display user grants in admin page const getGrants_Users = ( pagination: any, diff --git a/src/api/connectors.ts b/src/api/connectors.ts index f647c3acf..50b07c932 100644 --- a/src/api/connectors.ts +++ b/src/api/connectors.ts @@ -9,6 +9,7 @@ import { handleSuccess, SortingProps, supabaseClient, + supabaseRetry, TABLES, } from 'services/supabase'; @@ -59,12 +60,15 @@ const DETAILS_FORM_QUERY = ` `; const getConnectors_detailsForm = async (connectorId: string) => { - const data = await supabaseClient - .from(TABLES.CONNECTORS) - .select(DETAILS_FORM_QUERY) - .eq('id', connectorId) - .eq('connector_tags.connector_id', connectorId) - .then(handleSuccess, handleFailure); + const data = await supabaseRetry( + () => + supabaseClient + .from(TABLES.CONNECTORS) + .select(DETAILS_FORM_QUERY) + .eq('id', connectorId) + .eq('connector_tags.connector_id', connectorId), + 'getConnectors_detailsForm' + ).then(handleSuccess, handleFailure); return data; }; From b6629a52d62bd17adea5894b810d257b4b941329 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 15:03:19 -0500 Subject: [PATCH 18/29] Getting directives using retry --- src/api/directives.ts | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/api/directives.ts b/src/api/directives.ts index d264ca10b..92519b413 100644 --- a/src/api/directives.ts +++ b/src/api/directives.ts @@ -1,3 +1,4 @@ +import { PostgrestSingleResponse } from '@supabase/postgrest-js'; import { DIRECTIVES } from 'directives/shared'; import { UserClaims } from 'directives/types'; import { @@ -9,6 +10,7 @@ import { RPCS, SortingProps, supabaseClient, + supabaseRetry, TABLES, updateSupabase, } from 'services/supabase'; @@ -56,12 +58,16 @@ const callUpdate = ( }; const exchangeBearerToken = async (token: string) => { - return supabaseClient - .rpc(RPCS.EXCHANGE_DIRECTIVES, { - bearer_token: token, - }) - .throwOnError() - .single(); + return supabaseRetry>( + () => + supabaseClient + .rpc(RPCS.EXCHANGE_DIRECTIVES, { + bearer_token: token, + }) + .throwOnError() + .single(), + 'exchangeBearerToken' + ); }; const submitDirective = async ( @@ -94,6 +100,7 @@ const submitDirective = async ( } }; +// Called through SWR so do not need to manually retry const getAppliedDirectives = ( type: keyof typeof DIRECTIVES, userId: string, @@ -146,18 +153,22 @@ const generateGrantDirective = ( }; const getDirectiveByToken = async (token: string) => { - const data = await supabaseClient - .from(TABLES.DIRECTIVES) - .select(`spec,token`) - .eq('token', token) - .then( - handleSuccess[]>, - handleFailure - ); + const data = await supabaseRetry( + () => + supabaseClient + .from(TABLES.DIRECTIVES) + .select(`spec,token`) + .eq('token', token), + 'getDirectiveByToken' + ).then( + handleSuccess[]>, + handleFailure + ); return data; }; +// Used in table hydrator which handles the retrying const getDirectivesByType = ( directiveType: keyof typeof DIRECTIVES, pagination: any, From 7c299b9034d72d9b47de872bab607553c63a3505 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 15:09:12 -0500 Subject: [PATCH 19/29] Getting drafts using retry --- src/api/drafts.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/api/drafts.ts b/src/api/drafts.ts index ff38fc507..24967fc49 100644 --- a/src/api/drafts.ts +++ b/src/api/drafts.ts @@ -4,6 +4,7 @@ import { handleSuccess, insertSupabase, supabaseClient, + supabaseRetry, TABLES, } from 'services/supabase'; @@ -37,10 +38,10 @@ const getDraftsByCatalogName = async ( queryBuilder = queryBuilder.limit(1); } - const data = await queryBuilder.then( - handleSuccess, - handleFailure - ); + const data = await supabaseRetry( + () => queryBuilder, + 'getDraftsByCatalogName' + ).then(handleSuccess, handleFailure); return data; }; From 5d6749c3ab1251b4d08684a61203f9cc3938fb09 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 15:16:57 -0500 Subject: [PATCH 20/29] marking todo --- src/api/draftSpecs.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api/draftSpecs.ts b/src/api/draftSpecs.ts index dcc5eb4bc..63d5e9b96 100644 --- a/src/api/draftSpecs.ts +++ b/src/api/draftSpecs.ts @@ -168,6 +168,7 @@ export const deleteDraftSpecsByCatalogName = async ( const promises: Array>> = []; let index = 0; + // TODO (retry) need to figure how we want to handle this kind of action in retrying const deletePromiseGenerator = (idx: number) => { return supabaseClient .from(TABLES.DRAFT_SPECS) From 62c177eff9b435613bf2db37520db6489b026f8d Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 15:22:35 -0500 Subject: [PATCH 21/29] Getting hydration using retry --- src/api/hydration.ts | 60 ++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/src/api/hydration.ts b/src/api/hydration.ts index 240cc3ee1..1c2528cee 100644 --- a/src/api/hydration.ts +++ b/src/api/hydration.ts @@ -7,6 +7,7 @@ import { handleFailure, handleSuccess, supabaseClient, + supabaseRetry, TABLES, } from 'services/supabase'; import { Entity } from 'types'; @@ -24,21 +25,27 @@ type ConnectorTagEndpointData = Pick< >; export const getSchema_Endpoint = async (connectorId: string | null) => { - const endpointSchema = await supabaseClient - .from(TABLES.CONNECTOR_TAGS) - .select(`connector_id,endpoint_spec_schema`) - .eq('connector_id', connectorId) - .then(handleSuccess, handleFailure); + const endpointSchema = await supabaseRetry( + () => + supabaseClient + .from(TABLES.CONNECTOR_TAGS) + .select(`connector_id,endpoint_spec_schema`) + .eq('connector_id', connectorId), + 'getSchema_Endpoint' + ).then(handleSuccess, handleFailure); return endpointSchema; }; export const getSchema_Resource = async (connectorId: string | null) => { - const resourceSchema = await supabaseClient - .from(TABLES.CONNECTOR_TAGS) - .select(`connector_id,resource_spec_schema`) - .eq('connector_id', connectorId) - .then(handleSuccess, handleFailure); + const resourceSchema = await supabaseRetry( + () => + supabaseClient + .from(TABLES.CONNECTOR_TAGS) + .select(`connector_id,resource_spec_schema`) + .eq('connector_id', connectorId), + 'getSchema_Resource' + ).then(handleSuccess, handleFailure); return resourceSchema; }; @@ -52,13 +59,16 @@ export const getLiveSpecsByLiveSpecId = async ( const draftArray: string[] = typeof liveSpecId === 'string' ? [liveSpecId] : liveSpecId; - const data = await supabaseClient - .from(TABLES.LIVE_SPECS_EXT) - .select(liveSpecColumns) - .eq('spec_type', specType) - .or(`id.in.(${draftArray})`) - .order('updated_at', { ascending: false }) - .then(handleSuccess, handleFailure); + const data = await supabaseRetry( + () => + supabaseClient + .from(TABLES.LIVE_SPECS_EXT) + .select(liveSpecColumns) + .eq('spec_type', specType) + .or(`id.in.(${draftArray})`) + .order('updated_at', { ascending: false }), + 'getLiveSpecsByLiveSpecId' + ).then(handleSuccess, handleFailure); return data; }; @@ -66,11 +76,17 @@ export const getLiveSpecsByLiveSpecId = async ( export const getLiveSpecsById_writesTo = async ( liveSpecId: string | string[] ) => { - const data = await supabaseClient - .from(TABLES.LIVE_SPECS_EXT) - .select(`catalog_name,writes_to,spec_type`) - .in('id', typeof liveSpecId === 'string' ? [liveSpecId] : liveSpecId) - .then(handleSuccess, handleFailure); + const data = await supabaseRetry( + () => + supabaseClient + .from(TABLES.LIVE_SPECS_EXT) + .select(`catalog_name,writes_to,spec_type`) + .in( + 'id', + typeof liveSpecId === 'string' ? [liveSpecId] : liveSpecId + ), + 'getLiveSpecsById_writesTo' + ).then(handleSuccess, handleFailure); return data; }; From ad7dda17ab0824617a86c1cabf3c031662e956fd Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 15:26:36 -0500 Subject: [PATCH 22/29] Getting inferred schema using retry --- src/api/inferred_schemas.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/api/inferred_schemas.ts b/src/api/inferred_schemas.ts index 8f1543a88..6b54d3c2f 100644 --- a/src/api/inferred_schemas.ts +++ b/src/api/inferred_schemas.ts @@ -2,16 +2,20 @@ import { handleFailure, handleSuccess, supabaseClient, + supabaseRetry, TABLES, } from 'services/supabase'; import { InferredSchemas } from 'types'; export const fetchInferredSchema = (collectionName: string) => { - const queryBuilder = supabaseClient - .from(TABLES.INFERRED_SCHEMAS) - .select(`schema`) - .eq('collection_name', collectionName) - .then(handleSuccess[]>, handleFailure); + const queryBuilder = supabaseRetry( + () => + supabaseClient + .from(TABLES.INFERRED_SCHEMAS) + .select(`schema`) + .eq('collection_name', collectionName), + 'fetchInferredSchema' + ).then(handleSuccess[]>, handleFailure); return queryBuilder; }; From 3825b36afade1fe006cade26e906140bca953c96 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 15:37:53 -0500 Subject: [PATCH 23/29] Do not want to throw on error to help with retry --- src/stores/Tables/Store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/Tables/Store.ts b/src/stores/Tables/Store.ts index 205a5f358..03fceba86 100644 --- a/src/stores/Tables/Store.ts +++ b/src/stores/Tables/Store.ts @@ -365,7 +365,7 @@ export const getInitialState = ( ); const response = await supabaseRetry>( - () => fetcher.throwOnError(), + () => fetcher, 'tablesHydrateStore' ); From 791d5d24b4bd37dc3e2215a14b91c3a175f89d37 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 15:38:42 -0500 Subject: [PATCH 24/29] marking todo --- src/api/draftSpecs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/draftSpecs.ts b/src/api/draftSpecs.ts index 63d5e9b96..97b6a0a7e 100644 --- a/src/api/draftSpecs.ts +++ b/src/api/draftSpecs.ts @@ -168,7 +168,7 @@ export const deleteDraftSpecsByCatalogName = async ( const promises: Array>> = []; let index = 0; - // TODO (retry) need to figure how we want to handle this kind of action in retrying + // TODO (retry) promise generator const deletePromiseGenerator = (idx: number) => { return supabaseClient .from(TABLES.DRAFT_SPECS) From 13239b1e2b03ca51efdbc68a5aa71d3b75678453 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 15:40:09 -0500 Subject: [PATCH 25/29] Do not want to throw on error to help with retry --- src/api/combinedGrantsExt.ts | 8 +++----- src/api/directives.ts | 1 - 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/api/combinedGrantsExt.ts b/src/api/combinedGrantsExt.ts index 1b7ceaea5..a2cb57750 100644 --- a/src/api/combinedGrantsExt.ts +++ b/src/api/combinedGrantsExt.ts @@ -80,11 +80,9 @@ const getGrants_Users = ( export const getAuthRoles = async (capability: string) => { return supabaseRetry>( () => - supabaseClient - .rpc(RPCS.AUTH_ROLES, { - min_capability: capability, - }) - .throwOnError(), + supabaseClient.rpc(RPCS.AUTH_ROLES, { + min_capability: capability, + }), 'getAuthRoles' ); }; diff --git a/src/api/directives.ts b/src/api/directives.ts index 92519b413..09ffb88df 100644 --- a/src/api/directives.ts +++ b/src/api/directives.ts @@ -64,7 +64,6 @@ const exchangeBearerToken = async (token: string) => { .rpc(RPCS.EXCHANGE_DIRECTIVES, { bearer_token: token, }) - .throwOnError() .single(), 'exchangeBearerToken' ); From 3529240a181b1c38eda770c976ef0250ff7e62e3 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 15:40:28 -0500 Subject: [PATCH 26/29] getting live specs using retry --- src/api/liveSpecsExt.ts | 50 +++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src/api/liveSpecsExt.ts b/src/api/liveSpecsExt.ts index 5b5c6f313..286fa21a2 100644 --- a/src/api/liveSpecsExt.ts +++ b/src/api/liveSpecsExt.ts @@ -10,6 +10,7 @@ import { QUERY_PARAM_CONNECTOR_TITLE, SortingProps, supabaseClient, + supabaseRetry, TABLES, } from 'services/supabase'; import { @@ -230,11 +231,14 @@ const DETAILS_FORM_QUERY = ` `; const getLiveSpecs_detailsForm = async (liveSpecId: string) => { - const data = await supabaseClient - .from(TABLES.LIVE_SPECS_EXT) - .select(DETAILS_FORM_QUERY) - .eq('id', liveSpecId) - .then(handleSuccess, handleFailure); + const data = await supabaseRetry( + () => + supabaseClient + .from(TABLES.LIVE_SPECS_EXT) + .select(DETAILS_FORM_QUERY) + .eq('id', liveSpecId), + 'getLiveSpecs_detailsForm' + ).then(handleSuccess, handleFailure); return data; }; @@ -251,12 +255,15 @@ const getLiveSpecsByCatalogName = async ( catalogName: string, specType: Entity ) => { - const data = await supabaseClient - .from(TABLES.LIVE_SPECS_EXT) - .select(`catalog_name,spec_type,spec,last_pub_id`) - .eq('catalog_name', catalogName) - .eq('spec_type', specType) - .then(handleSuccess, handleFailure); + const data = await supabaseRetry( + () => + supabaseClient + .from(TABLES.LIVE_SPECS_EXT) + .select(`catalog_name,spec_type,spec,last_pub_id`) + .eq('catalog_name', catalogName) + .eq('spec_type', specType), + 'getLiveSpecsByCatalogName' + ).then(handleSuccess, handleFailure); return data; }; @@ -279,6 +286,7 @@ const getLiveSpecsByCatalogNames = async ( > = []; let index = 0; + // TODO (retry) promise generator const queryPromiseGenerator = (idx: number) => { let query = supabaseClient .from(TABLES.LIVE_SPECS_EXT) @@ -333,7 +341,10 @@ const getLiveSpecsByConnectorId = async ( }); } - const data = await queryBuilder.then( + const data = await supabaseRetry( + () => queryBuilder, + 'getLiveSpecsByConnectorId' + ).then( handleSuccess, handleFailure ); @@ -351,11 +362,16 @@ export interface LiveSpecsExtQuery_ByLiveSpecId { } const getLiveSpecsByLiveSpecId = async (liveSpecId: string) => { - const data = await supabaseClient - .from(TABLES.LIVE_SPECS_EXT) - .select('catalog_name,id,spec_type,last_pub_id,spec,connector_id') - .eq('id', liveSpecId) - .then(handleSuccess, handleFailure); + const data = await supabaseRetry( + () => + supabaseClient + .from(TABLES.LIVE_SPECS_EXT) + .select( + 'catalog_name,id,spec_type,last_pub_id,spec,connector_id' + ) + .eq('id', liveSpecId), + 'getLiveSpecsByLiveSpecId' + ).then(handleSuccess, handleFailure); return data; }; From 4b72b34a0fc002b62e8a6dcadd6828ab3b9254c0 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 15:49:17 -0500 Subject: [PATCH 27/29] Getting pub view using retry --- src/api/publicationSpecsExt.ts | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/api/publicationSpecsExt.ts b/src/api/publicationSpecsExt.ts index 4bfd9d02f..2a4d994c0 100644 --- a/src/api/publicationSpecsExt.ts +++ b/src/api/publicationSpecsExt.ts @@ -1,4 +1,5 @@ -import { supabaseClient, TABLES } from 'services/supabase'; +import { PostgrestResponse } from '@supabase/postgrest-js'; +import { supabaseClient, supabaseRetry, TABLES } from 'services/supabase'; import { Schema } from 'types'; export interface PublicationSpecsExt_PublicationHistory { @@ -32,11 +33,17 @@ export const getLiveSpecIdByPublication = ( pubId: string | null, // Do not actually pass null... just making typing easiser catalogName: string ) => { - return supabaseClient - .from( - TABLES.PUBLICATION_SPECS_EXT - ) - .select(`live_spec_id`) - .eq('pub_id', pubId) - .eq('catalog_name', catalogName); + return supabaseRetry< + PostgrestResponse + >( + () => + supabaseClient + .from( + TABLES.PUBLICATION_SPECS_EXT + ) + .select(`live_spec_id`) + .eq('pub_id', pubId) + .eq('catalog_name', catalogName), + 'getLiveSpecIdByPublication' + ); }; From 43ffa1052fb98fbc15107ba993658845e922fbb1 Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Thu, 9 Nov 2023 15:58:52 -0500 Subject: [PATCH 28/29] Getting more stuff working with retry --- src/api/directives.ts | 1 - src/api/stats.ts | 6 +++++- src/api/tokens.ts | 22 ++++++++++++++-------- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/api/directives.ts b/src/api/directives.ts index 09ffb88df..e508c0394 100644 --- a/src/api/directives.ts +++ b/src/api/directives.ts @@ -99,7 +99,6 @@ const submitDirective = async ( } }; -// Called through SWR so do not need to manually retry const getAppliedDirectives = ( type: keyof typeof DIRECTIVES, userId: string, diff --git a/src/api/stats.ts b/src/api/stats.ts index 80e5def7a..7704873b4 100644 --- a/src/api/stats.ts +++ b/src/api/stats.ts @@ -18,6 +18,7 @@ import { handleSuccess, SortingProps, supabaseClient, + supabaseRetry, TABLES, } from 'services/supabase'; import { @@ -187,7 +188,10 @@ const getStatsByName = (names: string[], filter?: StatsFilter) => { throw new Error('Unsupported filter used in Stats Query'); } - return queryBuilder.then(handleSuccess, handleFailure); + return supabaseRetry(() => queryBuilder, '').then( + handleSuccess, + handleFailure + ); }; const getStatsForBilling = (tenants: string[], startDate: AllowedDates) => { diff --git a/src/api/tokens.ts b/src/api/tokens.ts index 0008777e2..7ef092b30 100644 --- a/src/api/tokens.ts +++ b/src/api/tokens.ts @@ -1,16 +1,22 @@ // {"multi_use": true, "valid_for": "10 days"} -import { RPCS, supabaseClient } from 'services/supabase'; +import { PostgrestSingleResponse } from '@supabase/postgrest-js'; +import { RPCS, supabaseClient, supabaseRetry } from 'services/supabase'; export const createRefreshToken = async ( multi_use: boolean, valid_for: string ) => { - return supabaseClient - .rpc<{ id: string; secret: string }>(RPCS.CREATE_REFRESH_TOKEN, { - multi_use, - valid_for, - }) - .throwOnError() - .single(); + return supabaseRetry< + PostgrestSingleResponse<{ id: string; secret: string }> + >( + () => + supabaseClient + .rpc(RPCS.CREATE_REFRESH_TOKEN, { + multi_use, + valid_for, + }) + .single(), + 'createRefreshToken' + ); }; From efbc6a04f5fe56f9648cbd16698b4cf845e8a79e Mon Sep 17 00:00:00 2001 From: Travis Jenkins Date: Tue, 14 Nov 2023 10:03:23 -0500 Subject: [PATCH 29/29] PR: updating name --- src/components/shared/Error/Message.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/shared/Error/Message.tsx b/src/components/shared/Error/Message.tsx index eb15cc92a..06aa3db7c 100644 --- a/src/components/shared/Error/Message.tsx +++ b/src/components/shared/Error/Message.tsx @@ -22,16 +22,16 @@ function Message({ error }: Props) { // needs translated. However, Our shortest keys (cta.foo) // can also look a lot like how Supabase returns // errors when fetching wrong keys from a table (tableName.col). - const displayOnlyError = + const displayErrorOnly = (typeof error === 'object' && error.hasOwnProperty('code')) || failedAfterRetry; // We do not need to translate messages from Supabase as they comeback readable - const message = displayOnlyError + const message = displayErrorOnly ? error.message : intl.formatMessage({ id: error.message }); - if (!displayOnlyError) { + if (!displayErrorOnly) { return ( {message}