mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-09 00:18:00 +01:00
feat: show strategies used by segments (#5407)
This PR displays change request usage of segments when such usage is returned from the API. It expects at least #5406 to have been merged before it can be merged. ![image](https://github.com/Unleash/unleash/assets/17786332/c74bb1c9-07f9-4bca-95bb-4ca020398444)
This commit is contained in:
parent
20bfa73b82
commit
b021e7cf85
@ -18,9 +18,10 @@ export const SegmentDelete = ({
|
|||||||
onClose,
|
onClose,
|
||||||
onRemove,
|
onRemove,
|
||||||
}: ISegmentDeleteProps) => {
|
}: ISegmentDeleteProps) => {
|
||||||
const { strategies, loading } = useStrategiesBySegment(segment.id);
|
const { strategies, changeRequestStrategies, loading } =
|
||||||
const canDeleteSegment = strategies?.length === 0;
|
useStrategiesBySegment(segment.id);
|
||||||
|
const canDeleteSegment =
|
||||||
|
strategies?.length === 0 && changeRequestStrategies?.length === 0;
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -42,6 +43,7 @@ export const SegmentDelete = ({
|
|||||||
open={open}
|
open={open}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
strategies={strategies}
|
strategies={strategies}
|
||||||
|
changeRequestStrategies={changeRequestStrategies}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render } from 'utils/testRenderer';
|
||||||
|
import { screen } from '@testing-library/react';
|
||||||
|
import { SegmentDeleteUsedSegment } from './SegmentDeleteUsedSegment';
|
||||||
|
|
||||||
|
describe('SegmentDeleteUsedSegment', () => {
|
||||||
|
it('should link to change requests for change request strategies', async () => {
|
||||||
|
const projectId = 'project1';
|
||||||
|
|
||||||
|
const crStrategies = [
|
||||||
|
{
|
||||||
|
projectId,
|
||||||
|
featureName: 'feature1',
|
||||||
|
strategyName: 'flexible rollout',
|
||||||
|
environment: 'default',
|
||||||
|
changeRequest: { id: 1, title: null },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
projectId,
|
||||||
|
featureName: 'feature1',
|
||||||
|
strategyName: 'flexible rollout',
|
||||||
|
environment: 'default',
|
||||||
|
changeRequest: { id: 2, title: 'My cool CR' },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
render(
|
||||||
|
<SegmentDeleteUsedSegment
|
||||||
|
changeRequestStrategies={crStrategies}
|
||||||
|
segment={{
|
||||||
|
id: 1,
|
||||||
|
name: 'segment',
|
||||||
|
description: 'description',
|
||||||
|
project: projectId,
|
||||||
|
constraints: [],
|
||||||
|
createdAt: '',
|
||||||
|
createdBy: '',
|
||||||
|
}}
|
||||||
|
open={true}
|
||||||
|
strategies={[]}
|
||||||
|
onClose={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const links = await screen.findAllByRole('link');
|
||||||
|
expect(links).toHaveLength(crStrategies.length);
|
||||||
|
|
||||||
|
const [link1, link2] = links;
|
||||||
|
|
||||||
|
expect(link1).toHaveTextContent('#1');
|
||||||
|
expect(link1).toHaveAccessibleDescription('Change request 1');
|
||||||
|
expect(link1).toHaveAttribute(
|
||||||
|
'href',
|
||||||
|
`/projects/${projectId}/change-requests/1`,
|
||||||
|
);
|
||||||
|
expect(link2).toHaveTextContent('#2 (My cool CR)');
|
||||||
|
expect(link2).toHaveAccessibleDescription('Change request 2');
|
||||||
|
expect(link2).toHaveAttribute(
|
||||||
|
'href',
|
||||||
|
`/projects/${projectId}/change-requests/2`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
@ -5,6 +5,12 @@ import { Link } from 'react-router-dom';
|
|||||||
import { formatEditStrategyPath } from 'component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit';
|
import { formatEditStrategyPath } from 'component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit';
|
||||||
import { formatStrategyName } from 'utils/strategyNames';
|
import { formatStrategyName } from 'utils/strategyNames';
|
||||||
import { styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
|
import {
|
||||||
|
ChangeRequestNewStrategy,
|
||||||
|
ChangeRequestStrategy,
|
||||||
|
ChangeRequestUpdatedStrategy,
|
||||||
|
} from 'hooks/api/getters/useStrategiesBySegment/useStrategiesBySegment';
|
||||||
|
import { sortStrategiesByFeature } from './sort-strategies';
|
||||||
|
|
||||||
const StyledUl = styled('ul')({
|
const StyledUl = styled('ul')({
|
||||||
marginBottom: 0,
|
marginBottom: 0,
|
||||||
@ -21,14 +27,29 @@ interface ISegmentDeleteUsedSegmentProps {
|
|||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
strategies: IFeatureStrategy[] | undefined;
|
strategies: IFeatureStrategy[] | undefined;
|
||||||
|
changeRequestStrategies: ChangeRequestStrategy[] | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const formatChangeRequestPath = (
|
||||||
|
projectId: string,
|
||||||
|
changeRequestId: number,
|
||||||
|
): string => {
|
||||||
|
return `/projects/${projectId}/change-requests/${changeRequestId}`;
|
||||||
|
};
|
||||||
|
|
||||||
export const SegmentDeleteUsedSegment = ({
|
export const SegmentDeleteUsedSegment = ({
|
||||||
segment,
|
segment,
|
||||||
open,
|
open,
|
||||||
onClose,
|
onClose,
|
||||||
strategies,
|
strategies,
|
||||||
|
changeRequestStrategies,
|
||||||
}: ISegmentDeleteUsedSegmentProps) => {
|
}: ISegmentDeleteUsedSegmentProps) => {
|
||||||
|
const sortedStrategies = sortStrategiesByFeature<
|
||||||
|
IFeatureStrategy,
|
||||||
|
ChangeRequestUpdatedStrategy,
|
||||||
|
ChangeRequestNewStrategy
|
||||||
|
>(strategies ?? [], changeRequestStrategies ?? []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialogue
|
<Dialogue
|
||||||
title="You can't delete a segment that's currently in use"
|
title="You can't delete a segment that's currently in use"
|
||||||
@ -41,32 +62,74 @@ export const SegmentDeleteUsedSegment = ({
|
|||||||
<strong>{segment.name}</strong> segment for their strategies:
|
<strong>{segment.name}</strong> segment for their strategies:
|
||||||
</p>
|
</p>
|
||||||
<StyledUl>
|
<StyledUl>
|
||||||
{strategies?.map((strategy) => (
|
{sortedStrategies.map((strategy, index) =>
|
||||||
<li key={strategy.id}>
|
strategyListItem(strategy, index),
|
||||||
<StyledLink
|
)}
|
||||||
to={formatEditStrategyPath(
|
|
||||||
strategy.projectId!,
|
|
||||||
strategy.featureName!,
|
|
||||||
strategy.environment!,
|
|
||||||
strategy.id,
|
|
||||||
)}
|
|
||||||
target='_blank'
|
|
||||||
rel='noopener noreferrer'
|
|
||||||
>
|
|
||||||
{strategy.featureName!}{' '}
|
|
||||||
{formatStrategyNameParens(strategy)}
|
|
||||||
</StyledLink>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</StyledUl>
|
</StyledUl>
|
||||||
</Dialogue>
|
</Dialogue>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatStrategyNameParens = (strategy: IFeatureStrategy): string => {
|
const formatStrategyNameParens = (strategy: {
|
||||||
|
strategyName?: string;
|
||||||
|
}): string => {
|
||||||
if (!strategy.strategyName) {
|
if (!strategy.strategyName) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return `(${formatStrategyName(strategy.strategyName)})`;
|
return `(${formatStrategyName(strategy.strategyName)})`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const strategyListItem = (
|
||||||
|
strategy:
|
||||||
|
| IFeatureStrategy
|
||||||
|
| ChangeRequestUpdatedStrategy
|
||||||
|
| ChangeRequestNewStrategy,
|
||||||
|
index: number,
|
||||||
|
) => {
|
||||||
|
const isChangeRequest = (
|
||||||
|
strategy: IFeatureStrategy | ChangeRequestStrategy,
|
||||||
|
): strategy is ChangeRequestStrategy => 'changeRequest' in strategy;
|
||||||
|
|
||||||
|
if (isChangeRequest(strategy)) {
|
||||||
|
const { id, title } = strategy.changeRequest;
|
||||||
|
|
||||||
|
const text = title ? `#${id} (${title})` : `#${id}`;
|
||||||
|
return (
|
||||||
|
<li key={`#${strategy.changeRequest.id}@${index}`}>
|
||||||
|
<p>
|
||||||
|
{strategy.featureName}{' '}
|
||||||
|
{`${formatStrategyNameParens(
|
||||||
|
strategy,
|
||||||
|
)} — in change request `}
|
||||||
|
|
||||||
|
<StyledLink
|
||||||
|
to={formatChangeRequestPath(strategy.projectId, id)}
|
||||||
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
title={`Change request ${id}`}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</StyledLink>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<li key={strategy.id}>
|
||||||
|
<StyledLink
|
||||||
|
to={formatEditStrategyPath(
|
||||||
|
strategy.projectId!,
|
||||||
|
strategy.featureName!,
|
||||||
|
strategy.environment!,
|
||||||
|
strategy.id,
|
||||||
|
)}
|
||||||
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
>
|
||||||
|
{strategy.featureName!} {formatStrategyNameParens(strategy)}
|
||||||
|
</StyledLink>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -5,8 +5,26 @@ import handleErrorResponses from '../httpErrorResponseHandler';
|
|||||||
import { IFeatureStrategy } from 'interfaces/strategy';
|
import { IFeatureStrategy } from 'interfaces/strategy';
|
||||||
import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
|
import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
|
||||||
|
|
||||||
|
export type ChangeRequestInfo = { id: number; title: string | null };
|
||||||
|
export type ChangeRequestNewStrategy = {
|
||||||
|
projectId: string;
|
||||||
|
featureName: string;
|
||||||
|
strategyName: string;
|
||||||
|
environment: string;
|
||||||
|
changeRequest: ChangeRequestInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ChangeRequestUpdatedStrategy = ChangeRequestNewStrategy & {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ChangeRequestStrategy =
|
||||||
|
| ChangeRequestNewStrategy
|
||||||
|
| ChangeRequestUpdatedStrategy;
|
||||||
|
|
||||||
export interface IUseStrategiesBySegmentOutput {
|
export interface IUseStrategiesBySegmentOutput {
|
||||||
strategies: IFeatureStrategy[];
|
strategies: IFeatureStrategy[];
|
||||||
|
changeRequestStrategies: ChangeRequestStrategy[];
|
||||||
refetchUsedSegments: () => void;
|
refetchUsedSegments: () => void;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
error?: Error;
|
error?: Error;
|
||||||
@ -26,6 +44,7 @@ export const useStrategiesBySegment = (
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
strategies: data?.strategies || [],
|
strategies: data?.strategies || [],
|
||||||
|
changeRequestStrategies: data?.changeRequestStrategies || [],
|
||||||
refetchUsedSegments,
|
refetchUsedSegments,
|
||||||
loading: !error && !data,
|
loading: !error && !data,
|
||||||
error,
|
error,
|
||||||
|
Loading…
Reference in New Issue
Block a user