mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: flag lifecycle status - first pass (#9736)
Resolving "status" column for flags overview page
This commit is contained in:
		
							parent
							
								
									67c0ffa1ab
								
							
						
					
					
						commit
						1043eb8f03
					
				| @ -41,6 +41,7 @@ import { LifecycleFilters } from './FeatureToggleFilters/LifecycleFilters'; | |||||||
| import { ExportFlags } from './ExportFlags'; | import { ExportFlags } from './ExportFlags'; | ||||||
| import { createFeatureOverviewCell } from 'component/common/Table/cells/FeatureOverviewCell/FeatureOverviewCell'; | import { createFeatureOverviewCell } from 'component/common/Table/cells/FeatureOverviewCell/FeatureOverviewCell'; | ||||||
| import { AvatarCell } from 'component/project/Project/PaginatedProjectFeatureToggles/AvatarCell'; | import { AvatarCell } from 'component/project/Project/PaginatedProjectFeatureToggles/AvatarCell'; | ||||||
|  | import { StatusCell } from './StatusCell/StatusCell'; | ||||||
| 
 | 
 | ||||||
| export const featuresPlaceholder = Array(15).fill({ | export const featuresPlaceholder = Array(15).fill({ | ||||||
|     name: 'Name of the feature', |     name: 'Name of the feature', | ||||||
| @ -175,7 +176,6 @@ export const FeatureToggleListTable: FC = () => { | |||||||
|                           meta: { width: '1%', align: 'center' }, |                           meta: { width: '1%', align: 'center' }, | ||||||
|                           enableSorting: false, |                           enableSorting: false, | ||||||
|                       }), |                       }), | ||||||
| 
 |  | ||||||
|                       columnHelper.accessor('lifecycle', { |                       columnHelper.accessor('lifecycle', { | ||||||
|                           id: 'lifecycle', |                           id: 'lifecycle', | ||||||
|                           header: 'Lifecycle', |                           header: 'Lifecycle', | ||||||
| @ -190,6 +190,15 @@ export const FeatureToggleListTable: FC = () => { | |||||||
|                           size: 50, |                           size: 50, | ||||||
|                           meta: { width: '1%' }, |                           meta: { width: '1%' }, | ||||||
|                       }), |                       }), | ||||||
|  |                       columnHelper.accessor('environments', { | ||||||
|  |                           id: 'status', | ||||||
|  |                           header: 'Status', | ||||||
|  |                           cell: ({ row: { original } }) => ( | ||||||
|  |                               <StatusCell {...original} /> | ||||||
|  |                           ), | ||||||
|  |                           enableSorting: false, | ||||||
|  |                           size: 50, | ||||||
|  |                       }), | ||||||
|                       columnHelper.accessor('project', { |                       columnHelper.accessor('project', { | ||||||
|                           header: 'Project', |                           header: 'Project', | ||||||
|                           cell: ({ getValue }) => { |                           cell: ({ getValue }) => { | ||||||
|  | |||||||
| @ -0,0 +1,20 @@ | |||||||
|  | import { type FC, useMemo } from 'react'; | ||||||
|  | import type { FeatureSearchResponseSchema } from 'openapi'; | ||||||
|  | import { styled } from '@mui/material'; | ||||||
|  | import { getStatus } from './getStatus'; | ||||||
|  | 
 | ||||||
|  | const Container = styled('div')(({ theme }) => ({ | ||||||
|  |     padding: theme.spacing(0, 2), | ||||||
|  | })); | ||||||
|  | 
 | ||||||
|  | export const StatusCell: FC<FeatureSearchResponseSchema> = ({ | ||||||
|  |     lifecycle, | ||||||
|  |     environments, | ||||||
|  | }) => { | ||||||
|  |     const status = useMemo( | ||||||
|  |         () => getStatus({ lifecycle, environments }), | ||||||
|  |         [lifecycle, environments], | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     return <Container>{status}</Container>; | ||||||
|  | }; | ||||||
| @ -0,0 +1,151 @@ | |||||||
|  | import { getStatus } from './getStatus'; | ||||||
|  | import { PRODUCTION } from 'constants/environmentTypes'; | ||||||
|  | import type { FeatureSearchEnvironmentSchema } from 'openapi'; | ||||||
|  | 
 | ||||||
|  | const prodEnvEnabled: FeatureSearchEnvironmentSchema = { | ||||||
|  |     name: 'production', | ||||||
|  |     enabled: true, | ||||||
|  |     type: PRODUCTION, | ||||||
|  |     sortOrder: 1, | ||||||
|  |     variantCount: 0, | ||||||
|  |     lastSeenAt: null, | ||||||
|  |     hasStrategies: true, | ||||||
|  |     hasEnabledStrategies: true, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const prodEnvDisabled: FeatureSearchEnvironmentSchema = { | ||||||
|  |     ...prodEnvEnabled, | ||||||
|  |     enabled: false, | ||||||
|  |     hasEnabledStrategies: false, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const prodEnvDisabledNoStrategies: FeatureSearchEnvironmentSchema = { | ||||||
|  |     ...prodEnvDisabled, | ||||||
|  |     hasStrategies: false, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const devEnvEnabled: FeatureSearchEnvironmentSchema = { | ||||||
|  |     name: 'development', | ||||||
|  |     enabled: true, | ||||||
|  |     type: 'development', | ||||||
|  |     sortOrder: 0, | ||||||
|  |     variantCount: 0, | ||||||
|  |     lastSeenAt: null, | ||||||
|  |     hasStrategies: true, | ||||||
|  |     hasEnabledStrategies: true, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const devEnvDisabledWithStrategies: FeatureSearchEnvironmentSchema = { | ||||||
|  |     ...devEnvEnabled, | ||||||
|  |     enabled: false, | ||||||
|  |     hasEnabledStrategies: false, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const devEnvDisabledNoStrategies: FeatureSearchEnvironmentSchema = { | ||||||
|  |     ...devEnvDisabledWithStrategies, | ||||||
|  |     hasStrategies: false, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | describe('getStatus', () => { | ||||||
|  |     describe("lifecycle 'define' stage", () => { | ||||||
|  |         it('has no strategies', () => { | ||||||
|  |             expect( | ||||||
|  |                 getStatus({ | ||||||
|  |                     environments: [devEnvDisabledNoStrategies, prodEnvDisabled], | ||||||
|  |                     lifecycle: { | ||||||
|  |                         stage: 'initial', | ||||||
|  |                         enteredStageAt: null as any, | ||||||
|  |                     }, | ||||||
|  |                 }), | ||||||
|  |             ).toBe('No strategies'); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         it('has no enabled strategies', () => { | ||||||
|  |             expect( | ||||||
|  |                 getStatus({ | ||||||
|  |                     environments: [ | ||||||
|  |                         devEnvDisabledWithStrategies, | ||||||
|  |                         prodEnvDisabled, | ||||||
|  |                     ], | ||||||
|  |                     lifecycle: { | ||||||
|  |                         stage: 'initial', | ||||||
|  |                         enteredStageAt: null as any, | ||||||
|  |                     }, | ||||||
|  |                 }), | ||||||
|  |             ).toBe('No enabled strategies'); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         it('has a non-production environment enabled', () => { | ||||||
|  |             expect( | ||||||
|  |                 getStatus({ | ||||||
|  |                     environments: [devEnvEnabled], | ||||||
|  |                     lifecycle: { | ||||||
|  |                         stage: 'initial', | ||||||
|  |                         enteredStageAt: null as any, | ||||||
|  |                     }, | ||||||
|  |                 }), | ||||||
|  |             ).toBe('No traffic'); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     describe("lifecycle 'develop' stage", () => { | ||||||
|  |         it('is paused', () => { | ||||||
|  |             expect( | ||||||
|  |                 getStatus({ | ||||||
|  |                     environments: [ | ||||||
|  |                         devEnvDisabledWithStrategies, | ||||||
|  |                         prodEnvDisabled, | ||||||
|  |                     ], | ||||||
|  |                     lifecycle: { | ||||||
|  |                         stage: 'pre-live', | ||||||
|  |                         enteredStageAt: null as any, | ||||||
|  |                     }, | ||||||
|  |                 }), | ||||||
|  |             ).toBe('Paused'); | ||||||
|  |             expect( | ||||||
|  |                 getStatus({ | ||||||
|  |                     environments: [devEnvEnabled, prodEnvDisabled], | ||||||
|  |                     lifecycle: { | ||||||
|  |                         stage: 'pre-live', | ||||||
|  |                         enteredStageAt: null as any, | ||||||
|  |                     }, | ||||||
|  |                 }), | ||||||
|  |             ).not.toBe('Paused'); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     describe("lifecycle 'production' stage", () => { | ||||||
|  |         it('has no production environments', () => { | ||||||
|  |             expect( | ||||||
|  |                 getStatus({ | ||||||
|  |                     environments: [devEnvEnabled], | ||||||
|  |                     lifecycle: { stage: 'live', enteredStageAt: null as any }, | ||||||
|  |                 }), | ||||||
|  |             ).toBe('No production environments'); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         it('is paused', () => { | ||||||
|  |             expect( | ||||||
|  |                 getStatus({ | ||||||
|  |                     environments: [prodEnvDisabled], | ||||||
|  |                     lifecycle: { stage: 'live', enteredStageAt: null as any }, | ||||||
|  |                 }), | ||||||
|  |             ).toBe('Paused'); | ||||||
|  |             expect( | ||||||
|  |                 getStatus({ | ||||||
|  |                     environments: [prodEnvEnabled], | ||||||
|  |                     lifecycle: { stage: 'live', enteredStageAt: null as any }, | ||||||
|  |                 }), | ||||||
|  |             ).not.toBe('Paused'); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         it('has no produciton strategies', () => { | ||||||
|  |             expect( | ||||||
|  |                 getStatus({ | ||||||
|  |                     environments: [prodEnvDisabledNoStrategies], | ||||||
|  |                     lifecycle: { stage: 'live', enteredStageAt: null as any }, | ||||||
|  |                 }), | ||||||
|  |             ).toBe('No strategies'); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  | }); | ||||||
| @ -0,0 +1,66 @@ | |||||||
|  | import { PRODUCTION } from 'constants/environmentTypes'; | ||||||
|  | import type { FeatureSearchResponseSchema } from 'openapi'; | ||||||
|  | 
 | ||||||
|  | export const getStatus = ({ | ||||||
|  |     lifecycle, | ||||||
|  |     environments, | ||||||
|  | }: Pick< | ||||||
|  |     FeatureSearchResponseSchema, | ||||||
|  |     'lifecycle' | 'environments' | 'lastSeenAt' | ||||||
|  | >) => { | ||||||
|  |     if (lifecycle?.stage === 'initial') { | ||||||
|  |         if ( | ||||||
|  |             environments?.some((env) => env.type !== PRODUCTION && env.enabled) | ||||||
|  |         ) { | ||||||
|  |             return 'No traffic'; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if ( | ||||||
|  |             !environments?.some( | ||||||
|  |                 (env) => env.type !== PRODUCTION && env.hasStrategies, | ||||||
|  |             ) | ||||||
|  |         ) { | ||||||
|  |             return 'No strategies'; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if ( | ||||||
|  |             !environments?.some( | ||||||
|  |                 (env) => env.type !== PRODUCTION && env.hasEnabledStrategies, | ||||||
|  |             ) | ||||||
|  |         ) { | ||||||
|  |             return 'No enabled strategies'; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (lifecycle?.stage === 'pre-live') { | ||||||
|  |         if (!environments?.some((env) => env.enabled)) { | ||||||
|  |             return 'Paused'; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const productionEnvironment = environments?.find( | ||||||
|  |         (env) => env.type === PRODUCTION, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     if (lifecycle?.stage === 'live') { | ||||||
|  |         if (!productionEnvironment) { | ||||||
|  |             return 'No production environments'; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!productionEnvironment?.hasStrategies) { | ||||||
|  |             return 'No strategies'; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!productionEnvironment?.enabled) { | ||||||
|  |             return 'Paused'; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (lifecycle?.stage === 'completed') { | ||||||
|  |         if (productionEnvironment?.enabled) { | ||||||
|  |             return 'Enabled'; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return '–'; | ||||||
|  | }; | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user