Skip to content

Commit

Permalink
feat: connect feature lifecycle to real API (#6921)
Browse files Browse the repository at this point in the history
  • Loading branch information
kwasniew authored Apr 25, 2024
1 parent 1b2f983 commit e6355f4
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -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);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { IFeatureToggle } from 'interfaces/featureToggle';
import type { LifecycleStage } from './LifecycleStage';

export const populateCurrentStage = (
feature: Pick<IFeatureToggle, 'lifecycle' | 'environments'>,
): 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;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 (
<StyledContainer>
Expand Down Expand Up @@ -122,11 +112,16 @@ const FeatureOverviewMetaData = () => {
show={
<StyledRow data-loading>
<StyledLabel>Lifecycle:</StyledLabel>
<FeatureLifecycleTooltip stage={currentStage}>
<FeatureLifecycleStageIcon

{currentStage && (
<FeatureLifecycleTooltip
stage={currentStage}
/>
</FeatureLifecycleTooltip>
>
<FeatureLifecycleStageIcon
stage={currentStage}
/>
</FeatureLifecycleTooltip>
)}
</StyledRow>
}
/>
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/interfaces/featureToggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ export interface IFeatureToggle {
impressionData: boolean;
strategies?: IFeatureStrategy[];
dependencies: Array<IDependency>;
lifecycle?: {
stage: 'initial' | 'pre-live' | 'live' | 'completed' | 'archived';
enteredStageAt: string;
};
children: Array<string>;
}

Expand Down

0 comments on commit e6355f4

Please sign in to comment.