mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-31 13:47:02 +02:00
chore: remove featureLifecycle and featureLifecycleMetrics flags (#7808)
This commit is contained in:
parent
fffed5d8dc
commit
b65e593c23
@ -7,7 +7,6 @@ import Edit from '@mui/icons-material/Edit';
|
|||||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||||
import { UPDATE_FEATURE } from 'component/providers/AccessProvider/permissions';
|
import { UPDATE_FEATURE } from 'component/providers/AccessProvider/permissions';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
|
||||||
import { FeatureArchiveDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveDialog';
|
import { FeatureArchiveDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveDialog';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { FeatureArchiveNotAllowedDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveNotAllowedDialog';
|
import { FeatureArchiveNotAllowedDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveNotAllowedDialog';
|
||||||
@ -98,7 +97,6 @@ const FeatureOverviewMetaData = () => {
|
|||||||
const featureId = useRequiredPathParam('featureId');
|
const featureId = useRequiredPathParam('featureId');
|
||||||
const { feature, refetchFeature } = useFeature(projectId, featureId);
|
const { feature, refetchFeature } = useFeature(projectId, featureId);
|
||||||
const { project, description, type } = feature;
|
const { project, description, type } = feature;
|
||||||
const featureLifecycleEnabled = useUiFlag('featureLifecycle');
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [showDelDialog, setShowDelDialog] = useState(false);
|
const [showDelDialog, setShowDelDialog] = useState(false);
|
||||||
const [showMarkCompletedDialogue, setShowMarkCompletedDialogue] =
|
const [showMarkCompletedDialogue, setShowMarkCompletedDialogue] =
|
||||||
@ -142,10 +140,7 @@ const FeatureOverviewMetaData = () => {
|
|||||||
<Box sx={{ wordBreak: 'break-all' }}>{project}</Box>
|
<Box sx={{ wordBreak: 'break-all' }}>{project}</Box>
|
||||||
</SpacedBodyItem>
|
</SpacedBodyItem>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={
|
condition={Boolean(feature.lifecycle)}
|
||||||
featureLifecycleEnabled &&
|
|
||||||
Boolean(feature.lifecycle)
|
|
||||||
}
|
|
||||||
show={
|
show={
|
||||||
<SpacedBodyItem data-loading>
|
<SpacedBodyItem data-loading>
|
||||||
<StyledLabel>Lifecycle:</StyledLabel>
|
<StyledLabel>Lifecycle:</StyledLabel>
|
||||||
|
@ -34,7 +34,6 @@ import { TableEmptyState } from './TableEmptyState/TableEmptyState';
|
|||||||
import { useRowActions } from './hooks/useRowActions';
|
import { useRowActions } from './hooks/useRowActions';
|
||||||
import { useSelectedData } from './hooks/useSelectedData';
|
import { useSelectedData } from './hooks/useSelectedData';
|
||||||
import { FeatureOverviewCell } from '../../../common/Table/cells/FeatureOverviewCell/FeatureOverviewCell';
|
import { FeatureOverviewCell } from '../../../common/Table/cells/FeatureOverviewCell/FeatureOverviewCell';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
|
||||||
import {
|
import {
|
||||||
useProjectFeatureSearch,
|
useProjectFeatureSearch,
|
||||||
useProjectFeatureSearchActions,
|
useProjectFeatureSearchActions,
|
||||||
@ -55,7 +54,6 @@ export const ProjectFeatureToggles = ({
|
|||||||
environments,
|
environments,
|
||||||
}: IPaginatedProjectFeatureTogglesProps) => {
|
}: IPaginatedProjectFeatureTogglesProps) => {
|
||||||
const projectId = useRequiredPathParam('projectId');
|
const projectId = useRequiredPathParam('projectId');
|
||||||
const featureLifecycleEnabled = useUiFlag('featureLifecycle');
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
features,
|
features,
|
||||||
@ -192,36 +190,30 @@ export const ProjectFeatureToggles = ({
|
|||||||
width: '1%',
|
width: '1%',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
...(featureLifecycleEnabled
|
columnHelper.accessor('lifecycle', {
|
||||||
? [
|
id: 'lifecycle',
|
||||||
columnHelper.accessor('lifecycle', {
|
header: 'Lifecycle',
|
||||||
id: 'lifecycle',
|
cell: ({ row: { original } }) => (
|
||||||
header: 'Lifecycle',
|
<FeatureLifecycleCell
|
||||||
cell: ({ row: { original } }) => (
|
feature={original}
|
||||||
<FeatureLifecycleCell
|
onComplete={() => {
|
||||||
feature={original}
|
setShowMarkCompletedDialogue({
|
||||||
onComplete={() => {
|
featureId: original.name,
|
||||||
setShowMarkCompletedDialogue({
|
open: true,
|
||||||
featureId: original.name,
|
});
|
||||||
open: true,
|
}}
|
||||||
});
|
onUncomplete={refetch}
|
||||||
}}
|
onArchive={() => setFeatureArchiveState(original.name)}
|
||||||
onUncomplete={refetch}
|
data-loading
|
||||||
onArchive={() =>
|
/>
|
||||||
setFeatureArchiveState(original.name)
|
),
|
||||||
}
|
enableSorting: false,
|
||||||
data-loading
|
size: 50,
|
||||||
/>
|
meta: {
|
||||||
),
|
align: 'center',
|
||||||
enableSorting: false,
|
width: '1%',
|
||||||
size: 50,
|
},
|
||||||
meta: {
|
}),
|
||||||
align: 'center',
|
|
||||||
width: '1%',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
...environments.map((name: string) => {
|
...environments.map((name: string) => {
|
||||||
const isChangeRequestEnabled = isChangeRequestConfigured(name);
|
const isChangeRequestEnabled = isChangeRequestConfigured(name);
|
||||||
|
|
||||||
@ -302,7 +294,6 @@ export const ProjectFeatureToggles = ({
|
|||||||
tableState.favoritesFirst,
|
tableState.favoritesFirst,
|
||||||
refetch,
|
refetch,
|
||||||
isPlaceholder,
|
isPlaceholder,
|
||||||
featureLifecycleEnabled,
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -430,16 +421,11 @@ export const ProjectFeatureToggles = ({
|
|||||||
id: 'lastSeenAt',
|
id: 'lastSeenAt',
|
||||||
isVisible: columnVisibility.lastSeenAt,
|
isVisible: columnVisibility.lastSeenAt,
|
||||||
},
|
},
|
||||||
...(featureLifecycleEnabled
|
{
|
||||||
? [
|
header: 'Lifecycle',
|
||||||
{
|
id: 'lifecycle',
|
||||||
header: 'Lifecycle',
|
isVisible: columnVisibility.lifecycle,
|
||||||
id: 'lifecycle',
|
},
|
||||||
isVisible:
|
|
||||||
columnVisibility.lifecycle,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
{
|
{
|
||||||
id: 'divider',
|
id: 'divider',
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useMediaQuery, useTheme } from '@mui/material';
|
import { useMediaQuery, useTheme } from '@mui/material';
|
||||||
import type { VisibilityState } from '@tanstack/react-table';
|
import type { VisibilityState } from '@tanstack/react-table';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
|
||||||
|
|
||||||
const staticColumns = ['select', 'actions', 'name', 'favorite'];
|
const staticColumns = ['select', 'actions', 'name', 'favorite'];
|
||||||
|
|
||||||
@ -22,7 +21,6 @@ export const useDefaultColumnVisibility = (allColumnIds: string[]) => {
|
|||||||
const isTinyScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
const isTinyScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
||||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
||||||
const isMediumScreen = useMediaQuery(theme.breakpoints.down('lg'));
|
const isMediumScreen = useMediaQuery(theme.breakpoints.down('lg'));
|
||||||
const featureLifecycleEnabled = useUiFlag('featureLifecycle');
|
|
||||||
|
|
||||||
const showEnvironments = useCallback(
|
const showEnvironments = useCallback(
|
||||||
(environmentsToShow: number = 0) =>
|
(environmentsToShow: number = 0) =>
|
||||||
@ -55,7 +53,7 @@ export const useDefaultColumnVisibility = (allColumnIds: string[]) => {
|
|||||||
return formatAsColumnVisibility(allColumnIds, [
|
return formatAsColumnVisibility(allColumnIds, [
|
||||||
...staticColumns,
|
...staticColumns,
|
||||||
'lastSeenAt',
|
'lastSeenAt',
|
||||||
...(featureLifecycleEnabled ? ['lifecycle'] : []),
|
'lifecycle',
|
||||||
'createdAt',
|
'createdAt',
|
||||||
'createdBy',
|
'createdBy',
|
||||||
'type',
|
'type',
|
||||||
|
@ -105,7 +105,6 @@ exports[`should create default config 1`] = `
|
|||||||
"extendedMetrics": false,
|
"extendedMetrics": false,
|
||||||
"extendedUsageMetrics": false,
|
"extendedUsageMetrics": false,
|
||||||
"featureCollaborators": false,
|
"featureCollaborators": false,
|
||||||
"featureLifecycle": false,
|
|
||||||
"featureSearchFeedback": {
|
"featureSearchFeedback": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"name": "withText",
|
"name": "withText",
|
||||||
|
@ -19,7 +19,6 @@ import {
|
|||||||
} from '../../openapi';
|
} from '../../openapi';
|
||||||
import Controller from '../../routes/controller';
|
import Controller from '../../routes/controller';
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
import { NotFoundError } from '../../error';
|
|
||||||
import type { IAuthRequest } from '../../routes/unleash-types';
|
import type { IAuthRequest } from '../../routes/unleash-types';
|
||||||
import type { WithTransactional } from '../../db/transaction';
|
import type { WithTransactional } from '../../db/transaction';
|
||||||
|
|
||||||
@ -120,9 +119,6 @@ export default class FeatureLifecycleController extends Controller {
|
|||||||
req: Request<FeatureLifecycleParams, any, any, any>,
|
req: Request<FeatureLifecycleParams, any, any, any>,
|
||||||
res: Response<FeatureLifecycleSchema>,
|
res: Response<FeatureLifecycleSchema>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!this.flagResolver.isEnabled('featureLifecycle')) {
|
|
||||||
throw new NotFoundError('Feature lifecycle is disabled.');
|
|
||||||
}
|
|
||||||
const { featureName } = req.params;
|
const { featureName } = req.params;
|
||||||
|
|
||||||
const result =
|
const result =
|
||||||
@ -144,9 +140,6 @@ export default class FeatureLifecycleController extends Controller {
|
|||||||
>,
|
>,
|
||||||
res: Response,
|
res: Response,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!this.flagResolver.isEnabled('featureLifecycle')) {
|
|
||||||
throw new NotFoundError('Feature lifecycle is disabled.');
|
|
||||||
}
|
|
||||||
const { featureName, projectId } = req.params;
|
const { featureName, projectId } = req.params;
|
||||||
|
|
||||||
const status = req.body;
|
const status = req.body;
|
||||||
@ -162,9 +155,6 @@ export default class FeatureLifecycleController extends Controller {
|
|||||||
req: IAuthRequest<FeatureLifecycleParams>,
|
req: IAuthRequest<FeatureLifecycleParams>,
|
||||||
res: Response,
|
res: Response,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!this.flagResolver.isEnabled('featureLifecycle')) {
|
|
||||||
throw new NotFoundError('Feature lifecycle is disabled.');
|
|
||||||
}
|
|
||||||
const { featureName, projectId } = req.params;
|
const { featureName, projectId } = req.params;
|
||||||
|
|
||||||
await this.featureLifecycleService.transactional((service) =>
|
await this.featureLifecycleService.transactional((service) =>
|
||||||
|
@ -36,10 +36,6 @@ export class FeatureLifecycleReadModel implements IFeatureLifecycleReadModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getStageCount(): Promise<StageCount[]> {
|
async getStageCount(): Promise<StageCount[]> {
|
||||||
if (!this.flagResolver.isEnabled('featureLifecycleMetrics')) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const { rows } = await this.db.raw(`
|
const { rows } = await this.db.raw(`
|
||||||
SELECT
|
SELECT
|
||||||
stage,
|
stage,
|
||||||
@ -65,10 +61,6 @@ export class FeatureLifecycleReadModel implements IFeatureLifecycleReadModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getStageCountByProject(): Promise<StageCountByProject[]> {
|
async getStageCountByProject(): Promise<StageCountByProject[]> {
|
||||||
if (!this.flagResolver.isEnabled('featureLifecycleMetrics')) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const { rows } = await this.db.raw(`
|
const { rows } = await this.db.raw(`
|
||||||
SELECT
|
SELECT
|
||||||
f.project,
|
f.project,
|
||||||
@ -133,10 +125,6 @@ export class FeatureLifecycleReadModel implements IFeatureLifecycleReadModel {
|
|||||||
public async getAllWithStageDuration(): Promise<
|
public async getAllWithStageDuration(): Promise<
|
||||||
IProjectLifecycleStageDuration[]
|
IProjectLifecycleStageDuration[]
|
||||||
> {
|
> {
|
||||||
if (!this.flagResolver.isEnabled('featureLifecycleMetrics')) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const featureLifeCycles = await this.getAll();
|
const featureLifeCycles = await this.getAll();
|
||||||
return calculateStageDurations(featureLifeCycles);
|
return calculateStageDurations(featureLifeCycles);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
CLIENT_METRICS_ADDED,
|
CLIENT_METRICS_ADDED,
|
||||||
FEATURE_ARCHIVED,
|
FEATURE_ARCHIVED,
|
||||||
FEATURE_COMPLETED,
|
|
||||||
FEATURE_CREATED,
|
FEATURE_CREATED,
|
||||||
FEATURE_REVIVED,
|
FEATURE_REVIVED,
|
||||||
type IEnvironment,
|
type IEnvironment,
|
||||||
@ -101,33 +100,3 @@ test('can insert and read lifecycle stages', async () => {
|
|||||||
{ stage: 'initial', enteredStageAt: expect.any(Date) },
|
{ stage: 'initial', enteredStageAt: expect.any(Date) },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ignores lifecycle state updates when flag disabled', async () => {
|
|
||||||
const eventBus = new EventEmitter();
|
|
||||||
const { featureLifecycleService, eventStore, environmentStore } =
|
|
||||||
createFakeFeatureLifecycleService({
|
|
||||||
flagResolver: { isEnabled: () => false },
|
|
||||||
eventBus,
|
|
||||||
getLogger: noLoggerProvider,
|
|
||||||
} as unknown as IUnleashConfig);
|
|
||||||
const featureName = 'testFeature';
|
|
||||||
|
|
||||||
await environmentStore.create({
|
|
||||||
name: 'my-dev-environment',
|
|
||||||
type: 'development',
|
|
||||||
} as IEnvironment);
|
|
||||||
featureLifecycleService.listen();
|
|
||||||
|
|
||||||
await eventStore.emit(FEATURE_CREATED, { featureName });
|
|
||||||
await eventStore.emit(FEATURE_COMPLETED, { featureName });
|
|
||||||
await eventBus.emit(CLIENT_METRICS_ADDED, {
|
|
||||||
bucket: { toggles: { [featureName]: 'irrelevant' } },
|
|
||||||
environment: 'development',
|
|
||||||
});
|
|
||||||
await eventStore.emit(FEATURE_ARCHIVED, { featureName });
|
|
||||||
|
|
||||||
const lifecycle =
|
|
||||||
await featureLifecycleService.getFeatureLifecycle(featureName);
|
|
||||||
|
|
||||||
expect(lifecycle).toEqual([]);
|
|
||||||
});
|
|
||||||
|
@ -77,19 +77,10 @@ export class FeatureLifecycleService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async checkEnabled(fn: () => Promise<void>) {
|
|
||||||
const enabled = this.flagResolver.isEnabled('featureLifecycle');
|
|
||||||
if (enabled) {
|
|
||||||
return fn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
listen() {
|
listen() {
|
||||||
void this.checkEnabled(() => this.featureLifecycleStore.backfill());
|
this.featureLifecycleStore.backfill();
|
||||||
this.eventStore.on(FEATURE_CREATED, async (event) => {
|
this.eventStore.on(FEATURE_CREATED, async (event) => {
|
||||||
await this.checkEnabled(() =>
|
await this.featureInitialized(event.featureName);
|
||||||
this.featureInitialized(event.featureName),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
this.eventBus.on(
|
this.eventBus.on(
|
||||||
CLIENT_METRICS_ADDED,
|
CLIENT_METRICS_ADDED,
|
||||||
@ -103,22 +94,19 @@ export class FeatureLifecycleService {
|
|||||||
const features = metrics.map(
|
const features = metrics.map(
|
||||||
(metric) => metric.featureName,
|
(metric) => metric.featureName,
|
||||||
);
|
);
|
||||||
await this.checkEnabled(() =>
|
await this.featuresReceivedMetrics(
|
||||||
this.featuresReceivedMetrics(features, environment),
|
features,
|
||||||
|
environment,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
this.eventStore.on(FEATURE_ARCHIVED, async (event) => {
|
this.eventStore.on(FEATURE_ARCHIVED, async (event) => {
|
||||||
await this.checkEnabled(() =>
|
await this.featureArchived(event.featureName);
|
||||||
this.featureArchived(event.featureName),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
this.eventStore.on(FEATURE_REVIVED, async (event) => {
|
this.eventStore.on(FEATURE_REVIVED, async (event) => {
|
||||||
await this.checkEnabled(() =>
|
await this.featureRevived(event.featureName);
|
||||||
this.featureRevived(event.featureName),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,9 +32,7 @@ beforeAll(async () => {
|
|||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
experimental: {
|
experimental: {
|
||||||
flags: {
|
flags: {},
|
||||||
featureLifecycle: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
db.rawDatabase,
|
db.rawDatabase,
|
||||||
|
@ -11,7 +11,6 @@ import type {
|
|||||||
} from '../../types';
|
} from '../../types';
|
||||||
import FeatureToggleStore from '../feature-toggle/feature-toggle-store';
|
import FeatureToggleStore from '../feature-toggle/feature-toggle-store';
|
||||||
import type { Db } from '../../db/db';
|
import type { Db } from '../../db/db';
|
||||||
import Raw = Knex.Raw;
|
|
||||||
import type {
|
import type {
|
||||||
IFeatureSearchParams,
|
IFeatureSearchParams,
|
||||||
IQueryParam,
|
IQueryParam,
|
||||||
@ -19,6 +18,7 @@ import type {
|
|||||||
import { applyGenericQueryParams, applySearchFilters } from './search-utils';
|
import { applyGenericQueryParams, applySearchFilters } from './search-utils';
|
||||||
import type { FeatureSearchEnvironmentSchema } from '../../openapi/spec/feature-search-environment-schema';
|
import type { FeatureSearchEnvironmentSchema } from '../../openapi/spec/feature-search-environment-schema';
|
||||||
import { generateImageUrl } from '../../util';
|
import { generateImageUrl } from '../../util';
|
||||||
|
import Raw = Knex.Raw;
|
||||||
|
|
||||||
const sortEnvironments = (overview: IFeatureSearchOverview[]) => {
|
const sortEnvironments = (overview: IFeatureSearchOverview[]) => {
|
||||||
return overview.map((data: IFeatureSearchOverview) => ({
|
return overview.map((data: IFeatureSearchOverview) => ({
|
||||||
@ -112,9 +112,6 @@ class FeatureSearchStore implements IFeatureSearchStore {
|
|||||||
const validatedSortOrder =
|
const validatedSortOrder =
|
||||||
sortOrder === 'asc' || sortOrder === 'desc' ? sortOrder : 'asc';
|
sortOrder === 'asc' || sortOrder === 'desc' ? sortOrder : 'asc';
|
||||||
|
|
||||||
const featureLifecycleEnabled =
|
|
||||||
this.flagResolver.isEnabled('featureLifecycle');
|
|
||||||
|
|
||||||
const finalQuery = this.db
|
const finalQuery = this.db
|
||||||
.with('ranked_features', (query) => {
|
.with('ranked_features', (query) => {
|
||||||
query.from('features');
|
query.from('features');
|
||||||
@ -315,27 +312,22 @@ class FeatureSearchStore implements IFeatureSearchStore {
|
|||||||
.joinRaw('CROSS JOIN total_features')
|
.joinRaw('CROSS JOIN total_features')
|
||||||
.whereBetween('final_rank', [offset + 1, offset + limit])
|
.whereBetween('final_rank', [offset + 1, offset + limit])
|
||||||
.orderBy('final_rank');
|
.orderBy('final_rank');
|
||||||
if (featureLifecycleEnabled) {
|
finalQuery
|
||||||
finalQuery
|
.select(
|
||||||
.select(
|
'lifecycle.latest_stage',
|
||||||
'lifecycle.latest_stage',
|
'lifecycle.stage_status',
|
||||||
'lifecycle.stage_status',
|
'lifecycle.entered_stage_at',
|
||||||
'lifecycle.entered_stage_at',
|
)
|
||||||
)
|
.leftJoin(
|
||||||
.leftJoin(
|
'lifecycle',
|
||||||
'lifecycle',
|
'ranked_features.feature_name',
|
||||||
'ranked_features.feature_name',
|
'lifecycle.stage_feature',
|
||||||
'lifecycle.stage_feature',
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
this.queryExtraData(finalQuery);
|
this.queryExtraData(finalQuery);
|
||||||
const rows = await finalQuery;
|
const rows = await finalQuery;
|
||||||
stopTimer();
|
stopTimer();
|
||||||
if (rows.length > 0) {
|
if (rows.length > 0) {
|
||||||
const overview = this.getAggregatedSearchData(
|
const overview = this.getAggregatedSearchData(rows);
|
||||||
rows,
|
|
||||||
featureLifecycleEnabled,
|
|
||||||
);
|
|
||||||
const features = sortEnvironments(
|
const features = sortEnvironments(
|
||||||
overview,
|
overview,
|
||||||
) as IFeatureSearchOverview[];
|
) as IFeatureSearchOverview[];
|
||||||
@ -461,10 +453,7 @@ class FeatureSearchStore implements IFeatureSearchStore {
|
|||||||
return rankingSql;
|
return rankingSql;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAggregatedSearchData(
|
getAggregatedSearchData(rows): IFeatureSearchOverview[] {
|
||||||
rows,
|
|
||||||
featureLifecycleEnabled: boolean,
|
|
||||||
): IFeatureSearchOverview[] {
|
|
||||||
const entriesMap: Map<string, IFeatureSearchOverview> = new Map();
|
const entriesMap: Map<string, IFeatureSearchOverview> = new Map();
|
||||||
const orderedEntries: IFeatureSearchOverview[] = [];
|
const orderedEntries: IFeatureSearchOverview[] = [];
|
||||||
|
|
||||||
@ -501,17 +490,15 @@ class FeatureSearchStore implements IFeatureSearchStore {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if (featureLifecycleEnabled) {
|
entry.lifecycle = row.latest_stage
|
||||||
entry.lifecycle = row.latest_stage
|
? {
|
||||||
? {
|
stage: row.latest_stage,
|
||||||
stage: row.latest_stage,
|
...(row.stage_status
|
||||||
...(row.stage_status
|
? { status: row.stage_status }
|
||||||
? { status: row.stage_status }
|
: {}),
|
||||||
: {}),
|
enteredStageAt: row.entered_stage_at,
|
||||||
enteredStageAt: row.entered_stage_at,
|
}
|
||||||
}
|
: undefined;
|
||||||
: undefined;
|
|
||||||
}
|
|
||||||
entriesMap.set(row.feature_name, entry);
|
entriesMap.set(row.feature_name, entry);
|
||||||
orderedEntries.push(entry);
|
orderedEntries.push(entry);
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ beforeAll(async () => {
|
|||||||
experimental: {
|
experimental: {
|
||||||
flags: {
|
flags: {
|
||||||
strictSchemaValidation: true,
|
strictSchemaValidation: true,
|
||||||
featureLifecycle: true,
|
|
||||||
anonymiseEventLog: true,
|
anonymiseEventLog: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -52,11 +52,6 @@ beforeAll(async () => {
|
|||||||
server: {
|
server: {
|
||||||
serverMetrics: true,
|
serverMetrics: true,
|
||||||
},
|
},
|
||||||
experimental: {
|
|
||||||
flags: {
|
|
||||||
featureLifecycleMetrics: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
stores = createStores();
|
stores = createStores();
|
||||||
eventStore = stores.eventStore;
|
eventStore = stores.eventStore;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { createTestConfig } from '../test/config/test-config';
|
import { createTestConfig } from '../test/config/test-config';
|
||||||
import { create, start } from './server-impl';
|
import { create, start } from './server-impl';
|
||||||
import FakeEventStore from '../test/fixtures/fake-event-store';
|
|
||||||
|
|
||||||
jest.mock(
|
jest.mock(
|
||||||
'./routes',
|
'./routes',
|
||||||
@ -15,7 +14,6 @@ jest.mock(
|
|||||||
|
|
||||||
const noop = () => {};
|
const noop = () => {};
|
||||||
|
|
||||||
const eventStore = new FakeEventStore();
|
|
||||||
const settingStore = {
|
const settingStore = {
|
||||||
get: () => {
|
get: () => {
|
||||||
Promise.resolve('secret');
|
Promise.resolve('secret');
|
||||||
@ -34,21 +32,20 @@ jest.mock('./metrics', () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('./services', () => ({
|
||||||
|
createServices() {
|
||||||
|
return {
|
||||||
|
featureLifecycleService: { listen() {} },
|
||||||
|
schedulerService: { stop() {}, start() {} },
|
||||||
|
addonService: { destroy() {} },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
jest.mock('./db', () => ({
|
jest.mock('./db', () => ({
|
||||||
createStores() {
|
createStores() {
|
||||||
return {
|
return {
|
||||||
db: {
|
|
||||||
destroy: () => undefined,
|
|
||||||
},
|
|
||||||
clientInstanceStore: {
|
|
||||||
destroy: noop,
|
|
||||||
removeInstancesOlderThanTwoDays: noop,
|
|
||||||
},
|
|
||||||
clientMetricsStore: { destroy: noop, on: noop },
|
|
||||||
eventStore,
|
|
||||||
publicSignupTokenStore: { destroy: noop, on: noop },
|
|
||||||
settingStore,
|
settingStore,
|
||||||
projectStore: { getAll: () => Promise.resolve([]) },
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
@ -52,8 +52,6 @@ export type IFlagKey =
|
|||||||
| 'displayEdgeBanner'
|
| 'displayEdgeBanner'
|
||||||
| 'disableShowContextFieldSelectionValues'
|
| 'disableShowContextFieldSelectionValues'
|
||||||
| 'projectOverviewRefactorFeedback'
|
| 'projectOverviewRefactorFeedback'
|
||||||
| 'featureLifecycle'
|
|
||||||
| 'featureLifecycleMetrics'
|
|
||||||
| 'parseProjectFromSession'
|
| 'parseProjectFromSession'
|
||||||
| 'manyStrategiesPagination'
|
| 'manyStrategiesPagination'
|
||||||
| 'enableLegacyVariants'
|
| 'enableLegacyVariants'
|
||||||
@ -267,10 +265,6 @@ const flags: IFlags = {
|
|||||||
process.env.UNLEASH_EXPERIMENTAL_PROJECT_OVERVIEW_REFACTOR_FEEDBACK,
|
process.env.UNLEASH_EXPERIMENTAL_PROJECT_OVERVIEW_REFACTOR_FEEDBACK,
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
featureLifecycle: parseEnvVarBoolean(
|
|
||||||
process.env.UNLEASH_EXPERIMENTAL_FEATURE_LIFECYCLE,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
parseProjectFromSession: parseEnvVarBoolean(
|
parseProjectFromSession: parseEnvVarBoolean(
|
||||||
process.env.UNLEASH_EXPERIMENTAL_PARSE_PROJECT_FROM_SESSION,
|
process.env.UNLEASH_EXPERIMENTAL_PARSE_PROJECT_FROM_SESSION,
|
||||||
false,
|
false,
|
||||||
|
@ -48,7 +48,6 @@ process.nextTick(async () => {
|
|||||||
outdatedSdksBanner: true,
|
outdatedSdksBanner: true,
|
||||||
disableShowContextFieldSelectionValues: false,
|
disableShowContextFieldSelectionValues: false,
|
||||||
projectOverviewRefactorFeedback: true,
|
projectOverviewRefactorFeedback: true,
|
||||||
featureLifecycle: true,
|
|
||||||
parseProjectFromSession: true,
|
parseProjectFromSession: true,
|
||||||
manyStrategiesPagination: true,
|
manyStrategiesPagination: true,
|
||||||
enableLegacyVariants: false,
|
enableLegacyVariants: false,
|
||||||
|
Loading…
Reference in New Issue
Block a user