1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-02-23 00:22:19 +01:00

chore: remove dependent feature flags (#5419)

This commit is contained in:
Mateusz Kwasniewski 2023-11-27 14:54:40 +01:00 committed by GitHub
parent 581b238378
commit de287a75fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 83 additions and 193 deletions

View File

@ -31,9 +31,6 @@ const setupArchiveValidation = (orphanParents: string[]) => {
versionInfo: {
current: { oss: 'version', enterprise: 'version' },
},
flags: {
dependentFeatures: true,
},
});
testServerRoute(
server,
@ -126,6 +123,7 @@ test('Skip change request', async () => {
await screen.findByText('Archive feature toggles');
const button = await screen.findByText('Archive toggles');
await waitFor(() => expect(button).toBeEnabled());
button.click();
await waitFor(() => {

View File

@ -302,12 +302,8 @@ export const FeatureArchiveDialog: VFC<IFeatureArchiveDialogProps> = ({
const { disableArchive, offendingParents, hasDeletedDependencies } =
useVerifyArchive(featureIds, projectId, isOpen);
const dependentFeatures = useUiFlag('dependentFeatures');
const removeDependenciesWarning =
dependentFeatures &&
offendingParents.length === 0 &&
hasDeletedDependencies;
offendingParents.length === 0 && hasDeletedDependencies;
return (
<Dialogue
@ -317,7 +313,7 @@ export const FeatureArchiveDialog: VFC<IFeatureArchiveDialogProps> = ({
primaryButtonText={buttonText}
secondaryButtonText='Cancel'
title={dialogTitle}
disabledPrimaryButton={dependentFeatures && disableArchive}
disabledPrimaryButton={disableArchive}
>
<ConditionallyRender
condition={isBulkArchive}
@ -342,9 +338,7 @@ export const FeatureArchiveDialog: VFC<IFeatureArchiveDialogProps> = ({
}
/>
<ConditionallyRender
condition={
dependentFeatures && offendingParents.length > 0
}
condition={offendingParents.length > 0}
show={
<ArchiveParentError
ids={offendingParents}
@ -378,9 +372,7 @@ export const FeatureArchiveDialog: VFC<IFeatureArchiveDialogProps> = ({
?
</p>
<ConditionallyRender
condition={
dependentFeatures && offendingParents.length > 0
}
condition={offendingParents.length > 0}
show={
<ArchiveParentError
ids={offendingParents}

View File

@ -8,9 +8,6 @@ const server = testServerSetup();
const setupApi = () => {
testServerRoute(server, '/api/admin/ui-config', {
flags: {
dependentFeatures: true,
},
versionInfo: {
current: { oss: 'irrelevant', enterprise: 'some value' },
},

View File

@ -10,9 +10,6 @@ const server = testServerSetup();
const setupApi = () => {
testServerRoute(server, '/api/admin/ui-config', {
flags: {
dependentFeatures: true,
},
versionInfo: {
current: { oss: 'irrelevant', enterprise: 'some value' },
},
@ -35,9 +32,6 @@ const setupApi = () => {
const setupOssWithExistingDependencies = () => {
testServerRoute(server, '/api/admin/ui-config', {
flags: {
dependentFeatures: true,
},
versionInfo: {
current: { oss: 'some value' },
},

View File

@ -1,13 +1,9 @@
import { useUiFlag } from 'hooks/useUiFlag';
import { useCheckDependenciesExist } from 'hooks/api/getters/useCheckDependenciesExist/useCheckDependenciesExist';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
export const useShowDependentFeatures = (project: string) => {
const dependentFeatures = useUiFlag('dependentFeatures');
const { dependenciesExist } = useCheckDependenciesExist(project);
const { isOss } = useUiConfig();
return Boolean(
isOss() ? dependenciesExist && dependentFeatures : dependentFeatures,
);
return Boolean(isOss() ? dependenciesExist : true);
};

View File

@ -10,11 +10,7 @@ import userEvent from '@testing-library/user-event';
const server = testServerSetup();
const setupApi = () => {
testServerRoute(server, '/api/admin/ui-config', {
flags: {
dependentFeatures: true,
},
});
testServerRoute(server, '/api/admin/ui-config', {});
};
test('Cannot change project for feature with dependencies', async () => {

View File

@ -41,7 +41,6 @@ const FeatureSettingsProjectConfirm = ({
feature,
changeRequests,
}: IFeatureSettingsProjectConfirm) => {
const dependentFeatures = useUiFlag('dependentFeatures');
const currentProjectId = useRequiredPathParam('projectId');
const { project } = useProject(projectId);
@ -61,8 +60,7 @@ const FeatureSettingsProjectConfirm = ({
: false;
const hasDependencies =
dependentFeatures &&
(feature.dependencies.length > 0 || feature.children.length > 0);
feature.dependencies.length > 0 || feature.children.length > 0;
return (
<ConditionallyRender

View File

@ -143,7 +143,6 @@ export const FeatureView = () => {
const { refetch: projectRefetch } = useProject(projectId);
const { favorite, unfavorite } = useFavoriteFeaturesApi();
const { refetchFeature } = useFeature(projectId, featureId);
const dependentFeatures = useUiFlag('dependentFeatures');
const { setToastData, setToastApiError } = useToast();
const [openTagDialog, setOpenTagDialog] = useState(false);
@ -267,10 +266,7 @@ export const FeatureView = () => {
/>
</StyledToggleInfoContainer>
<ConditionallyRender
condition={
dependentFeatures &&
feature.dependencies.length > 0
}
condition={feature.dependencies.length > 0}
show={
<StyledDependency>
<b>Has parent: </b>
@ -283,10 +279,7 @@ export const FeatureView = () => {
}
/>
<ConditionallyRender
condition={
dependentFeatures &&
feature.children.length > 0
}
condition={feature.children.length > 0}
show={
<StyledDependency>
<b>Has children:</b>

View File

@ -75,7 +75,6 @@ exports[`should create default config 1`] = `
"caseInsensitiveInOperators": false,
"customRootRolesKillSwitch": false,
"demo": false,
"dependentFeatures": false,
"detectSegmentUsageInChangeRequests": false,
"disableBulkToggle": false,
"disableMetrics": false,

View File

@ -67,8 +67,6 @@ export default class FeatureToggleClientStore
const isPlayground = requestType === 'playground';
const environment = featureQuery?.environment || DEFAULT_ENV;
const stopTimer = this.timer('getFeatureAdmin');
const dependentFeaturesEnabled =
this.flagResolver.isEnabled('dependentFeatures');
let selectColumns = [
'features.name as name',
@ -201,7 +199,7 @@ export default class FeatureToggleClientStore
) {
this.addSegmentIdsToStrategy(feature, r);
}
if (r.parent && !isAdmin && dependentFeaturesEnabled) {
if (r.parent && !isAdmin) {
feature.dependencies = feature.dependencies || [];
feature.dependencies.push({
feature: r.parent,

View File

@ -64,7 +64,6 @@ beforeAll(async () => {
experimental: {
flags: {
strictSchemaValidation: true,
dependentFeatures: true,
},
},
},

View File

@ -182,25 +182,19 @@ export default class DependentFeaturesController extends Controller {
const { child, projectId } = req.params;
const { variants, enabled, feature } = req.body;
if (this.config.flagResolver.isEnabled('dependentFeatures')) {
await this.dependentFeaturesService.transactional((service) =>
service.upsertFeatureDependency(
{ child, projectId },
{
variants,
enabled,
feature,
},
req.user,
),
);
await this.dependentFeaturesService.transactional((service) =>
service.upsertFeatureDependency(
{ child, projectId },
{
variants,
enabled,
feature,
},
req.user,
),
);
res.status(200).end();
} else {
throw new InvalidOperationError(
'Dependent features are not enabled',
);
}
res.status(200).end();
}
async deleteFeatureDependency(
@ -209,23 +203,17 @@ export default class DependentFeaturesController extends Controller {
): Promise<void> {
const { child, parent, projectId } = req.params;
if (this.config.flagResolver.isEnabled('dependentFeatures')) {
await this.dependentFeaturesService.transactional((service) =>
service.deleteFeatureDependency(
{
parent,
child,
},
projectId,
req.user,
),
);
res.status(200).end();
} else {
throw new InvalidOperationError(
'Dependent features are not enabled',
);
}
await this.dependentFeaturesService.transactional((service) =>
service.deleteFeatureDependency(
{
parent,
child,
},
projectId,
req.user,
),
);
res.status(200).end();
}
async deleteFeatureDependencies(
@ -234,20 +222,10 @@ export default class DependentFeaturesController extends Controller {
): Promise<void> {
const { child, projectId } = req.params;
if (this.config.flagResolver.isEnabled('dependentFeatures')) {
await this.dependentFeaturesService.transactional((service) =>
service.deleteFeaturesDependencies(
[child],
projectId,
req.user,
),
);
res.status(200).end();
} else {
throw new InvalidOperationError(
'Dependent features are not enabled',
);
}
await this.dependentFeaturesService.transactional((service) =>
service.deleteFeaturesDependencies([child], projectId, req.user),
);
res.status(200).end();
}
async getParentOptions(
@ -256,15 +234,9 @@ export default class DependentFeaturesController extends Controller {
): Promise<void> {
const { child } = req.params;
if (this.config.flagResolver.isEnabled('dependentFeatures')) {
const parentOptions =
await this.dependentFeaturesService.getParentOptions(child);
res.send(parentOptions);
} else {
throw new InvalidOperationError(
'Dependent features are not enabled',
);
}
const parentOptions =
await this.dependentFeaturesService.getParentOptions(child);
res.send(parentOptions);
}
async checkDependenciesExist(
@ -273,14 +245,8 @@ export default class DependentFeaturesController extends Controller {
): Promise<void> {
const { child } = req.params;
if (this.config.flagResolver.isEnabled('dependentFeatures')) {
const exist =
await this.dependentFeaturesService.checkDependenciesExist();
res.send(exist);
} else {
throw new InvalidOperationError(
'Dependent features are not enabled',
);
}
const exist =
await this.dependentFeaturesService.checkDependenciesExist();
res.send(exist);
}
}

View File

@ -25,7 +25,6 @@ beforeAll(async () => {
experimental: {
flags: {
strictSchemaValidation: true,
dependentFeatures: true,
},
},
},

View File

@ -159,7 +159,6 @@ beforeAll(async () => {
experimental: {
flags: {
featuresExportImport: true,
dependentFeatures: true,
},
},
},

View File

@ -202,7 +202,6 @@ export class FeatureToggleRowConverter {
buildPlaygroundFeaturesFromRows = (
rows: any[],
dependentFeaturesEnabled: boolean,
featureQuery?: IFeatureToggleQuery,
): FeatureConfigurationClient[] => {
const result = rows.reduce((acc, r) => {
@ -212,7 +211,7 @@ export class FeatureToggleRowConverter {
feature = this.createBaseFeature(r, feature, featureQuery);
if (r.parent && dependentFeaturesEnabled) {
if (r.parent) {
feature.dependencies = feature.dependencies || [];
feature.dependencies.push({
feature: r.parent,

View File

@ -130,6 +130,7 @@ export type FeatureNameCheckResultWithFeaturePattern =
const oneOf = (values: string[], match: string) => {
return values.some((value) => value === match);
};
class FeatureToggleService {
private logger: Logger;
@ -256,32 +257,27 @@ class FeatureToggleService {
}
async validateNoChildren(featureName: string): Promise<void> {
if (this.flagResolver.isEnabled('dependentFeatures')) {
const children = await this.dependentFeaturesReadModel.getChildren([
featureName,
]);
if (children.length > 0) {
throw new InvalidOperationError(
'You can not archive/delete this feature since other features depend on it.',
);
}
const children = await this.dependentFeaturesReadModel.getChildren([
featureName,
]);
if (children.length > 0) {
throw new InvalidOperationError(
'You can not archive/delete this feature since other features depend on it.',
);
}
}
async validateNoOrphanParents(featureNames: string[]): Promise<void> {
if (this.flagResolver.isEnabled('dependentFeatures')) {
if (featureNames.length === 0) return;
const parents =
await this.dependentFeaturesReadModel.getOrphanParents(
featureNames,
);
if (parents.length > 0) {
throw new InvalidOperationError(
featureNames.length > 1
? `You can not archive/delete those features since other features depend on them.`
: `You can not archive/delete this feature since other features depend on it.`,
);
}
if (featureNames.length === 0) return;
const parents = await this.dependentFeaturesReadModel.getOrphanParents(
featureNames,
);
if (parents.length > 0) {
throw new InvalidOperationError(
featureNames.length > 1
? `You can not archive/delete those features since other features depend on them.`
: `You can not archive/delete this feature since other features depend on it.`,
);
}
}
@ -959,12 +955,10 @@ class FeatureToggleService {
let dependencies: IDependency[] = [];
let children: string[] = [];
if (this.flagResolver.isEnabled('dependentFeatures')) {
[dependencies, children] = await Promise.all([
this.dependentFeaturesReadModel.getParents(featureName),
this.dependentFeaturesReadModel.getChildren([featureName]),
]);
}
[dependencies, children] = await Promise.all([
this.dependentFeaturesReadModel.getParents(featureName),
this.dependentFeaturesReadModel.getChildren([featureName]),
]);
if (environmentVariants) {
const result =
@ -1288,21 +1282,17 @@ class FeatureToggleService {
}),
);
if (this.flagResolver.isEnabled('dependentFeatures')) {
const cloneDependencies =
this.dependentFeaturesService.cloneDependencies(
{ featureName, newFeatureName, projectId },
userName,
);
const cloneDependencies =
this.dependentFeaturesService.cloneDependencies(
{ featureName, newFeatureName, projectId },
userName,
);
await Promise.all([
...strategyTasks,
...variantTasks,
cloneDependencies,
]);
} else {
await Promise.all([...strategyTasks, ...variantTasks]);
}
await Promise.all([
...strategyTasks,
...variantTasks,
cloneDependencies,
]);
return created;
}

View File

@ -209,16 +209,11 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
const archived = false;
const builder = this.getBaseFeatureQuery(archived, environment);
const dependentFeaturesEnabled =
this.flagResolver.isEnabled('dependentFeatures');
builder.withDependentFeatureToggles();
if (dependentFeaturesEnabled) {
builder.withDependentFeatureToggles();
builder.addSelectColumn('df.parent as parent');
builder.addSelectColumn('df.variants as parent_variants');
builder.addSelectColumn('df.enabled as parent_enabled');
}
builder.addSelectColumn('df.parent as parent');
builder.addSelectColumn('df.variants as parent_variants');
builder.addSelectColumn('df.enabled as parent_enabled');
if (featureQuery?.project) {
builder.forProject(featureQuery.project);
@ -230,7 +225,6 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
return this.featureToggleRowConverter.buildPlaygroundFeaturesFromRows(
rows,
dependentFeaturesEnabled,
featureQuery,
);
}

View File

@ -22,7 +22,6 @@ beforeAll(async () => {
experimental: {
flags: {
strictSchemaValidation: true,
dependentFeatures: true,
useLastSeenRefactor: true,
},
},

View File

@ -93,7 +93,6 @@ beforeAll(async () => {
experimental: {
flags: {
strictSchemaValidation: true,
dependentFeatures: true,
},
},
},

View File

@ -10,11 +10,7 @@ let app: IUnleashTest;
let db: ITestDb;
beforeAll(async () => {
db = await dbInit('advanced_playground', getLogger, {
experimental: {
flags: { dependentFeatures: true },
},
});
db = await dbInit('advanced_playground', getLogger);
app = await setupAppWithCustomConfig(
db.stores,
{
@ -24,7 +20,6 @@ beforeAll(async () => {
strictSchemaValidation: true,
strategyVariant: true,
privateProjects: true,
dependentFeatures: true,
useLastSeenRefactor: true,
},
},

View File

@ -24,7 +24,6 @@ export type IFlagKey =
| 'filterInvalidClientMetrics'
| 'customRootRolesKillSwitch'
| 'privateProjects'
| 'dependentFeatures'
| 'disableMetrics'
| 'useLastSeenRefactor'
| 'banners'
@ -107,10 +106,6 @@ const flags: IFlags = {
process.env.UNLEASH_EXPERIMENTAL_CUSTOM_ROOT_ROLES_KILL_SWITCH,
false,
),
dependentFeatures: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_DEPENDENT_FEATURES,
false,
),
privateProjects: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_PRIVATE_PROJECTS,
false,

View File

@ -38,7 +38,6 @@ process.nextTick(async () => {
anonymiseEventLog: false,
responseTimeWithAppNameKillSwitch: false,
privateProjects: true,
dependentFeatures: true,
useLastSeenRefactor: true,
featureSearchAPI: true,
featureSearchFrontend: false,

View File

@ -17,7 +17,6 @@ beforeAll(async () => {
experimental: {
flags: {
strictSchemaValidation: true,
dependentFeatures: true,
},
},
},

View File

@ -12,16 +12,13 @@ let db: ITestDb;
const testUser = { name: 'test' } as User;
beforeAll(async () => {
db = await dbInit('feature_api_client', getLogger, {
experimental: { flags: { dependentFeatures: true } },
});
db = await dbInit('feature_api_client', getLogger);
app = await setupAppWithCustomConfig(
db.stores,
{
experimental: {
flags: {
strictSchemaValidation: true,
dependentFeatures: true,
},
},
},