1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-12-09 20:04:11 +01:00

chore: add warning about release plans in import-export (#10805)

https://linear.app/unleash/issue/2-3965/add-a-note-if-were-exporting-that-we-dont-understand-release-plans-in

Adds a warning about release plans in import/export.

It's not trivial to know every flag that will be exported in every
scenario, and whether they have release plans, so our logic here is
"have you configured release templates?"

<img width="706" height="516" alt="image"
src="https://github.com/user-attachments/assets/68ba8618-9887-491c-b46e-256b45700d74"
/>

<img width="732" height="503" alt="image"
src="https://github.com/user-attachments/assets/086e37d4-78ae-4647-93a2-5d1845c2758a"
/>
This commit is contained in:
Nuno Góis 2025-10-15 14:44:30 +01:00 committed by GitHub
parent f3ab70aeca
commit 4ff41fa6a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 65 additions and 36 deletions

View File

@ -1,5 +1,5 @@
import { createRef, useState } from 'react';
import { styled, Typography, Box } from '@mui/material';
import { styled, Typography, Box, Alert } from '@mui/material';
import { Dialogue } from 'component/common/Dialogue/Dialogue';
import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect';
import { useExportApi } from 'hooks/api/actions/useExportApi/useExportApi';
@ -8,10 +8,11 @@ import type { FeatureSchema } from 'openapi';
import { formatUnknownError } from 'utils/formatUnknownError';
import { ConditionallyRender } from '../../common/ConditionallyRender/ConditionallyRender.tsx';
import { useReleasePlanTemplates } from 'hooks/api/getters/useReleasePlanTemplates/useReleasePlanTemplates.ts';
interface IExportDialogProps {
showExportDialog: boolean;
data: Pick<FeatureSchema, 'name'>[];
data: Pick<FeatureSchema, 'name' | 'environments'>[];
project?: string;
onClose: () => void;
onConfirm?: () => void;
@ -35,6 +36,8 @@ export const ExportDialog = ({
const { createExport } = useExportApi();
const ref = createRef<HTMLDivElement>();
const { setToastApiError } = useToast();
const { templates } = useReleasePlanTemplates();
const hasReleaseTemplates = Boolean(templates.length);
const getOptions = () =>
environments.map((env) => ({
@ -88,6 +91,13 @@ export const ExportDialog = ({
secondaryButtonText='Cancel'
>
<Box ref={ref}>
{hasReleaseTemplates && (
<Alert severity='warning' sx={{ mb: 4 }}>
Exporting does not include release plans. You may need
to set up new release plans for the imported feature
flags.
</Alert>
)}
<ConditionallyRender
condition={data.length > 0}
show={

View File

@ -1,5 +1,6 @@
import type { FC } from 'react';
import { Box, styled, Typography } from '@mui/material';
import { useReleasePlanTemplates } from 'hooks/api/getters/useReleasePlanTemplates/useReleasePlanTemplates';
const ImportExplanationContainer = styled(Box)(({ theme }) => ({
backgroundColor: theme.palette.background.elevation2,
@ -17,7 +18,10 @@ const ImportExplanationDescription = styled(Box)(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
}));
export const ImportExplanation: FC = () => (
export const ImportExplanation: FC = () => {
const { templates } = useReleasePlanTemplates();
const hasReleaseTemplates = Boolean(templates.length);
return (
<ImportExplanationContainer>
<ImportExplanationHeader>
What is being imported?
@ -36,14 +40,23 @@ export const ImportExplanation: FC = () => (
</ImportExplanationDescription>
<ImportExplanationHeader>Exceptions?</ImportExplanationHeader>
<ImportExplanationDescription>
If the feature flag already exists in the new instance, it will be
overwritten
If the feature flag already exists in the new instance, it will
be overwritten
</ImportExplanationDescription>
<ImportExplanationHeader>What is not imported?</ImportExplanationHeader>
<ImportExplanationHeader>
What is not imported?
</ImportExplanationHeader>
<ImportExplanationDescription sx={{ marginBottom: 0 }}>
If we detect segments or custom strategies in your imported file, we
will stop the import. You need to create them first in the new
instance and run the import again
If we detect segments or custom strategies in your imported
file, we will stop the import. You need to create them first in
the new instance and run the import again
</ImportExplanationDescription>
{hasReleaseTemplates && (
<ImportExplanationDescription sx={{ marginTop: 2 }}>
Release plans are not included in the import. You may need
to set up new release plans for the imported feature flags
</ImportExplanationDescription>
)}
</ImportExplanationContainer>
);
};

View File

@ -1,5 +1,5 @@
import { type FC, type ReactNode, useState } from 'react';
import { Box, IconButton, Tooltip } from '@mui/material';
import { Box, IconButton, styled, Tooltip } from '@mui/material';
import useLoading from 'hooks/useLoading';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
@ -8,6 +8,10 @@ import IosShare from '@mui/icons-material/IosShare';
import { FlagCreationButton } from './FlagCreationButton/FlagCreationButton.tsx';
import { ImportButton } from './ImportButton/ImportButton.tsx';
const StyledIconButton = styled(IconButton)(({ theme }) => ({
padding: theme.spacing(1.5),
}));
type ProjectFeatureTogglesHeaderProps = {
isLoading?: boolean;
totalItems?: number;
@ -39,12 +43,12 @@ export const ProjectFeatureTogglesHeader: FC<
<>
{actions}
<Tooltip title='Export all project flags' arrow>
<IconButton
<StyledIconButton
data-loading
onClick={() => setShowExportDialog(true)}
>
<IosShare />
</IconButton>
</StyledIconButton>
</Tooltip>
<ImportButton />

View File

@ -335,7 +335,8 @@ export default class FakeFeatureStrategiesStore
this.featureStrategies.filter(
(strategy) =>
features.includes(strategy.featureName) &&
strategy.environment === environment,
strategy.environment === environment &&
!strategy.milestoneId,
),
);
}

View File

@ -278,6 +278,7 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
.select(COLUMNS)
.from<IFeatureStrategiesTable>(T.featureStrategies)
.whereIn('feature_name', features)
.andWhere('milestone_id', null)
.orderBy('feature_name', 'asc');
if (environment) {
query.where('environment', environment);