From e6355f467679bd986c62688a318a7c859f3cf1dc Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Thu, 25 Apr 2024 09:18:05 +0200 Subject: [PATCH] feat: connect feature lifecycle to real API (#6921) --- .../populateCurrentStage.test.ts | 76 +++++++++++++++++++ .../FeatureLifecycle/populateCurrentStage.ts | 48 ++++++++++++ .../FeatureOverviewMetaData.tsx | 27 +++---- frontend/src/interfaces/featureToggle.ts | 4 + 4 files changed, 139 insertions(+), 16 deletions(-) create mode 100644 frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/populateCurrentStage.test.ts create mode 100644 frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/populateCurrentStage.ts diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/populateCurrentStage.test.ts b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/populateCurrentStage.test.ts new file mode 100644 index 0000000000..cbe6ff611f --- /dev/null +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/populateCurrentStage.test.ts @@ -0,0 +1,76 @@ +import { populateCurrentStage } from './populateCurrentStage'; +import type { IFeatureToggle } from '../../../../../interfaces/featureToggle'; + +describe('populateCurrentStage', () => { + it('should return undefined if lifecycle is not defined', () => { + const feature = {}; + const result = populateCurrentStage(feature as IFeatureToggle); + expect(result).toBeUndefined(); + }); + + it('should return initial stage when lifecycle stage is initial', () => { + const feature = { + lifecycle: { stage: 'initial' }, + }; + const expected = { name: 'initial' }; + const result = populateCurrentStage(feature as IFeatureToggle); + expect(result).toEqual(expected); + }); + + it('should correctly populate pre-live stage with dev environments', () => { + const feature = { + lifecycle: { stage: 'pre-live' }, + environments: [ + { name: 'test', type: 'development', lastSeenAt: null }, + { name: 'test1', type: 'production', lastSeenAt: '2022-08-01' }, + { name: 'dev', type: 'development', lastSeenAt: '2022-08-01' }, + ], + } as IFeatureToggle; + const expected = { + name: 'pre-live', + environments: [{ name: 'dev', lastSeenAt: '2022-08-01' }], + }; + const result = populateCurrentStage(feature); + expect(result).toEqual(expected); + }); + + it('should handle live stage with production environments', () => { + const feature = { + lifecycle: { stage: 'live' }, + environments: [ + { name: 'prod', type: 'production', lastSeenAt: '2022-08-01' }, + ], + } as IFeatureToggle; + const expected = { + name: 'live', + environments: [{ name: 'prod', lastSeenAt: '2022-08-01' }], + }; + const result = populateCurrentStage(feature); + expect(result).toEqual(expected); + }); + + it('should return completed stage with production environments', () => { + const feature = { + lifecycle: { stage: 'completed' }, + environments: [ + { name: 'prod', type: 'production', lastSeenAt: '2022-08-01' }, + ], + } as IFeatureToggle; + const expected = { + name: 'completed', + status: 'kept', + environments: [{ name: 'prod', lastSeenAt: '2022-08-01' }], + }; + const result = populateCurrentStage(feature); + expect(result).toEqual(expected); + }); + + it('should return archived stage when lifecycle stage is archived', () => { + const feature = { + lifecycle: { stage: 'archived' }, + } as IFeatureToggle; + const expected = { name: 'archived' }; + const result = populateCurrentStage(feature); + expect(result).toEqual(expected); + }); +}); diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/populateCurrentStage.ts b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/populateCurrentStage.ts new file mode 100644 index 0000000000..e2bc152693 --- /dev/null +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/populateCurrentStage.ts @@ -0,0 +1,48 @@ +import type { IFeatureToggle } from 'interfaces/featureToggle'; +import type { LifecycleStage } from './LifecycleStage'; + +export const populateCurrentStage = ( + feature: Pick, +): LifecycleStage | undefined => { + if (!feature.lifecycle) return undefined; + + const getFilteredEnvironments = (condition: (type: string) => boolean) => { + return feature.environments + .filter((env) => condition(env.type) && Boolean(env.lastSeenAt)) + .map((env) => ({ + name: env.name, + lastSeenAt: env.lastSeenAt!, + })); + }; + + switch (feature.lifecycle.stage) { + case 'initial': + return { name: 'initial' }; + case 'pre-live': + return { + name: 'pre-live', + environments: getFilteredEnvironments( + (type) => type !== 'production', + ), + }; + case 'live': + return { + name: 'live', + environments: getFilteredEnvironments( + (type) => type === 'production', + ), + }; + case 'completed': + return { + name: 'completed', + status: 'kept', + environments: getFilteredEnvironments( + (type) => type === 'production', + ), + }; + case 'archived': + return { name: 'archived' }; + default: + return undefined; + } +}; diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/FeatureOverviewMetaData.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/FeatureOverviewMetaData.tsx index 3d01a9b58b..831261ac29 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/FeatureOverviewMetaData.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/FeatureOverviewMetaData.tsx @@ -10,7 +10,7 @@ import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { useUiFlag } from 'hooks/useUiFlag'; import { FeatureLifecycleTooltip } from '../FeatureLifecycle/FeatureLifecycleTooltip'; import { FeatureLifecycleStageIcon } from '../FeatureLifecycle/FeatureLifecycleStageIcon'; -import type { LifecycleStage } from '../FeatureLifecycle/LifecycleStage'; +import { populateCurrentStage } from '../FeatureLifecycle/populateCurrentStage'; const StyledContainer = styled('div')(({ theme }) => ({ borderRadius: theme.shape.borderRadiusLarge, @@ -82,17 +82,7 @@ const FeatureOverviewMetaData = () => { const IconComponent = getFeatureTypeIcons(type); - const currentStage: LifecycleStage = { - name: 'completed', - status: 'kept', - environments: [ - { name: 'production', lastSeenAt: new Date().toISOString() }, - { - name: 'staging', - lastSeenAt: new Date().toISOString(), - }, - ], - }; + const currentStage = populateCurrentStage(feature); return ( @@ -122,11 +112,16 @@ const FeatureOverviewMetaData = () => { show={ Lifecycle: - - - + > + + + )} } /> diff --git a/frontend/src/interfaces/featureToggle.ts b/frontend/src/interfaces/featureToggle.ts index f05dfb2a1d..b10b5f2ac8 100644 --- a/frontend/src/interfaces/featureToggle.ts +++ b/frontend/src/interfaces/featureToggle.ts @@ -49,6 +49,10 @@ export interface IFeatureToggle { impressionData: boolean; strategies?: IFeatureStrategy[]; dependencies: Array; + lifecycle?: { + stage: 'initial' | 'pre-live' | 'live' | 'completed' | 'archived'; + enteredStageAt: string; + }; children: Array; }