mirror of
https://github.com/Unleash/unleash.git
synced 2025-11-24 20:06:55 +01:00
feat: delete safeguard button (#10974)
This commit is contained in:
parent
684a0ff48c
commit
71099247e7
@ -132,7 +132,7 @@ export const ReleasePlan = ({
|
|||||||
const { removeReleasePlanFromFeature, startReleasePlanMilestone } =
|
const { removeReleasePlanFromFeature, startReleasePlanMilestone } =
|
||||||
useReleasePlansApi();
|
useReleasePlansApi();
|
||||||
const { deleteMilestoneProgression } = useMilestoneProgressionsApi();
|
const { deleteMilestoneProgression } = useMilestoneProgressionsApi();
|
||||||
const { createOrUpdateSafeguard } = useSafeguardsApi();
|
const { createOrUpdateSafeguard, deleteSafeguard } = useSafeguardsApi();
|
||||||
const { setToastData, setToastApiError } = useToast();
|
const { setToastData, setToastApiError } = useToast();
|
||||||
const { trackEvent } = usePlausibleTracker();
|
const { trackEvent } = usePlausibleTracker();
|
||||||
|
|
||||||
@ -400,6 +400,24 @@ export const ReleasePlan = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSafeguardDelete = async () => {
|
||||||
|
try {
|
||||||
|
await deleteSafeguard({
|
||||||
|
projectId,
|
||||||
|
featureName,
|
||||||
|
environment,
|
||||||
|
planId: id,
|
||||||
|
});
|
||||||
|
setToastData({
|
||||||
|
type: 'success',
|
||||||
|
text: 'Safeguard deleted successfully',
|
||||||
|
});
|
||||||
|
refetch();
|
||||||
|
} catch (error: unknown) {
|
||||||
|
setToastApiError(formatUnknownError(error));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<StyledHeader>
|
<StyledHeader>
|
||||||
@ -436,6 +454,7 @@ export const ReleasePlan = ({
|
|||||||
safeguard={safeguards[0]}
|
safeguard={safeguards[0]}
|
||||||
onSubmit={handleSafeguardSubmit}
|
onSubmit={handleSafeguardSubmit}
|
||||||
onCancel={() => setSafeguardFormOpen(false)}
|
onCancel={() => setSafeguardFormOpen(false)}
|
||||||
|
onDelete={handleSafeguardDelete}
|
||||||
/>
|
/>
|
||||||
) : safeguardFormOpen ? (
|
) : safeguardFormOpen ? (
|
||||||
<SafeguardForm
|
<SafeguardForm
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Button, FormControl, TextField } from '@mui/material';
|
import { Button, FormControl, IconButton, TextField } from '@mui/material';
|
||||||
import ShieldIcon from '@mui/icons-material/Shield';
|
import ShieldIcon from '@mui/icons-material/Shield';
|
||||||
|
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
|
||||||
import type { FormEvent } from 'react';
|
import type { FormEvent } from 'react';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useImpactMetricsOptions } from 'hooks/api/getters/useImpactMetricsMetadata/useImpactMetricsMetadata';
|
import { useImpactMetricsOptions } from 'hooks/api/getters/useImpactMetricsMetadata/useImpactMetricsMetadata';
|
||||||
@ -27,6 +28,7 @@ const StyledIcon = createStyledIcon(ShieldIcon);
|
|||||||
interface ISafeguardFormProps {
|
interface ISafeguardFormProps {
|
||||||
onSubmit: (data: CreateSafeguardSchema) => void;
|
onSubmit: (data: CreateSafeguardSchema) => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
|
onDelete?: () => void;
|
||||||
safeguard?: ISafeguard;
|
safeguard?: ISafeguard;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,6 +65,7 @@ const getDefaultAggregationMode = (
|
|||||||
export const SafeguardForm = ({
|
export const SafeguardForm = ({
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onCancel,
|
onCancel,
|
||||||
|
onDelete,
|
||||||
safeguard,
|
safeguard,
|
||||||
}: ISafeguardFormProps) => {
|
}: ISafeguardFormProps) => {
|
||||||
const { metricOptions, loading } = useImpactMetricsOptions();
|
const { metricOptions, loading } = useImpactMetricsOptions();
|
||||||
@ -208,11 +211,27 @@ export const SafeguardForm = ({
|
|||||||
|
|
||||||
const showButtons = mode === 'create' || mode === 'edit';
|
const showButtons = mode === 'create' || mode === 'edit';
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
if (onDelete) {
|
||||||
|
onDelete();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledFormContainer onSubmit={handleSubmit}>
|
<StyledFormContainer onSubmit={handleSubmit}>
|
||||||
<StyledTopRow>
|
<StyledTopRow sx={{ mb: 1 }}>
|
||||||
<StyledIcon />
|
<StyledIcon />
|
||||||
<StyledLabel>Pause automation when</StyledLabel>
|
<StyledLabel>Pause automation when</StyledLabel>
|
||||||
|
{mode !== 'create' && (
|
||||||
|
<IconButton
|
||||||
|
onClick={handleDelete}
|
||||||
|
size='small'
|
||||||
|
aria-label='Delete safeguard'
|
||||||
|
sx={{ padding: 0.5, marginLeft: 'auto' }}
|
||||||
|
>
|
||||||
|
<DeleteOutlineIcon fontSize='small' />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
</StyledTopRow>
|
</StyledTopRow>
|
||||||
<StyledTopRow>
|
<StyledTopRow>
|
||||||
<MetricSelector
|
<MetricSelector
|
||||||
|
|||||||
@ -88,7 +88,7 @@ test('filters by flag type', async () => {
|
|||||||
|
|
||||||
await screen.findByText('Flag type');
|
await screen.findByText('Flag type');
|
||||||
await screen.findByText('Operational');
|
await screen.findByText('Operational');
|
||||||
});
|
}, 10000);
|
||||||
|
|
||||||
test('selects project features', async () => {
|
test('selects project features', async () => {
|
||||||
setupApi();
|
setupApi();
|
||||||
@ -130,7 +130,7 @@ test('selects project features', async () => {
|
|||||||
// deselect a single item
|
// deselect a single item
|
||||||
fireEvent.click(selectFeatureA);
|
fireEvent.click(selectFeatureA);
|
||||||
expect(screen.queryByTestId(BATCH_SELECTED_COUNT)).not.toBeInTheDocument();
|
expect(screen.queryByTestId(BATCH_SELECTED_COUNT)).not.toBeInTheDocument();
|
||||||
});
|
}, 10000);
|
||||||
|
|
||||||
test('filters by tag', async () => {
|
test('filters by tag', async () => {
|
||||||
setupApi();
|
setupApi();
|
||||||
@ -155,7 +155,7 @@ test('filters by tag', async () => {
|
|||||||
|
|
||||||
await screen.findByText('include');
|
await screen.findByText('include');
|
||||||
expect(await screen.findAllByText('backend:sdk')).toHaveLength(2);
|
expect(await screen.findAllByText('backend:sdk')).toHaveLength(2);
|
||||||
});
|
}, 10000);
|
||||||
|
|
||||||
test('filters by flag author', async () => {
|
test('filters by flag author', async () => {
|
||||||
setupApi();
|
setupApi();
|
||||||
@ -184,7 +184,7 @@ test('filters by flag author', async () => {
|
|||||||
fireEvent.click(authorA);
|
fireEvent.click(authorA);
|
||||||
|
|
||||||
expect(window.location.href).toContain('createdBy=IS%3A1');
|
expect(window.location.href).toContain('createdBy=IS%3A1');
|
||||||
});
|
}, 10000);
|
||||||
|
|
||||||
test('Project is onboarded', async () => {
|
test('Project is onboarded', async () => {
|
||||||
const projectId = 'default';
|
const projectId = 'default';
|
||||||
@ -208,7 +208,7 @@ test('Project is onboarded', async () => {
|
|||||||
expect(
|
expect(
|
||||||
screen.queryByText('Welcome to your project'),
|
screen.queryByText('Welcome to your project'),
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
});
|
}, 10000);
|
||||||
|
|
||||||
test('Project is not onboarded', async () => {
|
test('Project is not onboarded', async () => {
|
||||||
const projectId = 'default';
|
const projectId = 'default';
|
||||||
@ -230,7 +230,7 @@ test('Project is not onboarded', async () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
await screen.findByText('Welcome to your project');
|
await screen.findByText('Welcome to your project');
|
||||||
});
|
}, 10000);
|
||||||
|
|
||||||
test('renders lifecycle quick filters', async () => {
|
test('renders lifecycle quick filters', async () => {
|
||||||
setupApi();
|
setupApi();
|
||||||
@ -255,7 +255,7 @@ test('renders lifecycle quick filters', async () => {
|
|||||||
await screen.findByText(/Develop/);
|
await screen.findByText(/Develop/);
|
||||||
await screen.findByText(/Rollout production/);
|
await screen.findByText(/Rollout production/);
|
||||||
await screen.findByText(/Cleanup/);
|
await screen.findByText(/Cleanup/);
|
||||||
});
|
}, 10000);
|
||||||
|
|
||||||
test('clears lifecycle filter when switching to archived view', async () => {
|
test('clears lifecycle filter when switching to archived view', async () => {
|
||||||
setupApi();
|
setupApi();
|
||||||
@ -291,4 +291,4 @@ test('clears lifecycle filter when switching to archived view', async () => {
|
|||||||
|
|
||||||
expect(window.location.href).not.toContain('lifecycle=IS%3Alive');
|
expect(window.location.href).not.toContain('lifecycle=IS%3Alive');
|
||||||
expect(window.location.href).toContain('archived=IS%3Atrue');
|
expect(window.location.href).toContain('archived=IS%3Atrue');
|
||||||
});
|
}, 10000);
|
||||||
|
|||||||
@ -9,6 +9,13 @@ export interface CreateSafeguardParams {
|
|||||||
body: CreateSafeguardSchema;
|
body: CreateSafeguardSchema;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DeleteSafeguardParams {
|
||||||
|
projectId: string;
|
||||||
|
featureName: string;
|
||||||
|
environment: string;
|
||||||
|
planId: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const useSafeguardsApi = () => {
|
export const useSafeguardsApi = () => {
|
||||||
const { makeRequest, createRequest, errors, loading } = useAPI({
|
const { makeRequest, createRequest, errors, loading } = useAPI({
|
||||||
propagateErrors: true,
|
propagateErrors: true,
|
||||||
@ -35,8 +42,28 @@ export const useSafeguardsApi = () => {
|
|||||||
await makeRequest(req.caller, req.id);
|
await makeRequest(req.caller, req.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteSafeguard = async ({
|
||||||
|
projectId,
|
||||||
|
featureName,
|
||||||
|
environment,
|
||||||
|
planId,
|
||||||
|
}: DeleteSafeguardParams): Promise<void> => {
|
||||||
|
const requestId = 'deleteSafeguard';
|
||||||
|
const path = `api/admin/projects/${projectId}/features/${featureName}/environments/${environment}/release_plans/${planId}/safeguards`;
|
||||||
|
const req = createRequest(
|
||||||
|
path,
|
||||||
|
{
|
||||||
|
method: 'DELETE',
|
||||||
|
},
|
||||||
|
requestId,
|
||||||
|
);
|
||||||
|
|
||||||
|
await makeRequest(req.caller, req.id);
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createOrUpdateSafeguard,
|
createOrUpdateSafeguard,
|
||||||
|
deleteSafeguard,
|
||||||
errors,
|
errors,
|
||||||
loading,
|
loading,
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user