1
0
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:
Jaanus Sellin 2023-05-26 14:37:00 +03:00 committed by GitHub
parent 49722d5c48
commit f73d36fda3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 137 additions and 18 deletions

View File

@ -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',

View File

@ -51,6 +51,7 @@ export interface IFlags {
variantMetrics?: boolean;
strategyImprovements?: boolean;
disableBulkToggle?: boolean;
segmentContextFieldUsage?: boolean;
}
export interface IVersionInfo {

View File

@ -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,

View File

@ -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,

View File

@ -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 {}

View File

@ -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,

View File

@ -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);

View File

@ -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 = {

View File

@ -402,6 +402,8 @@ export interface ISegment {
description?: string;
project?: string;
constraints: IConstraint[];
usedInProjects?: number;
usedInFeatures?: number;
createdBy?: string;
createdAt: Date;
}

View File

@ -40,6 +40,7 @@ process.nextTick(async () => {
responseTimeWithAppNameKillSwitch: false,
variantMetrics: true,
strategyImprovements: true,
segmentContextFieldUsage: true,
},
},
authentication: {