mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: add usage of segment in list (#3853)
This commit is contained in:
		
							parent
							
								
									49722d5c48
								
							
						
					
					
						commit
						f73d36fda3
					
				| @ -11,7 +11,13 @@ import { | ||||
| import { useTable, useGlobalFilter, useSortBy } from 'react-table'; | ||||
| import { CreateSegmentButton } from 'component/segments/CreateSegmentButton/CreateSegmentButton'; | ||||
| import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; | ||||
| import { useMediaQuery } from '@mui/material'; | ||||
| import { | ||||
|     Box, | ||||
|     Checkbox, | ||||
|     styled, | ||||
|     Typography, | ||||
|     useMediaQuery, | ||||
| } from '@mui/material'; | ||||
| import { sortTypes } from 'utils/sortTypes'; | ||||
| import { useSegments } from 'hooks/api/getters/useSegments/useSegments'; | ||||
| import { useMemo, useState } from 'react'; | ||||
| @ -28,10 +34,13 @@ import { Search } from 'component/common/Search/Search'; | ||||
| import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns'; | ||||
| import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; | ||||
| import { useOptionalPathParam } from 'hooks/useOptionalPathParam'; | ||||
| import { RowSelectCell } from '../project/Project/ProjectFeatureToggles/RowSelectCell/RowSelectCell'; | ||||
| import useUiConfig from '../../hooks/api/getters/useUiConfig/useUiConfig'; | ||||
| 
 | ||||
| export const SegmentTable = () => { | ||||
|     const projectId = useOptionalPathParam('projectId'); | ||||
|     const { segments, loading } = useSegments(); | ||||
|     const { uiConfig } = useUiConfig(); | ||||
|     const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); | ||||
|     const [initialState] = useState({ | ||||
|         sortBy: [{ id: 'createdAt' }], | ||||
| @ -56,6 +65,10 @@ export const SegmentTable = () => { | ||||
|         return segments; | ||||
|     }, [segments, projectId]); | ||||
| 
 | ||||
|     const columns = useMemo( | ||||
|         () => getColumns(uiConfig.flags.segmentContextFieldUsage), | ||||
|         [uiConfig.flags.segmentContextFieldUsage] | ||||
|     ); | ||||
|     const { | ||||
|         getTableProps, | ||||
|         getTableBodyProps, | ||||
| @ -68,7 +81,7 @@ export const SegmentTable = () => { | ||||
|     } = useTable( | ||||
|         { | ||||
|             initialState, | ||||
|             columns: COLUMNS as any, | ||||
|             columns: columns as any, | ||||
|             data: data as any, | ||||
|             sortTypes, | ||||
|             autoResetGlobalFilter: false, | ||||
| @ -95,7 +108,7 @@ export const SegmentTable = () => { | ||||
|             }, | ||||
|         ], | ||||
|         setHiddenColumns, | ||||
|         COLUMNS | ||||
|         columns | ||||
|     ); | ||||
| 
 | ||||
|     return ( | ||||
| @ -170,7 +183,7 @@ export const SegmentTable = () => { | ||||
|     ); | ||||
| }; | ||||
| 
 | ||||
| const COLUMNS = [ | ||||
| const getColumns = (segmentContextFieldUsage?: boolean) => [ | ||||
|     { | ||||
|         id: 'Icon', | ||||
|         width: '1%', | ||||
| @ -182,10 +195,41 @@ const COLUMNS = [ | ||||
|         Header: 'Name', | ||||
|         accessor: 'name', | ||||
|         width: '60%', | ||||
|         Cell: ({ value, row: { original } }: any) => ( | ||||
|             <HighlightCell value={value} subtitle={original.description} /> | ||||
|         Cell: ({ | ||||
|             row: { | ||||
|                 original: { name, description, id }, | ||||
|             }, | ||||
|         }: any) => ( | ||||
|             <LinkCell | ||||
|                 title={name} | ||||
|                 to={`/segments/edit/${id}`} | ||||
|                 subtitle={description} | ||||
|             /> | ||||
|         ), | ||||
|     }, | ||||
|     ...(segmentContextFieldUsage | ||||
|         ? [ | ||||
|               { | ||||
|                   Header: 'Used in', | ||||
|                   width: '60%', | ||||
|                   Cell: ({ value, row: { original } }: any) => ( | ||||
|                       <TextCell | ||||
|                           sx={{ | ||||
|                               color: | ||||
|                                   original.usedInProjects === 0 && | ||||
|                                   original.usedInFeatures === 0 | ||||
|                                       ? theme.palette.text.disabled | ||||
|                                       : 'inherit', | ||||
|                           }} | ||||
|                       > | ||||
|                           <Box>{original.usedInProjects} projects</Box> | ||||
|                           <Box>{original.usedInFeatures} feature toggles</Box> | ||||
|                       </TextCell> | ||||
|                   ), | ||||
|               }, | ||||
|           ] | ||||
|         : []), | ||||
| 
 | ||||
|     { | ||||
|         Header: 'Project', | ||||
|         accessor: 'project', | ||||
|  | ||||
| @ -51,6 +51,7 @@ export interface IFlags { | ||||
|     variantMetrics?: boolean; | ||||
|     strategyImprovements?: boolean; | ||||
|     disableBulkToggle?: boolean; | ||||
|     segmentContextFieldUsage?: boolean; | ||||
| } | ||||
| 
 | ||||
| export interface IVersionInfo { | ||||
|  | ||||
| @ -90,6 +90,7 @@ exports[`should create default config 1`] = ` | ||||
|       "personalAccessTokensKillSwitch": false, | ||||
|       "proPlanAutoCharge": false, | ||||
|       "responseTimeWithAppNameKillSwitch": false, | ||||
|       "segmentContextFieldUsage": false, | ||||
|       "strategyImprovements": false, | ||||
|       "strictSchemaValidation": false, | ||||
|       "variantMetrics": false, | ||||
| @ -120,6 +121,7 @@ exports[`should create default config 1`] = ` | ||||
|       "personalAccessTokensKillSwitch": false, | ||||
|       "proPlanAutoCharge": false, | ||||
|       "responseTimeWithAppNameKillSwitch": false, | ||||
|       "segmentContextFieldUsage": false, | ||||
|       "strategyImprovements": false, | ||||
|       "strictSchemaValidation": false, | ||||
|       "variantMetrics": false, | ||||
|  | ||||
| @ -99,7 +99,12 @@ export const createStores = ( | ||||
|         ), | ||||
|         userSplashStore: new UserSplashStore(db, eventBus, getLogger), | ||||
|         roleStore: new RoleStore(db, eventBus, getLogger), | ||||
|         segmentStore: new SegmentStore(db, eventBus, getLogger), | ||||
|         segmentStore: new SegmentStore( | ||||
|             db, | ||||
|             eventBus, | ||||
|             getLogger, | ||||
|             config.flagResolver, | ||||
|         ), | ||||
|         groupStore: new GroupStore(db), | ||||
|         publicSignupTokenStore: new PublicSignupTokenStore( | ||||
|             db, | ||||
|  | ||||
| @ -6,6 +6,7 @@ import NotFoundError from '../error/notfound-error'; | ||||
| import { PartialSome } from '../types/partial'; | ||||
| import User from '../types/user'; | ||||
| import { Db } from './db'; | ||||
| import { IFlagResolver } from '../types'; | ||||
| 
 | ||||
| const T = { | ||||
|     segments: 'segments', | ||||
| @ -29,7 +30,9 @@ interface ISegmentRow { | ||||
|     description?: string; | ||||
|     segment_project_id?: string; | ||||
|     created_by?: string; | ||||
|     created_at?: Date; | ||||
|     created_at: Date; | ||||
|     used_in_projects?: number; | ||||
|     used_in_features?: number; | ||||
|     constraints: IConstraint[]; | ||||
| } | ||||
| 
 | ||||
| @ -46,9 +49,17 @@ export default class SegmentStore implements ISegmentStore { | ||||
| 
 | ||||
|     private db: Db; | ||||
| 
 | ||||
|     constructor(db: Db, eventBus: EventEmitter, getLogger: LogProvider) { | ||||
|     private flagResolver: IFlagResolver; | ||||
| 
 | ||||
|     constructor( | ||||
|         db: Db, | ||||
|         eventBus: EventEmitter, | ||||
|         getLogger: LogProvider, | ||||
|         flagResolver: IFlagResolver, | ||||
|     ) { | ||||
|         this.db = db; | ||||
|         this.eventBus = eventBus; | ||||
|         this.flagResolver = flagResolver; | ||||
|         this.logger = getLogger('lib/db/segment-store.ts'); | ||||
|     } | ||||
| 
 | ||||
| @ -96,12 +107,42 @@ export default class SegmentStore implements ISegmentStore { | ||||
|     } | ||||
| 
 | ||||
|     async getAll(): Promise<ISegment[]> { | ||||
|         const rows: ISegmentRow[] = await this.db | ||||
|             .select(this.prefixColumns()) | ||||
|             .from(T.segments) | ||||
|             .orderBy('name', 'asc'); | ||||
|         if (this.flagResolver.isEnabled('segmentContextFieldUsage')) { | ||||
|             const rows: ISegmentRow[] = await this.db | ||||
|                 .select( | ||||
|                     this.prefixColumns(), | ||||
|                     'used_in_projects', | ||||
|                     'used_in_features', | ||||
|                 ) | ||||
|                 .countDistinct( | ||||
|                     `${T.featureStrategies}.project_name AS used_in_projects`, | ||||
|                 ) | ||||
|                 .countDistinct( | ||||
|                     `${T.featureStrategies}.feature_name AS used_in_features`, | ||||
|                 ) | ||||
|                 .from(T.segments) | ||||
|                 .leftJoin( | ||||
|                     T.featureStrategySegment, | ||||
|                     `${T.segments}.id`, | ||||
|                     `${T.featureStrategySegment}.segment_id`, | ||||
|                 ) | ||||
|                 .leftJoin( | ||||
|                     T.featureStrategies, | ||||
|                     `${T.featureStrategies}.id`, | ||||
|                     `${T.featureStrategySegment}.feature_strategy_id`, | ||||
|                 ) | ||||
|                 .groupBy(this.prefixColumns()) | ||||
|                 .orderBy('name', 'asc'); | ||||
| 
 | ||||
|         return rows.map(this.mapRow); | ||||
|             return rows.map(this.mapRow); | ||||
|         } else { | ||||
|             const rows: ISegmentRow[] = await this.db | ||||
|                 .select(this.prefixColumns()) | ||||
|                 .from(T.segments) | ||||
|                 .orderBy('name', 'asc'); | ||||
| 
 | ||||
|             return rows.map(this.mapRow); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async getActive(): Promise<ISegment[]> { | ||||
| @ -199,7 +240,7 @@ export default class SegmentStore implements ISegmentStore { | ||||
|             throw new NotFoundError('No row'); | ||||
|         } | ||||
| 
 | ||||
|         return { | ||||
|         const segment: ISegment = { | ||||
|             id: row.id, | ||||
|             name: row.name, | ||||
|             description: row.description, | ||||
| @ -207,7 +248,15 @@ export default class SegmentStore implements ISegmentStore { | ||||
|             constraints: row.constraints, | ||||
|             createdBy: row.created_by, | ||||
|             createdAt: row.created_at, | ||||
|             usedInProjects: row.used_in_projects | ||||
|                 ? Number(row.used_in_projects) | ||||
|                 : 0, | ||||
|             usedInFeatures: row.used_in_projects | ||||
|                 ? Number(row.used_in_features) | ||||
|                 : 0, | ||||
|         }; | ||||
| 
 | ||||
|         return segment; | ||||
|     } | ||||
| 
 | ||||
|     destroy(): void {} | ||||
|  | ||||
| @ -118,7 +118,12 @@ export const createExportImportTogglesService = ( | ||||
|     const featureToggleStore = new FeatureToggleStore(db, eventBus, getLogger); | ||||
|     const tagStore = new TagStore(db, eventBus, getLogger); | ||||
|     const tagTypeStore = new TagTypeStore(db, eventBus, getLogger); | ||||
|     const segmentStore = new SegmentStore(db, eventBus, getLogger); | ||||
|     const segmentStore = new SegmentStore( | ||||
|         db, | ||||
|         eventBus, | ||||
|         getLogger, | ||||
|         flagResolver, | ||||
|     ); | ||||
|     const projectStore = new ProjectStore( | ||||
|         db, | ||||
|         eventBus, | ||||
|  | ||||
| @ -68,7 +68,12 @@ export const createFeatureToggleService = ( | ||||
|         eventBus, | ||||
|         getLogger, | ||||
|     ); | ||||
|     const segmentStore = new SegmentStore(db, eventBus, getLogger); | ||||
|     const segmentStore = new SegmentStore( | ||||
|         db, | ||||
|         eventBus, | ||||
|         getLogger, | ||||
|         flagResolver, | ||||
|     ); | ||||
|     const contextFieldStore = new ContextFieldStore(db, getLogger); | ||||
|     const groupStore = new GroupStore(db); | ||||
|     const accountStore = new AccountStore(db, getLogger); | ||||
|  | ||||
| @ -21,7 +21,8 @@ export type IFlagKey = | ||||
|     | 'strategyImprovements' | ||||
|     | 'googleAuthEnabled' | ||||
|     | 'variantMetrics' | ||||
|     | 'disableBulkToggle'; | ||||
|     | 'disableBulkToggle' | ||||
|     | 'segmentContextFieldUsage'; | ||||
| 
 | ||||
| export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>; | ||||
| 
 | ||||
| @ -98,6 +99,10 @@ const flags: IFlags = { | ||||
|         process.env.DISABLE_BULK_TOGGLE, | ||||
|         false, | ||||
|     ), | ||||
|     segmentContextFieldUsage: parseEnvVarBoolean( | ||||
|         process.env.UNLEASH_SSEGMENT_CONTEXT_FIELD_USAGE, | ||||
|         false, | ||||
|     ), | ||||
| }; | ||||
| 
 | ||||
| export const defaultExperimentalOptions: IExperimentalOptions = { | ||||
|  | ||||
| @ -402,6 +402,8 @@ export interface ISegment { | ||||
|     description?: string; | ||||
|     project?: string; | ||||
|     constraints: IConstraint[]; | ||||
|     usedInProjects?: number; | ||||
|     usedInFeatures?: number; | ||||
|     createdBy?: string; | ||||
|     createdAt: Date; | ||||
| } | ||||
|  | ||||
| @ -40,6 +40,7 @@ process.nextTick(async () => { | ||||
|                         responseTimeWithAppNameKillSwitch: false, | ||||
|                         variantMetrics: true, | ||||
|                         strategyImprovements: true, | ||||
|                         segmentContextFieldUsage: true, | ||||
|                     }, | ||||
|                 }, | ||||
|                 authentication: { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user