From cf4435ca2d4a7cfb951c21c8d61cbc68e0124e43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20G=C3=B3is?= Date: Tue, 23 Jul 2024 10:27:40 +0100 Subject: [PATCH] chore: integration events hook (#7641) https://linear.app/unleash/issue/2-2440/create-new-integration-event-hooks Adds a frontend hook for integration events, allowing us to easily fetch paginated integration events for a specific integration configuration id. --- .../useIntegrationEvents.ts | 74 +++++++++++++++++++ frontend/src/interfaces/integrationEvent.ts | 15 ++++ frontend/src/interfaces/uiConfig.ts | 1 + 3 files changed, 90 insertions(+) create mode 100644 frontend/src/hooks/api/getters/useIntegrationEvents/useIntegrationEvents.ts create mode 100644 frontend/src/interfaces/integrationEvent.ts diff --git a/frontend/src/hooks/api/getters/useIntegrationEvents/useIntegrationEvents.ts b/frontend/src/hooks/api/getters/useIntegrationEvents/useIntegrationEvents.ts new file mode 100644 index 0000000000..95176e4ce2 --- /dev/null +++ b/frontend/src/hooks/api/getters/useIntegrationEvents/useIntegrationEvents.ts @@ -0,0 +1,74 @@ +import { useContext } from 'react'; +import useSWRInfinite, { + type SWRInfiniteConfiguration, + type SWRInfiniteKeyLoader, +} from 'swr/infinite'; +import { formatApiPath } from 'utils/formatPath'; +import type { IntegrationEvents } from 'interfaces/integrationEvent'; +import { useUiFlag } from 'hooks/useUiFlag'; +import AccessContext from 'contexts/AccessContext'; +import handleErrorResponses from '../httpErrorResponseHandler'; + +const fetcher = async (url: string) => { + const response = await fetch(url); + await handleErrorResponses('Integration events')(response); + return response.json(); +}; + +export const useIntegrationEvents = ( + integrationId?: number, + limit = 50, + options: SWRInfiniteConfiguration = {}, +) => { + const { isAdmin } = useContext(AccessContext); + const integrationEventsEnabled = useUiFlag('integrationEvents'); + + const getKey: SWRInfiniteKeyLoader = ( + pageIndex: number, + previousPageData: IntegrationEvents, + ) => { + // Does not meet conditions + if (!integrationId || !isAdmin || !integrationEventsEnabled) + return null; + + // Reached the end + if (previousPageData && !previousPageData.integrationEvents.length) + return null; + + return formatApiPath( + `api/admin/addons/${integrationId}/events?limit=${limit}&offset=${ + pageIndex * limit + }`, + ); + }; + + const { data, error, size, setSize, mutate } = + useSWRInfinite(getKey, fetcher, { + ...options, + revalidateAll: true, + }); + + const integrationEvents = data + ? data.flatMap(({ integrationEvents }) => integrationEvents) + : []; + + const isLoadingInitialData = !data && !error; + const isLoadingMore = size > 0 && !data?.[size - 1]; + const loading = isLoadingInitialData || isLoadingMore; + + const hasMore = data?.[size - 1]?.integrationEvents.length === limit; + + const loadMore = () => { + if (loading || !hasMore) return; + setSize(size + 1); + }; + + return { + integrationEvents, + hasMore, + loadMore, + loading, + refetch: () => mutate(), + error, + }; +}; diff --git a/frontend/src/interfaces/integrationEvent.ts b/frontend/src/interfaces/integrationEvent.ts new file mode 100644 index 0000000000..fcbe5894e7 --- /dev/null +++ b/frontend/src/interfaces/integrationEvent.ts @@ -0,0 +1,15 @@ +import type { IEvent } from './event'; + +export type IntegrationEvent = { + id: string; + integrationId: number; + createdAt: string; + state: 'success' | 'failed' | 'successWithErrors'; + stateDetails: string; + event: IEvent; + details: Record; +}; + +export type IntegrationEvents = { + integrationEvents: IntegrationEvent[]; +}; diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index 64cf61575b..38072196ca 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -93,6 +93,7 @@ export type UiFlags = { resourceLimits?: boolean; insightsV2?: boolean; featureCollaborators?: boolean; + integrationEvents?: boolean; }; export interface IVersionInfo {