diff --git a/frontend/src/component/feature/FeatureToggleList/FeatureToggleListTable.tsx b/frontend/src/component/feature/FeatureToggleList/FeatureToggleListTable.tsx index 9cdea15fcb..a77ab7a2f2 100644 --- a/frontend/src/component/feature/FeatureToggleList/FeatureToggleListTable.tsx +++ b/frontend/src/component/feature/FeatureToggleList/FeatureToggleListTable.tsx @@ -68,6 +68,7 @@ export const FeatureToggleListTable: VFC = () => { const [showExportDialog, setShowExportDialog] = useState(false); const { features = [], loading, refetchFeatures } = useFeatures(); const [searchParams, setSearchParams] = useSearchParams(); + const { uiConfig } = useUiConfig(); const [initialState] = useState(() => ({ sortBy: [ { @@ -310,16 +311,28 @@ export const FeatureToggleListTable: VFC = () => { > View archive - - setShowExportDialog(true)} - sx={theme => ({ - marginRight: theme.spacing(2), - })} - > - - - + + + setShowExportDialog(true) + } + sx={theme => ({ + marginRight: theme.spacing(2), + })} + > + + + + } + /> { /> } /> - setShowExportDialog(false)} - environments={enabledEnvironments} + setShowExportDialog(false)} + environments={enabledEnvironments} + /> + } /> ); diff --git a/frontend/src/component/project/Project/Project.tsx b/frontend/src/component/project/Project/Project.tsx index ce2ea40bf3..ce02a943d8 100644 --- a/frontend/src/component/project/Project/Project.tsx +++ b/frontend/src/component/project/Project/Project.tsx @@ -139,16 +139,23 @@ export const Project = () => { - setModalOpen(true)} - tooltipProps={{ title: 'Import' }} - data-testid={IMPORT_BUTTON} - data-loading - > - - + setModalOpen(true)} + tooltipProps={{ title: 'Import' }} + data-testid={IMPORT_BUTTON} + data-loading + > + + + } + /> - - - setShowExportDialog(true) - } - sx={theme => ({ - marginRight: theme.spacing(2), - })} - > - - - + + + setShowExportDialog(true) + } + sx={theme => ({ + marginRight: + theme.spacing(2), + })} + > + + + + } + /> navigate(getCreateTogglePath(projectId)) @@ -704,7 +712,10 @@ export const ProjectFeatureToggles = ({ } /> = ({ selectedIds, data, projectId }) => { + const { uiConfig } = useUiConfig(); const [showExportDialog, setShowExportDialog] = useState(false); const { trackEvent } = usePlausibleTracker(); const selectedData = useMemo( @@ -54,12 +56,17 @@ export const ProjectFeaturesBatchActions: FC< - setShowExportDialog(false)} - environments={environments} - onConfirm={trackExport} + setShowExportDialog(false)} + environments={environments} + onConfirm={trackExport} + /> + } /> ); diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index 2815aa40d3..946c87ebb0 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -40,6 +40,7 @@ export interface IFlags { embedProxyFrontend?: boolean; maintenanceMode?: boolean; messageBanner?: boolean; + featuresExportImport?: boolean; newProjectOverview?: boolean; caseInsensitiveInOperators?: boolean; crOnVariants?: boolean; diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index 020bb3c737..13a2003714 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -75,6 +75,7 @@ exports[`should create default config 1`] = ` "crOnVariants": false, "embedProxy": true, "embedProxyFrontend": true, + "featuresExportImport": true, "loginHistory": false, "maintenanceMode": false, "messageBanner": false, @@ -101,6 +102,7 @@ exports[`should create default config 1`] = ` "crOnVariants": false, "embedProxy": true, "embedProxyFrontend": true, + "featuresExportImport": true, "loginHistory": false, "maintenanceMode": false, "messageBanner": false, diff --git a/src/lib/features/export-import-toggles/export-import-controller.ts b/src/lib/features/export-import-toggles/export-import-controller.ts index 1310df1c1d..dab6d3a52d 100644 --- a/src/lib/features/export-import-toggles/export-import-controller.ts +++ b/src/lib/features/export-import-toggles/export-import-controller.ts @@ -21,6 +21,7 @@ import { } from '../../openapi'; import { IAuthRequest } from '../../routes/unleash-types'; import { extractUsername } from '../../util'; +import { InvalidOperationError } from '../../error'; class ExportImportController extends Controller { private logger: Logger; @@ -118,6 +119,7 @@ class ExportImportController extends Controller { req: IAuthRequest, res: Response, ): Promise { + this.verifyExportImportEnabled(); const query = req.body; const userName = extractUsername(req); const data = await this.exportImportService.export(query, userName); @@ -134,6 +136,7 @@ class ExportImportController extends Controller { req: IAuthRequest, res: Response, ): Promise { + this.verifyExportImportEnabled(); const dto = req.body; const { user } = req; const validation = await this.startTransaction(async (tx) => @@ -152,6 +155,7 @@ class ExportImportController extends Controller { req: IAuthRequest, res: Response, ): Promise { + this.verifyExportImportEnabled(); const dto = req.body; const { user } = req; await this.startTransaction(async (tx) => @@ -160,5 +164,13 @@ class ExportImportController extends Controller { res.status(200).end(); } + + private verifyExportImportEnabled() { + if (!this.config.flagResolver.isEnabled('featuresExportImport')) { + throw new InvalidOperationError( + 'Feature export/import is not enabled', + ); + } + } } export default ExportImportController; diff --git a/src/lib/features/export-import-toggles/export-import-permissions.e2e.test.ts b/src/lib/features/export-import-toggles/export-import-permissions.e2e.test.ts index 618e3281fb..cbd114f258 100644 --- a/src/lib/features/export-import-toggles/export-import-permissions.e2e.test.ts +++ b/src/lib/features/export-import-toggles/export-import-permissions.e2e.test.ts @@ -233,7 +233,9 @@ beforeAll(async () => { db.stores, { experimental: { - flags: {}, + flags: { + featuresExportImport: true, + }, }, }, db.rawDatabase, diff --git a/src/lib/features/export-import-toggles/export-import.e2e.test.ts b/src/lib/features/export-import-toggles/export-import.e2e.test.ts index 8b0b2a2e72..924fc8eaa2 100644 --- a/src/lib/features/export-import-toggles/export-import.e2e.test.ts +++ b/src/lib/features/export-import-toggles/export-import.e2e.test.ts @@ -138,7 +138,9 @@ beforeAll(async () => { db.stores, { experimental: { - flags: {}, + flags: { + featuresExportImport: true, + }, }, }, db.rawDatabase, diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index 39c630665a..a92fa1b87a 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -34,6 +34,10 @@ const flags = { process.env.UNLEASH_EXPERIMENTAL_MESSAGE_BANNER, false, ), + featuresExportImport: parseEnvVarBoolean( + process.env.UNLEASH_EXPERIMENTAL_FEATURES_EXPORT_IMPORT, + true, + ), caseInsensitiveInOperators: parseEnvVarBoolean( process.env.UNLEASH_EXPERIMENTAL_CASE_INSENSITIVE_IN_OPERATORS, false,