mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-31 01:16:01 +02:00
Feat: scheduled change request badges (#5300)
Adds a new badge to strategies that have changes in an a scheduled change request Closes # [1-1620](https://linear.app/unleash/issue/1-1620/create-a-new-badge-for-flag-that-is-part-of-scheduled-change) <img width="1671" alt="Screenshot 2023-11-09 at 11 49 53" src="https://github.com/Unleash/unleash/assets/104830839/596abbc0-f9ab-4642-9ed2-79ef50fb6c05"> --------- Signed-off-by: andreas-unleash <andreas@getunleash.ai> Co-authored-by: Thomas Heartman <thomas@getunleash.io>
This commit is contained in:
parent
ece5a634bf
commit
100c22b42a
@ -0,0 +1,40 @@
|
|||||||
|
import { Box, useMediaQuery, useTheme } from '@mui/material';
|
||||||
|
import { StyledLink } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelDetails/StyledRow';
|
||||||
|
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';
|
||||||
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
|
import { Badge } from 'component/common/Badge/Badge';
|
||||||
|
|
||||||
|
export interface IChangesScheduledBadgeProps {
|
||||||
|
scheduledChangeRequestIds: number[];
|
||||||
|
}
|
||||||
|
export const ChangesScheduledBadge = ({
|
||||||
|
scheduledChangeRequestIds,
|
||||||
|
}: IChangesScheduledBadgeProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const isSmallScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
||||||
|
const project = useRequiredPathParam('projectId');
|
||||||
|
if (isSmallScreen) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ mr: 1.5 }}>
|
||||||
|
<TooltipLink
|
||||||
|
tooltip={
|
||||||
|
<>
|
||||||
|
{scheduledChangeRequestIds?.map((id, index) => (
|
||||||
|
<StyledLink
|
||||||
|
key={`${project}-${index}`}
|
||||||
|
to={`/projects/${project}/change-requests/${id}`}
|
||||||
|
>
|
||||||
|
Change request #{id}
|
||||||
|
</StyledLink>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Badge color='warning'>Changes Scheduled</Badge>
|
||||||
|
</TooltipLink>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,398 @@
|
|||||||
|
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
||||||
|
import { render } from 'utils/testRenderer';
|
||||||
|
import { StrategyDraggableItem } from './StrategyDraggableItem';
|
||||||
|
import { vi } from 'vitest';
|
||||||
|
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||||
|
import { screen } from '@testing-library/dom';
|
||||||
|
import { Route, Routes } from 'react-router-dom';
|
||||||
|
import {
|
||||||
|
IChangeRequest,
|
||||||
|
ChangeRequestAction,
|
||||||
|
} from 'component/changeRequest/changeRequest.types';
|
||||||
|
|
||||||
|
const server = testServerSetup();
|
||||||
|
|
||||||
|
const strategy = {
|
||||||
|
name: 'flexibleRollout',
|
||||||
|
constraints: [],
|
||||||
|
variants: [],
|
||||||
|
parameters: {
|
||||||
|
groupId: 'CR-toggle',
|
||||||
|
rollout: '100',
|
||||||
|
stickiness: 'default',
|
||||||
|
},
|
||||||
|
sortOrder: 0,
|
||||||
|
id: 'b6363cc8-ad8e-478a-b464-484bbd3b31f6',
|
||||||
|
title: '',
|
||||||
|
disabled: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const draftRequest = (
|
||||||
|
action: Omit<ChangeRequestAction, 'updateSegment'> = 'updateStrategy',
|
||||||
|
createdBy = 1,
|
||||||
|
): IChangeRequest => {
|
||||||
|
return {
|
||||||
|
id: 71,
|
||||||
|
title: 'Change request #71',
|
||||||
|
environment: 'production',
|
||||||
|
minApprovals: 1,
|
||||||
|
project: 'dafault',
|
||||||
|
createdBy: {
|
||||||
|
id: createdBy,
|
||||||
|
username: 'admin',
|
||||||
|
imageUrl:
|
||||||
|
'https://gravatar.com/avatar/8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918?s=42&d=retro&r=g',
|
||||||
|
},
|
||||||
|
createdAt: new Date('2023-11-08T10:28:47.183Z'),
|
||||||
|
features: [
|
||||||
|
{
|
||||||
|
name: 'feature1',
|
||||||
|
changes: [
|
||||||
|
{
|
||||||
|
id: 84,
|
||||||
|
action: action as any,
|
||||||
|
payload: {
|
||||||
|
id: 'b6363cc8-ad8e-478a-b464-484bbd3b31f6',
|
||||||
|
name: 'flexibleRollout',
|
||||||
|
title: '',
|
||||||
|
disabled: false,
|
||||||
|
segments: [],
|
||||||
|
variants: [],
|
||||||
|
parameters: {
|
||||||
|
groupId: 'CR-toggle',
|
||||||
|
rollout: '15',
|
||||||
|
stickiness: 'default',
|
||||||
|
},
|
||||||
|
constraints: [],
|
||||||
|
},
|
||||||
|
createdAt: new Date('2023-11-08T10:28:47.183Z'),
|
||||||
|
createdBy: {
|
||||||
|
id: 1,
|
||||||
|
username: 'admin',
|
||||||
|
imageUrl:
|
||||||
|
'https://gravatar.com/avatar/8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918?s=42&d=retro&r=g',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
segments: [],
|
||||||
|
approvals: [],
|
||||||
|
rejections: [],
|
||||||
|
comments: [],
|
||||||
|
state: 'In review',
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const scheduledRequest = (
|
||||||
|
action: Omit<ChangeRequestAction, 'updateSegment'>,
|
||||||
|
) => ({
|
||||||
|
...draftRequest(action),
|
||||||
|
state: 'Scheduled',
|
||||||
|
schedule: {
|
||||||
|
scheduledAt: new Date().toISOString(),
|
||||||
|
status: 'pending',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const uiConfig = () => {
|
||||||
|
testServerRoute(server, '/api/admin/ui-config', {
|
||||||
|
versionInfo: {
|
||||||
|
current: { oss: 'version', enterprise: 'version' },
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
scheduledConfigurationChanges: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const user = () => {
|
||||||
|
testServerRoute(server, '/api/admin/user', {
|
||||||
|
user: {
|
||||||
|
isAPI: false,
|
||||||
|
id: 1,
|
||||||
|
name: 'Some User',
|
||||||
|
email: 'user@example.com',
|
||||||
|
imageUrl:
|
||||||
|
'https://gravatar.com/avatar/8aa1132e102345f8c79322340e15340?size=42&default=retro',
|
||||||
|
seenAt: '2022-11-28T14:55:18.982Z',
|
||||||
|
loginAttempts: 0,
|
||||||
|
createdAt: '2022-11-23T13:31:17.061Z',
|
||||||
|
},
|
||||||
|
permissions: [{ permission: ADMIN }],
|
||||||
|
feedback: [],
|
||||||
|
splash: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const changeRequestConfig = () =>
|
||||||
|
testServerRoute(
|
||||||
|
server,
|
||||||
|
'/api/admin/projects/default/change-requests/config',
|
||||||
|
[
|
||||||
|
{
|
||||||
|
environment: 'development',
|
||||||
|
type: 'development',
|
||||||
|
changeRequestEnabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
environment: 'production',
|
||||||
|
type: 'production',
|
||||||
|
changeRequestEnabled: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'get',
|
||||||
|
);
|
||||||
|
|
||||||
|
const feature = () => {
|
||||||
|
testServerRoute(server, '/api/admin/projects/default/features/feature1', {
|
||||||
|
environments: [
|
||||||
|
{
|
||||||
|
name: 'development',
|
||||||
|
lastSeenAt: null,
|
||||||
|
variants: [],
|
||||||
|
enabled: false,
|
||||||
|
type: 'development',
|
||||||
|
sortOrder: 2,
|
||||||
|
strategies: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'production',
|
||||||
|
lastSeenAt: null,
|
||||||
|
variants: [],
|
||||||
|
enabled: false,
|
||||||
|
type: 'production',
|
||||||
|
sortOrder: 3,
|
||||||
|
strategies: [
|
||||||
|
{
|
||||||
|
name: 'flexibleRollout',
|
||||||
|
constraints: [],
|
||||||
|
variants: [],
|
||||||
|
parameters: {
|
||||||
|
groupId: 'CR-toggle',
|
||||||
|
rollout: '100',
|
||||||
|
stickiness: 'default',
|
||||||
|
},
|
||||||
|
sortOrder: 0,
|
||||||
|
id: 'b6363cc8-ad8e-478a-b464-484bbd3b31f6',
|
||||||
|
title: '',
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
name: 'feature1',
|
||||||
|
favorite: false,
|
||||||
|
impressionData: false,
|
||||||
|
description: null,
|
||||||
|
project: 'MyNewProject',
|
||||||
|
stale: false,
|
||||||
|
lastSeenAt: null,
|
||||||
|
createdAt: '2023-11-01T10:11:58.505Z',
|
||||||
|
type: 'release',
|
||||||
|
variants: [],
|
||||||
|
archived: false,
|
||||||
|
dependencies: [],
|
||||||
|
children: [],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const setupOtherServerRoutes = () => {
|
||||||
|
uiConfig();
|
||||||
|
changeRequestConfig();
|
||||||
|
user();
|
||||||
|
feature();
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
setupOtherServerRoutes();
|
||||||
|
});
|
||||||
|
|
||||||
|
const Component = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Routes>
|
||||||
|
<Route
|
||||||
|
path={'/projects/:projectId/features/:featureId'}
|
||||||
|
element={
|
||||||
|
<StrategyDraggableItem
|
||||||
|
strategy={strategy}
|
||||||
|
environmentName={'production'}
|
||||||
|
index={1}
|
||||||
|
onDragStartRef={vi.fn()}
|
||||||
|
onDragOver={vi.fn()}
|
||||||
|
onDragEnd={vi.fn()}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Routes>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
describe('Change request badges for strategies', () => {
|
||||||
|
test('should not render a badge if no changes', async () => {
|
||||||
|
testServerRoute(
|
||||||
|
server,
|
||||||
|
'/api/admin/projects/default/change-requests/pending/feature1',
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<Component />, {
|
||||||
|
route: '/projects/default/features/feature1',
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
permission: ADMIN,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(screen.queryByText('Modified in draft')).toBe(null);
|
||||||
|
expect(screen.queryByText('Changes Scheduled')).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should only render the "Modified in draft" badge when logged in user is the creator of change request', async () => {
|
||||||
|
const changeRequest = draftRequest('updateStrategy', 5);
|
||||||
|
|
||||||
|
testServerRoute(
|
||||||
|
server,
|
||||||
|
'/api/admin/projects/default/change-requests/pending/feature1',
|
||||||
|
[changeRequest],
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<Component />, {
|
||||||
|
route: '/projects/default/features/feature1',
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
permission: ADMIN,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(screen.queryByText('Modified in draft')).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render a "Modified in draft" badge when "updateStrategy" action exists in "pending" change request', async () => {
|
||||||
|
testServerRoute(
|
||||||
|
server,
|
||||||
|
'/api/admin/projects/default/change-requests/pending/feature1',
|
||||||
|
[draftRequest('updateStrategy', 1)],
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<Component />, {
|
||||||
|
route: '/projects/default/features/feature1',
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
permission: ADMIN,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await screen.findByText('Modified in draft');
|
||||||
|
expect(screen.queryByText('Changes Scheduled')).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render a "Deleted in draft" badge when "deleteStrategy" action exists in "pending" change request', async () => {
|
||||||
|
testServerRoute(
|
||||||
|
server,
|
||||||
|
'/api/admin/projects/default/change-requests/pending/feature1',
|
||||||
|
[draftRequest('deleteStrategy', 1)],
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<Component />, {
|
||||||
|
route: '/projects/default/features/feature1',
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
permission: ADMIN,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await screen.findByText('Deleted in draft');
|
||||||
|
expect(screen.queryByText('Changes Scheduled')).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render a "Changes scheduled" badge when "updateStrategy" action exists in "Scheduled" change request', async () => {
|
||||||
|
testServerRoute(
|
||||||
|
server,
|
||||||
|
'/api/admin/projects/default/change-requests/pending/feature1',
|
||||||
|
[scheduledRequest('updateStrategy')],
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<Component />, {
|
||||||
|
route: '/projects/default/features/feature1',
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
permission: ADMIN,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await screen.findByText('Changes Scheduled');
|
||||||
|
expect(screen.queryByText('Modified in draft')).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render a "Changes Scheduled" badge when "deleteStrategy" action exists in "Scheduled" change request', async () => {
|
||||||
|
testServerRoute(
|
||||||
|
server,
|
||||||
|
'/api/admin/projects/default/change-requests/pending/feature1',
|
||||||
|
[scheduledRequest('deleteStrategy')],
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<Component />, {
|
||||||
|
route: '/projects/default/features/feature1',
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
permission: ADMIN,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await screen.findByText('Changes Scheduled');
|
||||||
|
expect(screen.queryByText('Modified in draft')).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render a both badges when "updateStrategy" action exists in "Scheduled" and pending change request', async () => {
|
||||||
|
testServerRoute(
|
||||||
|
server,
|
||||||
|
'/api/admin/projects/default/change-requests/pending/feature1',
|
||||||
|
[
|
||||||
|
scheduledRequest('updateStrategy'),
|
||||||
|
draftRequest('updateStrategy', 1),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<Component />, {
|
||||||
|
route: '/projects/default/features/feature1',
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
permission: ADMIN,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await screen.findByText('Changes Scheduled');
|
||||||
|
await screen.findByText('Modified in draft');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render a both badges when "deleteStrategy" action exists in "Scheduled" and pending change request', async () => {
|
||||||
|
testServerRoute(
|
||||||
|
server,
|
||||||
|
'/api/admin/projects/default/change-requests/pending/feature1',
|
||||||
|
[
|
||||||
|
scheduledRequest('deleteStrategy'),
|
||||||
|
draftRequest('deleteStrategy', 1),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<Component />, {
|
||||||
|
route: '/projects/default/features/feature1',
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
permission: ADMIN,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await screen.findByText('Changes Scheduled');
|
||||||
|
await screen.findByText('Deleted in draft');
|
||||||
|
});
|
||||||
|
});
|
@ -6,9 +6,13 @@ import { IFeatureEnvironment } from 'interfaces/featureToggle';
|
|||||||
import { IFeatureStrategy } from 'interfaces/strategy';
|
import { IFeatureStrategy } from 'interfaces/strategy';
|
||||||
import { StrategyItem } from './StrategyItem/StrategyItem';
|
import { StrategyItem } from './StrategyItem/StrategyItem';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { Badge } from 'component/common/Badge/Badge';
|
import {
|
||||||
|
useStrategyChangesFromRequest,
|
||||||
|
UseStrategyChangeFromRequestResult,
|
||||||
|
} from './StrategyItem/useStrategyChangesFromRequest';
|
||||||
|
import { ChangesScheduledBadge } from 'component/changeRequest/ModifiedInChangeRequestStatusBadge/ChangesScheduledBadge';
|
||||||
import { IFeatureChange } from 'component/changeRequest/changeRequest.types';
|
import { IFeatureChange } from 'component/changeRequest/changeRequest.types';
|
||||||
import { useStrategyChangeFromRequest } from './StrategyItem/useStrategyChangeFromRequest';
|
import { Badge } from 'component/common/Badge/Badge';
|
||||||
|
|
||||||
interface IStrategyDraggableItemProps {
|
interface IStrategyDraggableItemProps {
|
||||||
strategy: IFeatureStrategy;
|
strategy: IFeatureStrategy;
|
||||||
@ -26,6 +30,7 @@ interface IStrategyDraggableItemProps {
|
|||||||
) => DragEventHandler<HTMLDivElement>;
|
) => DragEventHandler<HTMLDivElement>;
|
||||||
onDragEnd: () => void;
|
onDragEnd: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StrategyDraggableItem = ({
|
export const StrategyDraggableItem = ({
|
||||||
strategy,
|
strategy,
|
||||||
index,
|
index,
|
||||||
@ -39,7 +44,7 @@ export const StrategyDraggableItem = ({
|
|||||||
const projectId = useRequiredPathParam('projectId');
|
const projectId = useRequiredPathParam('projectId');
|
||||||
const featureId = useRequiredPathParam('featureId');
|
const featureId = useRequiredPathParam('featureId');
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const change = useStrategyChangeFromRequest(
|
const strategyChangesFromRequest = useStrategyChangesFromRequest(
|
||||||
projectId,
|
projectId,
|
||||||
featureId,
|
featureId,
|
||||||
environmentName,
|
environmentName,
|
||||||
@ -65,7 +70,9 @@ export const StrategyDraggableItem = ({
|
|||||||
onDragStart={onDragStartRef(ref, index)}
|
onDragStart={onDragStartRef(ref, index)}
|
||||||
onDragEnd={onDragEnd}
|
onDragEnd={onDragEnd}
|
||||||
orderNumber={index + 1}
|
orderNumber={index + 1}
|
||||||
headerChildren={<ChangeRequestStatusBadge change={change} />}
|
headerChildren={renderHeaderChildren(
|
||||||
|
strategyChangesFromRequest,
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
@ -96,3 +103,36 @@ const ChangeRequestStatusBadge = ({
|
|||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderHeaderChildren = (
|
||||||
|
changes: UseStrategyChangeFromRequestResult,
|
||||||
|
): JSX.Element[] => {
|
||||||
|
const badges: JSX.Element[] = [];
|
||||||
|
if (changes.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const draftChange = changes.find(
|
||||||
|
({ isScheduledChange }) => !isScheduledChange,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (draftChange) {
|
||||||
|
badges.push(<ChangeRequestStatusBadge change={draftChange.change} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
const scheduledChanges = changes.filter(
|
||||||
|
({ isScheduledChange }) => isScheduledChange,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (scheduledChanges.length > 0) {
|
||||||
|
badges.push(
|
||||||
|
<ChangesScheduledBadge
|
||||||
|
scheduledChangeRequestIds={scheduledChanges.map(
|
||||||
|
(scheduledChange) => scheduledChange.changeRequestId,
|
||||||
|
)}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return badges;
|
||||||
|
};
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
import { usePendingChangeRequests } from 'hooks/api/getters/usePendingChangeRequests/usePendingChangeRequests';
|
|
||||||
|
|
||||||
export const useStrategyChangeFromRequest = (
|
|
||||||
projectId: string,
|
|
||||||
featureId: string,
|
|
||||||
environment: string,
|
|
||||||
strategyId: string,
|
|
||||||
) => {
|
|
||||||
const { data } = usePendingChangeRequests(projectId);
|
|
||||||
|
|
||||||
const environmentDraft = data?.find(
|
|
||||||
(draft) => draft.environment === environment,
|
|
||||||
);
|
|
||||||
const feature = environmentDraft?.features.find(
|
|
||||||
(feature) => feature.name === featureId,
|
|
||||||
);
|
|
||||||
const change = feature?.changes.find((change) => {
|
|
||||||
if (
|
|
||||||
change.action === 'updateStrategy' ||
|
|
||||||
change.action === 'deleteStrategy'
|
|
||||||
) {
|
|
||||||
return change.payload.id === strategyId;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
return change;
|
|
||||||
};
|
|
@ -0,0 +1,66 @@
|
|||||||
|
import { IFeatureChange } from 'component/changeRequest/changeRequest.types';
|
||||||
|
import { usePendingChangeRequestsForFeature } from 'hooks/api/getters/usePendingChangeRequestsForFeature/usePendingChangeRequestsForFeature';
|
||||||
|
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
|
||||||
|
|
||||||
|
export type UseStrategyChangeFromRequestResult = Array<{
|
||||||
|
changeRequestId: number;
|
||||||
|
change: IFeatureChange;
|
||||||
|
isScheduledChange: boolean;
|
||||||
|
}>;
|
||||||
|
export const useStrategyChangesFromRequest = (
|
||||||
|
projectId: string,
|
||||||
|
featureId: string,
|
||||||
|
environment: string,
|
||||||
|
strategyId: string,
|
||||||
|
) => {
|
||||||
|
const { user } = useAuthUser();
|
||||||
|
|
||||||
|
const { changeRequests } = usePendingChangeRequestsForFeature(
|
||||||
|
projectId,
|
||||||
|
featureId,
|
||||||
|
);
|
||||||
|
const result: UseStrategyChangeFromRequestResult = [];
|
||||||
|
|
||||||
|
const environmentDraftOrScheduled = changeRequests?.filter(
|
||||||
|
(changeRequest) => changeRequest.environment === environment,
|
||||||
|
);
|
||||||
|
|
||||||
|
environmentDraftOrScheduled?.forEach((draftOrScheduled) => {
|
||||||
|
const feature = draftOrScheduled?.features.find(
|
||||||
|
(feature) => feature.name === featureId,
|
||||||
|
);
|
||||||
|
const change = feature?.changes.find((change) => {
|
||||||
|
if (
|
||||||
|
change.action === 'updateStrategy' ||
|
||||||
|
change.action === 'deleteStrategy'
|
||||||
|
) {
|
||||||
|
return change.payload.id === strategyId;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (change) {
|
||||||
|
const isScheduledChange = draftOrScheduled.state === 'Scheduled';
|
||||||
|
const isOwnDraft =
|
||||||
|
!isScheduledChange &&
|
||||||
|
draftOrScheduled.createdBy.id === user?.id;
|
||||||
|
|
||||||
|
if (isScheduledChange) {
|
||||||
|
result.push({
|
||||||
|
changeRequestId: draftOrScheduled.id,
|
||||||
|
change,
|
||||||
|
isScheduledChange,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOwnDraft) {
|
||||||
|
result.push({
|
||||||
|
changeRequestId: draftOrScheduled.id,
|
||||||
|
change,
|
||||||
|
isScheduledChange,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user