From 437669725065ea6039c4dde23673763be8d8c777 Mon Sep 17 00:00:00 2001 From: andreas-unleash Date: Tue, 12 Dec 2023 15:34:32 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20show=20Changes=20scheduled=20in=20featu?= =?UTF-8?q?re=20variants=20even=20when=20CR=20are=20dis=E2=80=A6=20(#5613)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit show Changes scheduled in feature variants even when CR are disabled Modifies existing hook to call the new `change-requests/scheduled` endpoint that returns the relevant scheduled change requests even when change requests are disabled Rename the ChangeRequestIdentityData to ScheduledChangeRequestViewModel for consistency (finalised schemas will replace the BE and FE types in a follow up PR) Closes # [1-1746](https://linear.app/unleash/issue/1-1746/show-change-scheduled-badge-in-feature-environment-variants-even-if) Screenshot 2023-12-12 at 14 24 44 --------- Signed-off-by: andreas-unleash --- .../FeatureArchiveDialog.tsx | 8 ++-- .../StrategyDraggableItem.tsx | 4 +- .../EnvironmentVariantsCard.test.tsx | 42 ++++++++++++++++++- .../useVariantsFromScheduledRequests.ts | 36 +++++----------- .../useScheduledChangeRequestsWithFlags.ts | 8 +--- .../useScheduledChangeRequestsWithStrategy.ts | 5 ++- .../useScheduledChangeRequestsWithVariant.ts | 32 ++++++++++++++ 7 files changed, 94 insertions(+), 41 deletions(-) create mode 100644 frontend/src/hooks/api/getters/useScheduledChangeRequestsWithVariants/useScheduledChangeRequestsWithVariant.ts diff --git a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx index e64c8fb1db..13610fc663 100644 --- a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx +++ b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx @@ -11,10 +11,8 @@ import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled'; import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi'; import { usePendingChangeRequests } from 'hooks/api/getters/usePendingChangeRequests/usePendingChangeRequests'; import { useHighestPermissionChangeRequestEnvironment } from 'hooks/useHighestPermissionChangeRequestEnvironment'; -import { - ChangeRequestIdentityData, - useScheduledChangeRequestsWithFlags, -} from 'hooks/api/getters/useScheduledChangeRequestsWithFlags/useScheduledChangeRequestsWithFlags'; +import { useScheduledChangeRequestsWithFlags } from 'hooks/api/getters/useScheduledChangeRequestsWithFlags/useScheduledChangeRequestsWithFlags'; +import { ScheduledChangeRequestViewModel } from 'hooks/api/getters/useScheduledChangeRequestsWithStrategy/useScheduledChangeRequestsWithStrategy'; interface IFeatureArchiveDialogProps { isOpen: boolean; @@ -128,7 +126,7 @@ const ArchiveParentError = ({ }; const ScheduledChangeRequestAlert: VFC<{ - changeRequests?: ChangeRequestIdentityData[]; + changeRequests?: ScheduledChangeRequestViewModel[]; projectId: string; }> = ({ changeRequests, projectId }) => { if (changeRequests && changeRequests.length > 0) { diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyDraggableItem.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyDraggableItem.tsx index a3a83cde5c..d91182c573 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyDraggableItem.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyDraggableItem.tsx @@ -14,7 +14,7 @@ import { ChangesScheduledBadge } from 'component/changeRequest/ModifiedInChangeR import { IFeatureChange } from 'component/changeRequest/changeRequest.types'; import { Badge } from 'component/common/Badge/Badge'; import { - ChangeRequestIdentityData, + ScheduledChangeRequestViewModel, useScheduledChangeRequestsWithStrategy, } from 'hooks/api/getters/useScheduledChangeRequestsWithStrategy/useScheduledChangeRequestsWithStrategy'; @@ -114,7 +114,7 @@ const ChangeRequestStatusBadge = ({ const renderHeaderChildren = ( changes?: UseStrategyChangeFromRequestResult, - scheduledChanges?: ChangeRequestIdentityData[], + scheduledChanges?: ScheduledChangeRequestViewModel[], ): JSX.Element[] => { const badges: JSX.Element[] = []; if (changes?.length === 0 && scheduledChanges?.length === 0) { diff --git a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsCard.test.tsx b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsCard.test.tsx index 8ab488802a..583086a6ff 100644 --- a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsCard.test.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsCard.test.tsx @@ -136,6 +136,25 @@ const changeRequestConfig = () => 'get', ); +const disableChangeRequests = () => + testServerRoute( + server, + '/api/admin/projects/default/change-requests/config', + [ + { + environment: 'development', + type: 'development', + changeRequestEnabled: false, + }, + { + environment: 'production', + type: 'production', + changeRequestEnabled: false, + }, + ], + 'get', + ); + const feature = () => { testServerRoute(server, '/api/admin/projects/default/features/feature1', { environments: [ @@ -246,7 +265,28 @@ describe('Change request badges for variants', () => { testServerRoute( server, - '/api/admin/projects/default/change-requests/pending/feature1', + '/api/admin/projects/default/change-requests/scheduled', + [changeRequest], + ); + + render(, { + route: '/projects/default/features/feature1/variants', + permissions: [ + { + permission: ADMIN, + }, + ], + }); + await screen.findByText('Changes Scheduled'); + }); + + test('should render the badge when scheduled request with "patchVariant" action when change requests are disabled', async () => { + disableChangeRequests(); + const changeRequest = scheduledRequest('patchVariant', 1); + + testServerRoute( + server, + '/api/admin/projects/default/change-requests/scheduled', [changeRequest], ); diff --git a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/useVariantsFromScheduledRequests.ts b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/useVariantsFromScheduledRequests.ts index 325e088773..e47cd6ef31 100644 --- a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/useVariantsFromScheduledRequests.ts +++ b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/useVariantsFromScheduledRequests.ts @@ -1,39 +1,25 @@ -import { usePendingChangeRequestsForFeature } from 'hooks/api/getters/usePendingChangeRequestsForFeature/usePendingChangeRequestsForFeature'; +import { useScheduledChangeRequestsWithVariant } from 'hooks/api/getters/useScheduledChangeRequestsWithVariants/useScheduledChangeRequestsWithVariant'; export const useVariantsFromScheduledRequests = ( projectId: string, featureId: string, environment: string, ): number[] => { - const { changeRequests } = usePendingChangeRequestsForFeature( + const { changeRequests } = useScheduledChangeRequestsWithVariant( projectId, featureId, ); - const scheduledEnvironmentRequests = - changeRequests?.filter( - (request) => - request.environment === environment && - request.state === 'Scheduled', - ) || []; + const filtered = changeRequests?.filter( + (changeRequestIdentity) => + changeRequestIdentity.environment === environment, + ); - const result: number[] = []; - if (scheduledEnvironmentRequests.length === 0) { - return result; + if (filtered) { + return filtered.map( + (changeRequestIdentity) => changeRequestIdentity.id, + ); } - scheduledEnvironmentRequests.forEach((scheduledRequest) => { - const feature = scheduledRequest?.features.find( - (feature) => feature.name === featureId, - ); - const change = feature?.changes.find((change) => { - return change.action === 'patchVariant'; - }); - - if (change) { - result.push(scheduledRequest.id); - } - }); - - return result; + return []; }; diff --git a/frontend/src/hooks/api/getters/useScheduledChangeRequestsWithFlags/useScheduledChangeRequestsWithFlags.ts b/frontend/src/hooks/api/getters/useScheduledChangeRequestsWithFlags/useScheduledChangeRequestsWithFlags.ts index 678e547b4f..d5b96cc9c8 100644 --- a/frontend/src/hooks/api/getters/useScheduledChangeRequestsWithFlags/useScheduledChangeRequestsWithFlags.ts +++ b/frontend/src/hooks/api/getters/useScheduledChangeRequestsWithFlags/useScheduledChangeRequestsWithFlags.ts @@ -1,6 +1,7 @@ import { formatApiPath } from 'utils/formatPath'; import handleErrorResponses from '../httpErrorResponseHandler'; import { useEnterpriseSWR } from '../useEnterpriseSWR/useEnterpriseSWR'; +import { ScheduledChangeRequestViewModel } from '../useScheduledChangeRequestsWithStrategy/useScheduledChangeRequestsWithStrategy'; const fetcher = (path: string) => { return fetch(path) @@ -8,11 +9,6 @@ const fetcher = (path: string) => { .then((res) => res.json()); }; -export type ChangeRequestIdentityData = { - id: number; - title?: string; -}; - export const useScheduledChangeRequestsWithFlags = ( project: string, flags: string[], @@ -20,7 +16,7 @@ export const useScheduledChangeRequestsWithFlags = ( const queryString = flags.map((flag) => `feature=${flag}`).join('&'); const { data, error, mutate } = useEnterpriseSWR< - ChangeRequestIdentityData[] + ScheduledChangeRequestViewModel[] >( [], formatApiPath( diff --git a/frontend/src/hooks/api/getters/useScheduledChangeRequestsWithStrategy/useScheduledChangeRequestsWithStrategy.ts b/frontend/src/hooks/api/getters/useScheduledChangeRequestsWithStrategy/useScheduledChangeRequestsWithStrategy.ts index 02f498ceb4..35d97341e4 100644 --- a/frontend/src/hooks/api/getters/useScheduledChangeRequestsWithStrategy/useScheduledChangeRequestsWithStrategy.ts +++ b/frontend/src/hooks/api/getters/useScheduledChangeRequestsWithStrategy/useScheduledChangeRequestsWithStrategy.ts @@ -8,8 +8,9 @@ const fetcher = (path: string) => { .then((res) => res.json()); }; -export type ChangeRequestIdentityData = { +export type ScheduledChangeRequestViewModel = { id: number; + environment: string; title?: string; }; @@ -18,7 +19,7 @@ export const useScheduledChangeRequestsWithStrategy = ( strategyId: string, ) => { const { data, error, mutate } = useEnterpriseSWR< - ChangeRequestIdentityData[] + ScheduledChangeRequestViewModel[] >( [], formatApiPath( diff --git a/frontend/src/hooks/api/getters/useScheduledChangeRequestsWithVariants/useScheduledChangeRequestsWithVariant.ts b/frontend/src/hooks/api/getters/useScheduledChangeRequestsWithVariants/useScheduledChangeRequestsWithVariant.ts new file mode 100644 index 0000000000..619e36571f --- /dev/null +++ b/frontend/src/hooks/api/getters/useScheduledChangeRequestsWithVariants/useScheduledChangeRequestsWithVariant.ts @@ -0,0 +1,32 @@ +import { formatApiPath } from 'utils/formatPath'; +import handleErrorResponses from '../httpErrorResponseHandler'; +import { useEnterpriseSWR } from '../useEnterpriseSWR/useEnterpriseSWR'; +import { ScheduledChangeRequestViewModel } from '../useScheduledChangeRequestsWithStrategy/useScheduledChangeRequestsWithStrategy'; + +const fetcher = (path: string) => { + return fetch(path) + .then(handleErrorResponses('ChangeRequest')) + .then((res) => res.json()); +}; + +export const useScheduledChangeRequestsWithVariant = ( + project: string, + feature: string, +) => { + const { data, error, mutate } = useEnterpriseSWR< + ScheduledChangeRequestViewModel[] + >( + [], + formatApiPath( + `api/admin/projects/${project}/change-requests/scheduled?variantForFlag=${feature}`, + ), + fetcher, + ); + + return { + changeRequests: data, + loading: !error && !data, + refetch: mutate, + error, + }; +};