From 96d61c03071c2f95631381651454611fede61ac9 Mon Sep 17 00:00:00 2001
From: kwasniew
Date: Fri, 13 Oct 2023 10:12:27 +0200
Subject: [PATCH 1/9] feat: validate archive dependent features
---
src/lib/db/transaction.ts | 5 ++
.../dependent-features-controller.ts | 9 ++-
.../feature-toggle/feature-toggle-service.ts | 4 ++
src/lib/openapi/util/openapi-tags.ts | 4 ++
.../admin-api/project/project-archive.ts | 40 ++++++++++++-
.../e2e/api/admin/feature-archive.e2e.test.ts | 60 +++++++++++++++++--
6 files changed, 111 insertions(+), 11 deletions(-)
diff --git a/src/lib/db/transaction.ts b/src/lib/db/transaction.ts
index 57c6905aa12f..574126966bd8 100644
--- a/src/lib/db/transaction.ts
+++ b/src/lib/db/transaction.ts
@@ -16,6 +16,11 @@ export const createKnexTransactionStarter = (
function transaction(
scope: (trx: KnexTransaction) => void | Promise,
) {
+ if (!knex) {
+ console.warn(
+ 'It looks like your DB is not provided. Very often it is a test setup problem in setupAppWithCustomConfig',
+ );
+ }
return knex.transaction(scope);
}
return transaction;
diff --git a/src/lib/features/dependent-features/dependent-features-controller.ts b/src/lib/features/dependent-features/dependent-features-controller.ts
index dd1b6585e1e9..7b0de412ff65 100644
--- a/src/lib/features/dependent-features/dependent-features-controller.ts
+++ b/src/lib/features/dependent-features/dependent-features-controller.ts
@@ -21,7 +21,6 @@ import { IAuthRequest } from '../../routes/unleash-types';
import { InvalidOperationError } from '../../error';
import { DependentFeaturesService } from './dependent-features-service';
import { TransactionCreator, UnleashTransaction } from '../../db/transaction';
-import { extractUsernameFromUser } from '../../util';
interface ProjectParams {
projectId: string;
@@ -91,7 +90,7 @@ export default class DependentFeaturesController extends Controller {
permission: UPDATE_FEATURE_DEPENDENCY,
middleware: [
openApiService.validPath({
- tags: ['Features'],
+ tags: ['Dependencies'],
summary: 'Add a feature dependency.',
description:
'Add a dependency to a parent feature. Each environment will resolve corresponding dependency independently.',
@@ -115,7 +114,7 @@ export default class DependentFeaturesController extends Controller {
acceptAnyContentType: true,
middleware: [
openApiService.validPath({
- tags: ['Features'],
+ tags: ['Dependencies'],
summary: 'Deletes a feature dependency.',
description: 'Remove a dependency to a parent feature.',
operationId: 'deleteFeatureDependency',
@@ -135,7 +134,7 @@ export default class DependentFeaturesController extends Controller {
acceptAnyContentType: true,
middleware: [
openApiService.validPath({
- tags: ['Features'],
+ tags: ['Dependencies'],
summary: 'Deletes feature dependencies.',
description: 'Remove dependencies to all parent features.',
operationId: 'deleteFeatureDependencies',
@@ -154,7 +153,7 @@ export default class DependentFeaturesController extends Controller {
permission: NONE,
middleware: [
openApiService.validPath({
- tags: ['Features'],
+ tags: ['Dependencies'],
summary: 'List parent options.',
description:
'List available parents who have no transitive dependencies.',
diff --git a/src/lib/features/feature-toggle/feature-toggle-service.ts b/src/lib/features/feature-toggle/feature-toggle-service.ts
index 824262a74da2..7cd674b009d1 100644
--- a/src/lib/features/feature-toggle/feature-toggle-service.ts
+++ b/src/lib/features/feature-toggle/feature-toggle-service.ts
@@ -1525,6 +1525,10 @@ class FeatureToggleService {
);
}
+ async validateArchiveToggles(featureNames: string[]): Promise {
+ return this.dependentFeaturesReadModel.getOrphanParents(featureNames);
+ }
+
async unprotectedArchiveToggles(
featureNames: string[],
createdBy: string,
diff --git a/src/lib/openapi/util/openapi-tags.ts b/src/lib/openapi/util/openapi-tags.ts
index da85dd87fa7e..817c61ae9013 100644
--- a/src/lib/openapi/util/openapi-tags.ts
+++ b/src/lib/openapi/util/openapi-tags.ts
@@ -138,6 +138,10 @@ const OPENAPI_TAGS = [
description:
'Experimental endpoints that may change or disappear at any time.',
},
+ {
+ name: 'Dependencies',
+ description: 'Manage feature dependencies.',
+ },
{ name: 'Users', description: 'Manage users and passwords.' },
] as const;
diff --git a/src/lib/routes/admin-api/project/project-archive.ts b/src/lib/routes/admin-api/project/project-archive.ts
index 7669e59a8b3f..f9432b8cd93f 100644
--- a/src/lib/routes/admin-api/project/project-archive.ts
+++ b/src/lib/routes/admin-api/project/project-archive.ts
@@ -16,11 +16,16 @@ import {
emptyResponse,
getStandardResponses,
} from '../../../openapi/util/standard-responses';
-import { BatchFeaturesSchema, createRequestSchema } from '../../../openapi';
+import {
+ BatchFeaturesSchema,
+ createRequestSchema,
+ createResponseSchema,
+} from '../../../openapi';
import Controller from '../../controller';
const PATH = '/:projectId';
const PATH_ARCHIVE = `${PATH}/archive`;
+const PATH_VALIDATE_ARCHIVE = `${PATH}/archive/validate`;
const PATH_DELETE = `${PATH}/delete`;
const PATH_REVIVE = `${PATH}/revive`;
@@ -90,6 +95,27 @@ export default class ProjectArchiveController extends Controller {
],
});
+ this.route({
+ method: 'post',
+ path: PATH_VALIDATE_ARCHIVE,
+ handler: this.validateArchiveFeatures,
+ permission: DELETE_FEATURE,
+ middleware: [
+ openApiService.validPath({
+ tags: ['Features'],
+ operationId: 'validateArchiveFeatures',
+ description:
+ 'This endpoint validated if a list of features can be archived. Returns a list of parent features that would orphan some child features. If archive can process then empty list is returned.',
+ summary: 'Validates if a list of features can be archived',
+ requestBody: createRequestSchema('batchFeaturesSchema'),
+ responses: {
+ 200: createResponseSchema('batchFeaturesSchema'),
+ ...getStandardResponses(400, 401, 403, 415),
+ },
+ }),
+ ],
+ });
+
this.route({
method: 'post',
path: PATH_ARCHIVE,
@@ -144,6 +170,18 @@ export default class ProjectArchiveController extends Controller {
await this.featureService.archiveToggles(features, req.user, projectId);
res.status(202).end();
}
+
+ async validateArchiveFeatures(
+ req: IAuthRequest,
+ res: Response,
+ ): Promise {
+ const { features } = req.body;
+
+ const offendingParents =
+ await this.featureService.validateArchiveToggles(features);
+
+ res.send(offendingParents);
+ }
}
module.exports = ProjectArchiveController;
diff --git a/src/test/e2e/api/admin/feature-archive.e2e.test.ts b/src/test/e2e/api/admin/feature-archive.e2e.test.ts
index 9fecb9c16b65..2dd69153a86b 100644
--- a/src/test/e2e/api/admin/feature-archive.e2e.test.ts
+++ b/src/test/e2e/api/admin/feature-archive.e2e.test.ts
@@ -11,13 +11,18 @@ let db: ITestDb;
beforeAll(async () => {
db = await dbInit('archive_serial', getLogger);
- app = await setupAppWithCustomConfig(db.stores, {
- experimental: {
- flags: {
- strictSchemaValidation: true,
+ app = await setupAppWithCustomConfig(
+ db.stores,
+ {
+ experimental: {
+ flags: {
+ strictSchemaValidation: true,
+ dependentFeatures: true,
+ },
},
},
- });
+ db.rawDatabase,
+ );
await app.createFeature({
name: 'featureX',
description: 'the #1 feature',
@@ -242,3 +247,48 @@ test('Should be able to bulk archive features', async () => {
);
expect(archivedFeatures).toHaveLength(2);
});
+
+test('Should validate if a list of features with dependencies can be archived', async () => {
+ const child1 = 'child1Feature';
+ const child2 = 'child2Feature';
+ const parent = 'parentFeature';
+
+ await app.createFeature(child1);
+ await app.createFeature(child2);
+ await app.createFeature(parent);
+ await app.addDependency(child1, parent);
+ await app.addDependency(child2, parent);
+
+ const { body: allChildrenAndParent } = await app.request
+ .post(`/api/admin/projects/${DEFAULT_PROJECT}/archive/validate`)
+ .send({
+ features: [child1, child2, parent],
+ })
+ .expect(200);
+
+ const { body: allChildren } = await app.request
+ .post(`/api/admin/projects/${DEFAULT_PROJECT}/archive/validate`)
+ .send({
+ features: [child1, child2],
+ })
+ .expect(200);
+
+ const { body: onlyParent } = await app.request
+ .post(`/api/admin/projects/${DEFAULT_PROJECT}/archive/validate`)
+ .send({
+ features: [parent],
+ })
+ .expect(200);
+
+ const { body: oneChildAndParent } = await app.request
+ .post(`/api/admin/projects/${DEFAULT_PROJECT}/archive/validate`)
+ .send({
+ features: [child1, parent],
+ })
+ .expect(200);
+
+ expect(allChildrenAndParent).toEqual([]);
+ expect(allChildren).toEqual([]);
+ expect(onlyParent).toEqual([parent]);
+ expect(oneChildAndParent).toEqual([parent]);
+});
From e23af1675f3e1dc253447d2f6c041aeab0bf3ff3 Mon Sep 17 00:00:00 2001
From: kwasniew
Date: Fri, 13 Oct 2023 10:19:22 +0200
Subject: [PATCH 2/9] feat: validate archive dependent features
---
.../FeatureArchiveDialog.tsx | 87 ++++++++++++++++++-
.../actions/useProjectApi/useProjectApi.ts | 14 +++
2 files changed, 100 insertions(+), 1 deletion(-)
diff --git a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
index 05e307873852..05f1f7c56076 100644
--- a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
+++ b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
@@ -1,4 +1,4 @@
-import { VFC } from 'react';
+import { useEffect, useState, VFC } from 'react';
import { Dialogue } from 'component/common/Dialogue/Dialogue';
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
import useToast from 'hooks/useToast';
@@ -62,6 +62,47 @@ const UsageWarning = ({
return null;
};
+const ArchiveParentError = ({
+ ids,
+ projectId,
+}: {
+ ids?: string[];
+ projectId: string;
+}) => {
+ const formatPath = (id: string) => {
+ return `/projects/${projectId}/features/${id}`;
+ };
+ if (ids) {
+ return (
+ theme.spacing(2, 0) }}
+ >
+
+ {`${ids.length} feature toggles `}
+
+
+ have child features that depend on them and are not part of
+ the archive operation. These parent features can not be
+ archived:
+
+
+ {ids?.map((id) => (
+ -
+ {{id}}
+
+ ))}
+
+
+ );
+ }
+ return null;
+};
+
const useActionButtonText = (projectId: string, isBulkArchive: boolean) => {
const getHighestEnvironment =
useHighestPermissionChangeRequestEnvironment(projectId);
@@ -167,6 +208,34 @@ const useArchiveAction = ({
};
};
+const useVerifyArchive = (
+ featureIds: string[],
+ projectId: string,
+ isOpen: boolean,
+) => {
+ const [disableArchive, setDisableArchive] = useState(true);
+ const [offendingParents, setOffendingParents] = useState([]);
+ const { verifyArchiveFeatures } = useProjectApi();
+
+ useEffect(() => {
+ if (isOpen) {
+ verifyArchiveFeatures(projectId, featureIds)
+ .then((res) => res.json())
+ .then((offendingParents) => {
+ if (offendingParents.length === 0) {
+ setDisableArchive(false);
+ setOffendingParents(offendingParents);
+ } else {
+ setDisableArchive(true);
+ setOffendingParents(offendingParents);
+ }
+ });
+ }
+ }, [JSON.stringify(featureIds), isOpen, projectId]);
+
+ return { disableArchive, offendingParents };
+};
+
export const FeatureArchiveDialog: VFC = ({
isOpen,
onClose,
@@ -197,6 +266,12 @@ export const FeatureArchiveDialog: VFC = ({
},
});
+ const { disableArchive, offendingParents } = useVerifyArchive(
+ featureIds,
+ projectId,
+ isOpen,
+ );
+
return (
= ({
primaryButtonText={buttonText}
secondaryButtonText='Cancel'
title={dialogTitle}
+ disabledPrimaryButton={disableArchive}
>
= ({
/>
}
/>
+ 0}
+ show={
+
+ }
+ />
{
return makeRequest(req.caller, req.id);
};
+ const verifyArchiveFeatures = async (
+ projectId: string,
+ featureIds: string[],
+ ) => {
+ const path = `api/admin/projects/${projectId}/archive/validate`;
+ const req = createRequest(path, {
+ method: 'POST',
+ body: JSON.stringify({ features: featureIds }),
+ });
+
+ return makeRequest(req.caller, req.id);
+ };
+
const reviveFeatures = async (projectId: string, featureIds: string[]) => {
const path = `api/admin/projects/${projectId}/revive`;
const req = createRequest(path, {
@@ -245,6 +258,7 @@ const useProjectApi = () => {
setUserRoles,
setGroupRoles,
archiveFeatures,
+ verifyArchiveFeatures,
reviveFeatures,
staleFeatures,
deleteFeature,
From a5cfc2c24a0203a4353810fd765cc7fa6b3861b1 Mon Sep 17 00:00:00 2001
From: kwasniew
Date: Fri, 13 Oct 2023 11:59:53 +0200
Subject: [PATCH 3/9] feat: detect orphaned children
---
.../FeatureArchiveDialog.tsx | 58 +++++++++++++++----
1 file changed, 48 insertions(+), 10 deletions(-)
diff --git a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
index 05f1f7c56076..860e1fc3cd6d 100644
--- a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
+++ b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
@@ -72,7 +72,8 @@ const ArchiveParentError = ({
const formatPath = (id: string) => {
return `/projects/${projectId}/features/${id}`;
};
- if (ids) {
+
+ if (ids && ids.length > 1) {
return (
);
}
+ if (ids && ids.length === 1) {
+ return (
+ theme.spacing(2, 0) }}
+ >
+
+
+ {ids[0]} has child
+ features that depend on it and are not part of the archive
+ operation.
+
+
+ );
+ }
return null;
};
@@ -214,7 +234,7 @@ const useVerifyArchive = (
isOpen: boolean,
) => {
const [disableArchive, setDisableArchive] = useState(true);
- const [offendingParents, setOffendingParents] = useState([]);
+ const [offendingParents, setOffendingParents] = useState([]);
const { verifyArchiveFeatures } = useProjectApi();
useEffect(() => {
@@ -231,7 +251,13 @@ const useVerifyArchive = (
}
});
}
- }, [JSON.stringify(featureIds), isOpen, projectId]);
+ }, [
+ JSON.stringify(featureIds),
+ isOpen,
+ projectId,
+ setOffendingParents,
+ setDisableArchive,
+ ]);
return { disableArchive, offendingParents };
};
@@ -271,6 +297,7 @@ export const FeatureArchiveDialog: VFC = ({
projectId,
isOpen,
);
+ console.log('offending parents', offendingParents, offendingParents.length);
return (
= ({
>
}
elseShow={
-
- Are you sure you want to archive{' '}
- {isBulkArchive
- ? 'these feature toggles'
- : 'this feature toggle'}
- ?
-
+ <>
+
+ Are you sure you want to archive{' '}
+ {isBulkArchive
+ ? 'these feature toggles'
+ : 'this feature toggle'}
+ ?
+
+ 0}
+ show={
+
+ }
+ />
+ >
}
/>
From f053d63b87fbcde492a3c9da4d56bf9ac8b844f5 Mon Sep 17 00:00:00 2001
From: kwasniew
Date: Fri, 13 Oct 2023 12:51:38 +0200
Subject: [PATCH 4/9] test: archive parents error message
---
.../FeatureArchiveDialog.test.tsx | 50 ++++++++++++++++++-
.../FeatureArchiveDialog.tsx | 1 -
2 files changed, 49 insertions(+), 2 deletions(-)
diff --git a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx
index 8ea6f2e0127f..2067e8a88711 100644
--- a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx
+++ b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx
@@ -25,17 +25,21 @@ const setupHappyPathForChangeRequest = () => {
},
],
);
+};
+const setupArchiveValidation = (orphanParents: string[]) => {
testServerRoute(server, '/api/admin/ui-config', {
versionInfo: {
current: { oss: 'version', enterprise: 'version' },
},
});
-};
+ testServerRoute(server, '/api/admin/projects/projectId/archive/validate', orphanParents, 'post');
+}
test('Add single archive feature change to change request', async () => {
const onClose = vi.fn();
const onConfirm = vi.fn();
setupHappyPathForChangeRequest();
+ setupArchiveValidation([]);
render(
{
const onClose = vi.fn();
const onConfirm = vi.fn();
setupHappyPathForChangeRequest();
+ setupArchiveValidation([]);
render(
{
const onClose = vi.fn();
const onConfirm = vi.fn();
setupHappyPathForChangeRequest();
+ setupArchiveValidation([]);
render(
{
await screen.findByText('Archive feature toggles');
const button = await screen.findByText('Archive toggles');
+ await waitFor(() => {
+ expect(button).toBeEnabled();
+ });
+
button.click();
await waitFor(() => {
@@ -110,3 +120,41 @@ test('Skip change request', async () => {
});
expect(onConfirm).toBeCalledTimes(0); // we didn't setup non Change Request flow so failure
});
+
+test('Show error message when multiple parents of orphaned children are archived', async () => {
+ const onClose = vi.fn();
+ const onConfirm = vi.fn();
+ setupArchiveValidation(['parentA', 'parentB']);
+ render(
+ ,
+ );
+
+ await screen.findByText('2 feature toggles');
+ await screen.findByText('have child features that depend on them and are not part of the archive operation. These parent features can not be archived:');
+});
+
+test('Show error message when 1 parent of orphaned children is archived', async () => {
+ const onClose = vi.fn();
+ const onConfirm = vi.fn();
+ setupArchiveValidation(['parent']);
+ render(
+ ,
+ );
+
+ await screen.findByText('parent');
+ await screen.findByText('has child features that depend on it and are not part of the archive operation.');
+});
diff --git a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
index 860e1fc3cd6d..1158c3390d24 100644
--- a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
+++ b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
@@ -297,7 +297,6 @@ export const FeatureArchiveDialog: VFC = ({
projectId,
isOpen,
);
- console.log('offending parents', offendingParents, offendingParents.length);
return (
Date: Fri, 13 Oct 2023 12:53:24 +0200
Subject: [PATCH 5/9] test: archive parents error message
---
src/lib/openapi/util/openapi-tags.ts | 4 ----
1 file changed, 4 deletions(-)
diff --git a/src/lib/openapi/util/openapi-tags.ts b/src/lib/openapi/util/openapi-tags.ts
index 80f5ab32c563..5070dc09d1da 100644
--- a/src/lib/openapi/util/openapi-tags.ts
+++ b/src/lib/openapi/util/openapi-tags.ts
@@ -142,10 +142,6 @@ const OPENAPI_TAGS = [
description:
'Experimental endpoints that may change or disappear at any time.',
},
- {
- name: 'Dependencies',
- description: 'Manage feature dependencies.',
- },
{ name: 'Users', description: 'Manage users and passwords.' },
] as const;
From 5912179871d5844213b84524965b3f16a5ad022f Mon Sep 17 00:00:00 2001
From: kwasniew
Date: Fri, 13 Oct 2023 13:06:54 +0200
Subject: [PATCH 6/9] test: archive parents error message
---
.../FeatureArchiveDialog.test.tsx | 17 +++++++++++++----
.../FeatureArchiveDialog.tsx | 12 ++----------
2 files changed, 15 insertions(+), 14 deletions(-)
diff --git a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx
index 2067e8a88711..9128cc5dcdb5 100644
--- a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx
+++ b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx
@@ -32,8 +32,13 @@ const setupArchiveValidation = (orphanParents: string[]) => {
current: { oss: 'version', enterprise: 'version' },
},
});
- testServerRoute(server, '/api/admin/projects/projectId/archive/validate', orphanParents, 'post');
-}
+ testServerRoute(
+ server,
+ '/api/admin/projects/projectId/archive/validate',
+ orphanParents,
+ 'post',
+ );
+};
test('Add single archive feature change to change request', async () => {
const onClose = vi.fn();
@@ -137,7 +142,9 @@ test('Show error message when multiple parents of orphaned children are archived
);
await screen.findByText('2 feature toggles');
- await screen.findByText('have child features that depend on them and are not part of the archive operation. These parent features can not be archived:');
+ await screen.findByText(
+ 'have child features that depend on them and are not part of the archive operation. These parent features can not be archived:',
+ );
});
test('Show error message when 1 parent of orphaned children is archived', async () => {
@@ -156,5 +163,7 @@ test('Show error message when 1 parent of orphaned children is archived', async
);
await screen.findByText('parent');
- await screen.findByText('has child features that depend on it and are not part of the archive operation.');
+ await screen.findByText(
+ 'has child features that depend on it and are not part of the archive operation.',
+ );
});
diff --git a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
index 1158c3390d24..6b220f60b3ef 100644
--- a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
+++ b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
@@ -107,16 +107,8 @@ const ArchiveParentError = ({
severity={'error'}
sx={{ m: (theme) => theme.spacing(2, 0) }}
>
-
-
- {ids[0]} has child
- features that depend on it and are not part of the archive
- operation.
-
+ {ids[0]} has child features
+ that depend on it and are not part of the archive operation.
);
}
From 27df65afa3552b84cb4f90ae81bc23f1a47a2396 Mon Sep 17 00:00:00 2001
From: kwasniew
Date: Fri, 13 Oct 2023 13:32:05 +0200
Subject: [PATCH 7/9] test: archive parents error message
---
.../FeatureArchiveDialog/FeatureArchiveDialog.test.tsx | 3 +++
.../common/FeatureArchiveDialog/FeatureArchiveDialog.tsx | 9 ++++++---
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx
index 9128cc5dcdb5..d85c3e7f784d 100644
--- a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx
+++ b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx
@@ -31,6 +31,9 @@ const setupArchiveValidation = (orphanParents: string[]) => {
versionInfo: {
current: { oss: 'version', enterprise: 'version' },
},
+ flags: {
+ dependentFeatures: true,
+ },
});
testServerRoute(
server,
diff --git a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
index 6b220f60b3ef..efbdd33981d5 100644
--- a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
+++ b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
@@ -12,6 +12,7 @@ import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
import { usePendingChangeRequests } from 'hooks/api/getters/usePendingChangeRequests/usePendingChangeRequests';
import { useHighestPermissionChangeRequestEnvironment } from 'hooks/useHighestPermissionChangeRequestEnvironment';
+import { useUiFlag } from '../../../hooks/useUiFlag';
interface IFeatureArchiveDialogProps {
isOpen: boolean;
@@ -290,6 +291,8 @@ export const FeatureArchiveDialog: VFC = ({
isOpen,
);
+ const dependentFeatures = useUiFlag('dependentFeatures');
+
return (
= ({
primaryButtonText={buttonText}
secondaryButtonText='Cancel'
title={dialogTitle}
- disabledPrimaryButton={disableArchive}
+ disabledPrimaryButton={dependentFeatures && disableArchive}
>
= ({
}
/>
0}
+ condition={dependentFeatures && offendingParents.length > 0}
show={
= ({
?
0}
+ condition={dependentFeatures && offendingParents.length > 0}
show={
Date: Fri, 13 Oct 2023 13:57:25 +0200
Subject: [PATCH 8/9] test: archive parents error message
---
.../common/FeatureArchiveDialog/FeatureArchiveDialog.tsx | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
index efbdd33981d5..6ac41ca70ffb 100644
--- a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
+++ b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.tsx
@@ -326,7 +326,9 @@ export const FeatureArchiveDialog: VFC = ({
}
/>
0}
+ condition={
+ dependentFeatures && offendingParents.length > 0
+ }
show={
= ({
?
0}
+ condition={
+ dependentFeatures && offendingParents.length > 0
+ }
show={
Date: Fri, 13 Oct 2023 14:16:35 +0200
Subject: [PATCH 9/9] test: archive parents error message
---
.../common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx | 4 ----
1 file changed, 4 deletions(-)
diff --git a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx
index d85c3e7f784d..8b5bac0d8a77 100644
--- a/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx
+++ b/frontend/src/component/common/FeatureArchiveDialog/FeatureArchiveDialog.test.tsx
@@ -117,10 +117,6 @@ 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(() => {