mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-23 00:16:25 +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