From 5dd8616c7403e008842f73cb37729f27863af909 Mon Sep 17 00:00:00 2001 From: sjaanus Date: Wed, 2 Nov 2022 07:34:14 +0100 Subject: [PATCH] Rename suggest changes to change request (#2311) * Rename change request * Merge with review status * Move events and permissions --- .../ChangeRequest/ChangeRequest.tsx} | 45 ++++----- .../ChangeRequestConfirmDialog.tsx} | 10 +- .../ChangeRequestFeatureToggleChange.tsx} | 6 +- .../StrategyChange.tsx | 0 .../ToggleStatusChange.tsx | 0 .../ChangeRequestHeader.tsx} | 18 ++-- .../ChangeRequestOverview.tsx} | 32 +++--- .../ChangeRequestReviewStatus.styles.ts} | 0 .../ChangeRequestReviewStatus.tsx} | 4 +- .../ChangeRequestReviewers.tsx} | 2 +- .../ChangeRequestTimeline.tsx} | 2 +- .../ChangeRequestSidebar.tsx} | 46 ++++----- .../DraftBanner/DraftBanner.tsx | 8 +- .../AvatarCell/AvatarCell.tsx | 0 .../ChangeRequestActionCell.tsx} | 2 +- .../ChangeRequestStatusCell.tsx} | 16 +-- .../ChangeRequestTitleCell.tsx} | 10 +- .../ChangeRequestsTabs.styles.ts} | 0 .../ChangeRequestsTabs.tsx} | 48 ++++----- .../ProjectChangeRequests.tsx} | 14 +-- .../FeatureOverviewEnvSwitch.tsx | 28 +++--- .../feature/FeatureView/FeatureView.tsx | 4 +- .../src/component/project/Project/Project.tsx | 24 ++--- .../ProjectFeatureToggles.tsx | 30 +++--- .../useChangeRequestApi.ts} | 30 +++--- .../useChangeRequest.ts} | 6 +- .../useChangeRequestDraft.ts} | 10 +- .../useProjectChangeRequests.ts} | 8 +- frontend/src/hooks/useChangeRequestToggle.ts | 67 +++++++++++++ frontend/src/hooks/useSuggestToggle.ts | 68 ------------- frontend/src/interfaces/suggestChangeset.ts | 29 +----- frontend/src/interfaces/uiConfig.ts | 2 +- .../__snapshots__/create-config.test.ts.snap | 4 +- src/lib/types/events.ts | 2 - src/lib/types/experimental.ts | 6 +- src/lib/types/model.ts | 98 +------------------ src/lib/types/permissions.ts | 1 - ...0221901130645-add-change-requests-table.js | 40 ++++++++ src/server-dev.ts | 2 +- src/test/config/test-config.ts | 2 +- .../admin/conditional-middleware.e2e.test.ts | 16 +-- website/docs/api/admin/events-api.md | 2 +- 42 files changed, 328 insertions(+), 414 deletions(-) rename frontend/src/component/{suggestChanges/SuggestedChangeset/SuggestedChangeset.tsx => changeRequest/ChangeRequest/ChangeRequest.tsx} (66%) rename frontend/src/component/{suggestChanges/SuggestChangeConfirmDialog/SuggestChangeConfirmDialog.tsx => changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog.tsx} (81%) rename frontend/src/component/{suggestChanges/SuggestedChangeOverview/SuggestedFeatureToggleChange/SuggestedFeatureToggleChange.tsx => changeRequest/ChangeRequestOverview/ChangeRequestFeatureToggleChange/ChangeRequestFeatureToggleChange.tsx} (92%) rename frontend/src/component/{suggestChanges/SuggestedChangeOverview/SuggestedFeatureToggleChange => changeRequest/ChangeRequestOverview/ChangeRequestFeatureToggleChange}/StrategyChange.tsx (100%) rename frontend/src/component/{suggestChanges/SuggestedChangeOverview/SuggestedFeatureToggleChange => changeRequest/ChangeRequestOverview/ChangeRequestFeatureToggleChange}/ToggleStatusChange.tsx (100%) rename frontend/src/component/{suggestChanges/SuggestedChangeOverview/SuggestedChangeHeader/SuggestedChangeHeader.tsx => changeRequest/ChangeRequestOverview/ChangeRequestHeader/ChangeRequestHeader.tsx} (81%) rename frontend/src/component/{suggestChanges/SuggestedChangeOverview/SuggestedChangeOverview.tsx => changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx} (63%) rename frontend/src/component/{suggestChanges/SuggestedChangeOverview/SuggestedChangeReviewStatus/SuggestChangeReviewStatus.styles.ts => changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.styles.ts} (100%) rename frontend/src/component/{suggestChanges/SuggestedChangeOverview/SuggestedChangeReviewStatus/SuggestedChangeReviewStatus.tsx => changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.tsx} (97%) rename frontend/src/component/{suggestChanges/SuggestedChangeOverview/SuggestedChangeReviewers/SuggestedChangeReviewers.tsx => changeRequest/ChangeRequestOverview/ChangeRequestReviewers/ChangeRequestReviewers.tsx} (89%) rename frontend/src/component/{suggestChanges/SuggestedChangeOverview/SuggestedChangeTimeline/SuggestedChangeTimeline.tsx => changeRequest/ChangeRequestOverview/ChangeRequestTimeline/ChangeRequestTimeline.tsx} (97%) rename frontend/src/component/{suggestChanges/SuggestedChangesSidebar/SuggestedChangesSidebar.tsx => changeRequest/ChangeRequestSidebar/ChangeRequestSidebar.tsx} (82%) rename frontend/src/component/{suggestChanges => changeRequest}/DraftBanner/DraftBanner.tsx (90%) rename frontend/src/component/{suggest-changes/ProjectSuggestions/SuggestionsTabs => changeRequest/ProjectChangeRequests/ChangeRequestsTabs}/AvatarCell/AvatarCell.tsx (100%) rename frontend/src/component/{suggest-changes/ProjectSuggestions/SuggestionsTabs/ChangesetActionCell/ChangesetActionCell.tsx => changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestActionCell/ChangeRequestActionCell.tsx} (88%) rename frontend/src/component/{suggest-changes/ProjectSuggestions/SuggestionsTabs/ChangesetStatusCell/ChangesetStatusCell.tsx => changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestStatusCell/ChangeRequestStatusCell.tsx} (88%) rename frontend/src/component/{suggest-changes/ProjectSuggestions/SuggestionsTabs/ChangesetTitleCell/ChangesetTitleCell.tsx => changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestTitleCell/ChangeRequestTitleCell.tsx} (88%) rename frontend/src/component/{suggest-changes/ProjectSuggestions/SuggestionsTabs/SuggestionsTabs.styles.ts => changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestsTabs.styles.ts} (100%) rename frontend/src/component/{suggest-changes/ProjectSuggestions/SuggestionsTabs/SuggestionsTabs.tsx => changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestsTabs.tsx} (87%) rename frontend/src/component/{suggest-changes/ProjectSuggestions/ProjectSuggestedChanges.tsx => changeRequest/ProjectChangeRequests/ProjectChangeRequests.tsx} (65%) rename frontend/src/hooks/api/actions/{useSuggestChangeApi/useSuggestChangeApi.ts => useChangeRequestApi/useChangeRequestApi.ts} (69%) rename frontend/src/hooks/api/getters/{useSuggestChange/useSuggestedChange.ts => useChangeRequest/useChangeRequest.ts} (69%) rename frontend/src/hooks/api/getters/{useSuggestedChangesDraft/useSuggestedChangesDraft.ts => useChangeRequestDraft/useChangeRequestDraft.ts} (76%) rename frontend/src/hooks/api/getters/{useProjectSuggestedChanges/useProjectSuggestedChanges.ts => useProjectChangeRequests/useProjectChangeRequests.ts} (70%) create mode 100644 frontend/src/hooks/useChangeRequestToggle.ts delete mode 100644 frontend/src/hooks/useSuggestToggle.ts create mode 100644 src/migrations/20221901130645-add-change-requests-table.js diff --git a/frontend/src/component/suggestChanges/SuggestedChangeset/SuggestedChangeset.tsx b/frontend/src/component/changeRequest/ChangeRequest/ChangeRequest.tsx similarity index 66% rename from frontend/src/component/suggestChanges/SuggestedChangeset/SuggestedChangeset.tsx rename to frontend/src/component/changeRequest/ChangeRequest/ChangeRequest.tsx index 9775aa0717..1263ce7c48 100644 --- a/frontend/src/component/suggestChanges/SuggestedChangeset/SuggestedChangeset.tsx +++ b/frontend/src/component/changeRequest/ChangeRequest/ChangeRequest.tsx @@ -1,45 +1,36 @@ -import { useCallback, VFC } from 'react'; +import { VFC } from 'react'; import { Box } from '@mui/material'; -import { SuggestedFeatureToggleChange } from '../SuggestedChangeOverview/SuggestedFeatureToggleChange/SuggestedFeatureToggleChange'; +import { ChangeRequestFeatureToggleChange } from '../ChangeRequestOverview/ChangeRequestFeatureToggleChange/ChangeRequestFeatureToggleChange'; import { objectId } from 'utils/objectId'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { ToggleStatusChange } from '../SuggestedChangeOverview/SuggestedFeatureToggleChange/ToggleStatusChange'; -// import { -// StrategyAddedChange, -// StrategyDeletedChange, -// StrategyEditedChange, -// } from '../SuggestedChangeOverview/SuggestedFeatureToggleChange/StrategyChange'; -// import { -// formatStrategyName, -// GetFeatureStrategyIcon, -// } from 'utils/strategyNames'; -import type { ISuggestChangesResponse } from 'hooks/api/getters/useSuggestedChangesDraft/useSuggestedChangesDraft'; -import { useSuggestChangeApi } from 'hooks/api/actions/useSuggestChangeApi/useSuggestChangeApi'; +import { ToggleStatusChange } from '../ChangeRequestOverview/ChangeRequestFeatureToggleChange/ToggleStatusChange'; +import type { IChangeRequestResponse } from 'hooks/api/getters/useChangeRequestDraft/useChangeRequestDraft'; +import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi'; import { formatUnknownError } from 'utils/formatUnknownError'; import useToast from 'hooks/useToast'; -interface ISuggestedChangeset { - suggestedChange: ISuggestChangesResponse; +interface IChangeRequest { + changeRequest: IChangeRequestResponse; onRefetch?: () => void; onNavigate?: () => void; } -export const SuggestedChangeset: VFC = ({ - suggestedChange, +export const ChangeRequest: VFC = ({ + changeRequest, onRefetch, onNavigate, }) => { - const { discardSuggestions } = useSuggestChangeApi(); + const { discardChangeRequestEvent } = useChangeRequestApi(); const { setToastData, setToastApiError } = useToast(); const onDiscard = (id: number) => async () => { try { - await discardSuggestions( - suggestedChange.project, - suggestedChange.id, + await discardChangeRequestEvent( + changeRequest.project, + changeRequest.id, id ); setToastData({ - title: 'Change discarded from suggestion draft.', + title: 'Change discarded from change request draft.', type: 'success', }); onRefetch?.(); @@ -51,11 +42,11 @@ export const SuggestedChangeset: VFC = ({ return ( Changes - {suggestedChange.features?.map(featureToggleChange => ( - ( + {featureToggleChange.changes.map(change => ( @@ -92,7 +83,7 @@ export const SuggestedChangeset: VFC = ({ /> */} ))} - + ))} ); diff --git a/frontend/src/component/suggestChanges/SuggestChangeConfirmDialog/SuggestChangeConfirmDialog.tsx b/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog.tsx similarity index 81% rename from frontend/src/component/suggestChanges/SuggestChangeConfirmDialog/SuggestChangeConfirmDialog.tsx rename to frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog.tsx index 1eb5ff0476..94486da395 100644 --- a/frontend/src/component/suggestChanges/SuggestChangeConfirmDialog/SuggestChangeConfirmDialog.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog.tsx @@ -2,7 +2,7 @@ import { FC } from 'react'; import { Alert, Typography } from '@mui/material'; import { Dialogue } from 'component/common/Dialogue/Dialogue'; -interface ISuggestChangesDialogueProps { +interface IChangeRequestDialogueProps { isOpen: boolean; onConfirm: () => void; onClose: () => void; @@ -11,7 +11,7 @@ interface ISuggestChangesDialogueProps { enabled?: boolean; } -export const SuggestChangesDialogue: FC = ({ +export const ChangeRequestDialogue: FC = ({ isOpen, onConfirm, onClose, @@ -25,15 +25,15 @@ export const SuggestChangesDialogue: FC = ({ secondaryButtonText="Cancel" onClick={onConfirm} onClose={onClose} - title="Suggest changes" + title="Request changes" > - Suggest changes is enabled for {environment}. Your changes needs to + Change requests is enabled for {environment}. Your changes needs to be approved before they will be live. All the changes you do now will be added into a draft that you can submit for review. - Suggested changes: + Change requests: {enabled ? 'Disable' : 'Enable'} feature toggle{' '} diff --git a/frontend/src/component/suggestChanges/SuggestedChangeOverview/SuggestedFeatureToggleChange/SuggestedFeatureToggleChange.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestFeatureToggleChange/ChangeRequestFeatureToggleChange.tsx similarity index 92% rename from frontend/src/component/suggestChanges/SuggestedChangeOverview/SuggestedFeatureToggleChange/SuggestedFeatureToggleChange.tsx rename to frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestFeatureToggleChange/ChangeRequestFeatureToggleChange.tsx index 6e1dd3c80e..725132dd74 100644 --- a/frontend/src/component/suggestChanges/SuggestedChangeOverview/SuggestedFeatureToggleChange/SuggestedFeatureToggleChange.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestFeatureToggleChange/ChangeRequestFeatureToggleChange.tsx @@ -3,14 +3,14 @@ import { Link } from 'react-router-dom'; import { Box, Card, Typography } from '@mui/material'; import ToggleOnIcon from '@mui/icons-material/ToggleOn'; -interface ISuggestedFeatureToggleChange { +interface IChangeRequestToggleChange { featureName: string; projectId: string; onNavigate?: () => void; } -export const SuggestedFeatureToggleChange: FC< - ISuggestedFeatureToggleChange +export const ChangeRequestFeatureToggleChange: FC< + IChangeRequestToggleChange > = ({ featureName, projectId, onNavigate, children }) => { return ( = ({ - suggestedChange, +export const ChangeRequestHeader: FC<{ changeRequest: any }> = ({ + changeRequest, }) => { return ( = ({ }} variant="h1" > - Suggestion + Change request - #{suggestedChange.id} + #{changeRequest.id} = ({ - Created{' '} - by + Created {' '} + by - + ({ @@ -56,11 +56,11 @@ export const SuggestedChangeHeader: FC<{ suggestedChange: any }> = ({ > Environment:{' '} - {suggestedChange?.environment} + {changeRequest?.environment} {' '} | Updates:{' '} - {suggestedChange?.features.length} feature toggles + {changeRequest?.features.length} feature toggles diff --git a/frontend/src/component/suggestChanges/SuggestedChangeOverview/SuggestedChangeOverview.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx similarity index 63% rename from frontend/src/component/suggestChanges/SuggestedChangeOverview/SuggestedChangeOverview.tsx rename to frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx index 8797175079..74014a2cfe 100644 --- a/frontend/src/component/suggestChanges/SuggestedChangeOverview/SuggestedChangeOverview.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx @@ -1,24 +1,24 @@ import { FC } from 'react'; import { Box, Button, Paper } from '@mui/material'; -import { useSuggestedChange } from 'hooks/api/getters/useSuggestChange/useSuggestedChange'; -import { SuggestedChangeHeader } from './SuggestedChangeHeader/SuggestedChangeHeader'; -import { SuggestedChangeTimeline } from './SuggestedChangeTimeline/SuggestedChangeTimeline'; -import { SuggestedChangeReviewers } from './SuggestedChangeReviewers/SuggestedChangeReviewers'; -import { SuggestedChangeset } from '../SuggestedChangeset/SuggestedChangeset'; +import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest'; +import { ChangeRequestHeader } from './ChangeRequestHeader/ChangeRequestHeader'; +import { ChangeRequestTimeline } from './ChangeRequestTimeline/ChangeRequestTimeline'; +import { ChangeRequestReviewers } from './ChangeRequestReviewers/ChangeRequestReviewers'; +import { ChangeRequest } from '../ChangeRequest/ChangeRequest'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; -import { useSuggestChangeApi } from 'hooks/api/actions/useSuggestChangeApi/useSuggestChangeApi'; +import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi'; import useToast from 'hooks/useToast'; import { formatUnknownError } from 'utils/formatUnknownError'; -import { SuggestedChangeReviewStatus } from './SuggestedChangeReviewStatus/SuggestedChangeReviewStatus'; +import { ChangeRequestReviewStatus } from './ChangeRequestReviewStatus/ChangeRequestReviewStatus'; -export const SuggestedChangeOverview: FC = () => { +export const ChangeRequestOverview: FC = () => { const projectId = useRequiredPathParam('projectId'); const id = useRequiredPathParam('id'); - const { data: suggestedChange } = useSuggestedChange(projectId, id); - const { applyChanges } = useSuggestChangeApi(); + const { data: changeRequest } = useChangeRequest(projectId, id); + const { applyChanges } = useChangeRequestApi(); const { setToastData, setToastApiError } = useToast(); - if (!suggestedChange) { + if (!changeRequest) { return null; } @@ -37,7 +37,7 @@ export const SuggestedChangeOverview: FC = () => { return ( <> - + { flexDirection: 'column', }} > - - + + { padding: theme.spacing(2), })} > - - + + - { diff --git a/frontend/src/component/suggest-changes/ProjectSuggestions/SuggestionsTabs/AvatarCell/AvatarCell.tsx b/frontend/src/component/changeRequest/ProjectChangeRequests/ChangeRequestsTabs/AvatarCell/AvatarCell.tsx similarity index 100% rename from frontend/src/component/suggest-changes/ProjectSuggestions/SuggestionsTabs/AvatarCell/AvatarCell.tsx rename to frontend/src/component/changeRequest/ProjectChangeRequests/ChangeRequestsTabs/AvatarCell/AvatarCell.tsx diff --git a/frontend/src/component/suggest-changes/ProjectSuggestions/SuggestionsTabs/ChangesetActionCell/ChangesetActionCell.tsx b/frontend/src/component/changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestActionCell/ChangeRequestActionCell.tsx similarity index 88% rename from frontend/src/component/suggest-changes/ProjectSuggestions/SuggestionsTabs/ChangesetActionCell/ChangesetActionCell.tsx rename to frontend/src/component/changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestActionCell/ChangeRequestActionCell.tsx index 3cb85e9945..cfc0fa04e2 100644 --- a/frontend/src/component/suggest-changes/ProjectSuggestions/SuggestionsTabs/ChangesetActionCell/ChangesetActionCell.tsx +++ b/frontend/src/component/changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestActionCell/ChangeRequestActionCell.tsx @@ -2,7 +2,7 @@ import { ArrowRight } from '@mui/icons-material'; import { useTheme } from '@mui/system'; import { TextCell } from '../../../../common/Table/cells/TextCell/TextCell'; -export const ChangesetActionCell = () => { +export const ChangeRequestActionCell = () => { const theme = useTheme(); return ( diff --git a/frontend/src/component/suggest-changes/ProjectSuggestions/SuggestionsTabs/ChangesetStatusCell/ChangesetStatusCell.tsx b/frontend/src/component/changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestStatusCell/ChangeRequestStatusCell.tsx similarity index 88% rename from frontend/src/component/suggest-changes/ProjectSuggestions/SuggestionsTabs/ChangesetStatusCell/ChangesetStatusCell.tsx rename to frontend/src/component/changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestStatusCell/ChangeRequestStatusCell.tsx index 697c65bb2d..8f5cbf8625 100644 --- a/frontend/src/component/suggest-changes/ProjectSuggestions/SuggestionsTabs/ChangesetStatusCell/ChangesetStatusCell.tsx +++ b/frontend/src/component/changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestStatusCell/ChangeRequestStatusCell.tsx @@ -4,11 +4,11 @@ import { colors } from 'themes/colors'; import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; import { Check, CircleOutlined, Close } from '@mui/icons-material'; -interface IChangesetStatusCellProps { +interface IChangeRequestStatusCellProps { value?: string | null; } -export enum SuggestChangesetState { +export enum ChangeRequestState { DRAFT = 'Draft', APPROVED = 'Approved', IN_REVIEW = 'In review', @@ -62,40 +62,40 @@ export const StyledReviewChip = styled(StyledChip)(({ theme }) => ({ }, })); -export const ChangesetStatusCell: VFC = ({ +export const ChangeRequestStatusCell: VFC = ({ value, }) => { const renderState = (state: string) => { switch (state) { - case SuggestChangesetState.IN_REVIEW: + case ChangeRequestState.IN_REVIEW: return ( } /> ); - case SuggestChangesetState.APPROVED: + case ChangeRequestState.APPROVED: return ( } /> ); - case SuggestChangesetState.APPLIED: + case ChangeRequestState.APPLIED: return ( } /> ); - case SuggestChangesetState.CANCELLED: + case ChangeRequestState.CANCELLED: return ( } /> ); - case SuggestChangesetState.REJECTED: + case ChangeRequestState.REJECTED: return ( ({ margin: 0, })); -export const ChangesetTitleCell = ({ +export const ChangeRequestTitleCell = ({ value, row: { original }, -}: IChangesetTitleCellProps) => { +}: IChangeRequestTitleCellProps) => { const projectId = useRequiredPathParam('projectId'); const { id, features: changes } = original; const theme = useTheme(); - const path = `/projects/${projectId}/suggest-changes/${id}`; + const path = `/projects/${projectId}/change-requests/${id}`; if (!value) { return ; @@ -37,7 +37,7 @@ export const ChangesetTitleCell = ({ to={path} sx={{ pt: 0.2 }} > - Suggestion + Change request ; setStoredParams: ( @@ -38,13 +38,13 @@ export interface IChangeSetTableProps { projectId: string; } -export const SuggestionsTabs = ({ - changesets = [], +export const ChangeRequestsTabs = ({ + changeRequests = [], loading, storedParams, setStoredParams, projectId, -}: IChangeSetTableProps) => { +}: IChangeRequestTableProps) => { const { classes } = useStyles(); const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); const [searchParams, setSearchParams] = useSearchParams(); @@ -53,27 +53,29 @@ export const SuggestionsTabs = ({ searchParams.get('search') || '' ); - const [openChangesets, closedChangesets] = useMemo(() => { - const open = changesets.filter( - changeset => - changeset.state !== 'Cancelled' && changeset.state !== 'Applied' + const [openChangeRequests, closedChangeRequests] = useMemo(() => { + const open = changeRequests.filter( + changeRequest => + changeRequest.state !== 'Cancelled' && + changeRequest.state !== 'Applied' ); - const closed = changesets.filter( - changeset => - changeset.state === 'Cancelled' || changeset.state === 'Applied' + const closed = changeRequests.filter( + changeRequest => + changeRequest.state === 'Cancelled' || + changeRequest.state === 'Applied' ); return [open, closed]; - }, [changesets]); + }, [changeRequests]); const tabs = [ { - title: 'Suggestions', - data: openChangesets, + title: 'Change requests', + data: openChangeRequests, }, { title: 'Closed', - data: closedChangesets, + data: closedChangeRequests, }, ]; @@ -87,7 +89,7 @@ export const SuggestionsTabs = ({ width: 100, canSort: true, accessor: 'id', - Cell: ChangesetTitleCell, + Cell: ChangeRequestTitleCell, }, { Header: 'By', @@ -117,7 +119,7 @@ export const SuggestionsTabs = ({ accessor: 'state', minWidth: 150, width: 150, - Cell: ChangesetStatusCell, + Cell: ChangeRequestStatusCell, sortType: 'text', }, { @@ -126,7 +128,7 @@ export const SuggestionsTabs = ({ minWidth: 50, width: 50, canSort: false, - Cell: ChangesetActionCell, + Cell: ChangeRequestActionCell, }, ], //eslint-disable-next-line diff --git a/frontend/src/component/suggest-changes/ProjectSuggestions/ProjectSuggestedChanges.tsx b/frontend/src/component/changeRequest/ProjectChangeRequests/ProjectChangeRequests.tsx similarity index 65% rename from frontend/src/component/suggest-changes/ProjectSuggestions/ProjectSuggestedChanges.tsx rename to frontend/src/component/changeRequest/ProjectChangeRequests/ProjectChangeRequests.tsx index 26506fa705..dd9b699e9a 100644 --- a/frontend/src/component/suggest-changes/ProjectSuggestions/ProjectSuggestedChanges.tsx +++ b/frontend/src/component/changeRequest/ProjectChangeRequests/ProjectChangeRequests.tsx @@ -2,28 +2,28 @@ import { usePageTitle } from 'hooks/usePageTitle'; import { createLocalStorage } from 'utils/createLocalStorage'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { useProjectNameOrId } from 'hooks/api/getters/useProject/useProject'; -import { SuggestionsTabs } from './SuggestionsTabs/SuggestionsTabs'; +import { ChangeRequestsTabs } from './ChangeRequestsTabs/ChangeRequestsTabs'; import { SortingRule } from 'react-table'; -import { useProjectSuggestedChanges } from 'hooks/api/getters/useProjectSuggestedChanges/useProjectSuggestedChanges'; +import { useProjectChangeRequests } from 'hooks/api/getters/useProjectChangeRequests/useProjectChangeRequests'; const defaultSort: SortingRule = { id: 'updatedAt', desc: true }; -export const ProjectSuggestedChanges = () => { +export const ProjectChangeRequests = () => { const projectId = useRequiredPathParam('projectId'); const projectName = useProjectNameOrId(projectId); usePageTitle(`Change requests – ${projectName}`); - const { changesets, loading } = useProjectSuggestedChanges(projectId); + const { changeRequests, loading } = useProjectChangeRequests(projectId); const { value, setValue } = createLocalStorage( - `${projectId}:ProjectSuggestedChanges`, + `${projectId}:ProjectChangeRequest`, defaultSort ); return ( - { try { @@ -84,9 +84,9 @@ const FeatureOverviewEnvSwitch = ({ }; const toggleEnvironment = async (e: React.ChangeEvent) => { - if (uiConfig?.flags?.suggestChanges && env.name === 'production') { + if (uiConfig?.flags?.changeRequests && env.name === 'production') { e.preventDefault(); - onSuggestToggle(featureId, env.name, env.enabled); + onChangeRequestToggle(featureId, env.name, env.enabled); return; } if (env.enabled) { @@ -119,12 +119,12 @@ const FeatureOverviewEnvSwitch = ({ /> {content} - ); diff --git a/frontend/src/component/feature/FeatureView/FeatureView.tsx b/frontend/src/component/feature/FeatureView/FeatureView.tsx index dbc1a5f8e6..10332d4b02 100644 --- a/frontend/src/component/feature/FeatureView/FeatureView.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureView.tsx @@ -30,7 +30,7 @@ import StatusChip from 'component/common/StatusChip/StatusChip'; import { FeatureNotFound } from 'component/feature/FeatureView/FeatureNotFound/FeatureNotFound'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { FeatureArchiveDialog } from '../../common/FeatureArchiveDialog/FeatureArchiveDialog'; -import { DraftBanner } from 'component/suggestChanges/DraftBanner/DraftBanner'; +import { DraftBanner } from 'component/changeRequest/DraftBanner/DraftBanner'; import { MainLayout } from 'component/layout/MainLayout/MainLayout'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; @@ -88,7 +88,7 @@ export const FeatureView = () => { ) : null } diff --git a/frontend/src/component/project/Project/Project.tsx b/frontend/src/component/project/Project/Project.tsx index 92eab90296..4878a26fc0 100644 --- a/frontend/src/component/project/Project/Project.tsx +++ b/frontend/src/component/project/Project/Project.tsx @@ -24,10 +24,10 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import { Routes, Route, useLocation } from 'react-router-dom'; import { DeleteProjectDialogue } from './DeleteProject/DeleteProjectDialogue'; import { ProjectLog } from './ProjectLog/ProjectLog'; -import { SuggestedChangeOverview } from 'component/suggestChanges/SuggestedChangeOverview/SuggestedChangeOverview'; -import { DraftBanner } from 'component/suggestChanges/DraftBanner/DraftBanner'; +import { ChangeRequestOverview } from 'component/changeRequest/ChangeRequestOverview/ChangeRequestOverview'; +import { DraftBanner } from 'component/changeRequest/DraftBanner/DraftBanner'; import { MainLayout } from 'component/layout/MainLayout/MainLayout'; -import { ProjectSuggestedChanges } from '../../suggest-changes/ProjectSuggestions/ProjectSuggestedChanges'; +import { ProjectChangeRequests } from '../../changeRequest/ProjectChangeRequests/ProjectChangeRequests'; const StyledDiv = styled('div')(() => ({ display: 'flex', @@ -91,8 +91,8 @@ const Project = () => { }, { title: 'Change requests', - path: `${basePath}/suggest-changes`, - name: 'suggest-changes' + '', + path: `${basePath}/change-requests`, + name: 'change-request' + '', }, { title: 'Event log', @@ -124,7 +124,7 @@ const Project = () => { ) : null } @@ -235,20 +235,20 @@ const Project = () => { } /> } /> } + condition={Boolean(uiConfig?.flags?.changeRequests)} + show={} /> } /> } + condition={Boolean(uiConfig?.flags?.changeRequests)} + show={} /> } /> diff --git a/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeatureToggles.tsx b/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeatureToggles.tsx index 3fc6f8db92..ae6676abd0 100644 --- a/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeatureToggles.tsx +++ b/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeatureToggles.tsx @@ -36,8 +36,8 @@ import { FeatureArchiveDialog } from 'component/common/FeatureArchiveDialog/Feat import { useSearch } from 'hooks/useSearch'; import { useMediaQuery } from '@mui/material'; import { Search } from 'component/common/Search/Search'; -import { useSuggestToggle } from 'hooks/useSuggestToggle'; -import { SuggestChangesDialogue } from 'component/suggestChanges/SuggestChangeConfirmDialog/SuggestChangeConfirmDialog'; +import { useChangeRequestToggle } from 'hooks/useChangeRequestToggle'; +import { ChangeRequestDialogue } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog'; interface IProjectFeatureTogglesProps { features: IProject['features']; @@ -102,11 +102,11 @@ export const ProjectFeatureToggles = ({ const { toggleFeatureEnvironmentOn, toggleFeatureEnvironmentOff } = useFeatureApi(); const { - onSuggestToggle, - onSuggestToggleClose, - onSuggestToggleConfirm, - suggestChangesDialogDetails, - } = useSuggestToggle(projectId); + onChangeRequestToggle, + onChangeRequestToggleClose, + onChangeRequestToggleConfirm, + changeRequestDialogDetails, + } = useChangeRequestToggle(projectId); const onToggle = useCallback( async ( @@ -116,10 +116,10 @@ export const ProjectFeatureToggles = ({ enabled: boolean ) => { if ( - uiConfig?.flags?.suggestChanges && + uiConfig?.flags?.changeRequests && environment === 'production' ) { - onSuggestToggle(featureName, environment, enabled); + onChangeRequestToggle(featureName, environment, enabled); throw new Error('Additional approval required'); } try { @@ -517,12 +517,12 @@ export const ProjectFeatureToggles = ({ featureId={featureArchiveState || ''} projectId={projectId} />{' '} - ); diff --git a/frontend/src/hooks/api/actions/useSuggestChangeApi/useSuggestChangeApi.ts b/frontend/src/hooks/api/actions/useChangeRequestApi/useChangeRequestApi.ts similarity index 69% rename from frontend/src/hooks/api/actions/useSuggestChangeApi/useSuggestChangeApi.ts rename to frontend/src/hooks/api/actions/useChangeRequestApi/useChangeRequestApi.ts index 1267802c79..af262cc935 100644 --- a/frontend/src/hooks/api/actions/useSuggestChangeApi/useSuggestChangeApi.ts +++ b/frontend/src/hooks/api/actions/useChangeRequestApi/useChangeRequestApi.ts @@ -1,6 +1,6 @@ import useAPI from '../useApi/useApi'; -interface ISuggestChangeSchema { +interface IChangeRequestsSchema { feature: string; action: | 'updateEnabled' @@ -10,17 +10,17 @@ interface ISuggestChangeSchema { payload: string | boolean | object | number; } -export const useSuggestChangeApi = () => { +export const useChangeRequestApi = () => { const { makeRequest, createRequest, errors, loading } = useAPI({ propagateErrors: true, }); - const addSuggestion = async ( + const addChangeRequest = async ( project: string, environment: string, - payload: ISuggestChangeSchema + payload: IChangeRequestsSchema ) => { - const path = `api/admin/projects/${project}/environments/${environment}/suggest-changes`; + const path = `api/admin/projects/${project}/environments/${environment}/change-requests`; const req = createRequest(path, { method: 'POST', body: JSON.stringify(payload), @@ -35,10 +35,10 @@ export const useSuggestChangeApi = () => { const changeState = async ( project: string, - suggestChangeId: number, + changeRequestId: number, payload: any ) => { - const path = `api/admin/projects/${project}/suggest-changes/${suggestChangeId}/state`; + const path = `api/admin/projects/${project}/change-requests/${changeRequestId}/state`; const req = createRequest(path, { method: 'PUT', body: JSON.stringify(payload), @@ -51,8 +51,8 @@ export const useSuggestChangeApi = () => { } }; - const applyChanges = async (project: string, suggestChangeId: string) => { - const path = `api/admin/projects/${project}/suggest-changes/${suggestChangeId}/apply`; + const applyChanges = async (project: string, changeRequestId: string) => { + const path = `api/admin/projects/${project}/change-requests/${changeRequestId}/apply`; const req = createRequest(path, { method: 'PUT', }); @@ -64,12 +64,12 @@ export const useSuggestChangeApi = () => { } }; - const discardSuggestions = async ( + const discardChangeRequestEvent = async ( project: string, - changesetId: number, - changeId: number + changeRequestId: number, + changeRequestEventId: number ) => { - const path = `api/admin/projects/${project}/suggest-changes/${changesetId}/changes/${changeId}`; + const path = `api/admin/projects/${project}/change-requests/${changeRequestId}/changes/${changeRequestEventId}`; const req = createRequest(path, { method: 'DELETE', }); @@ -81,10 +81,10 @@ export const useSuggestChangeApi = () => { }; return { - addSuggestion, + addChangeRequest, applyChanges, changeState, - discardSuggestions, + discardChangeRequestEvent, errors, loading, }; diff --git a/frontend/src/hooks/api/getters/useSuggestChange/useSuggestedChange.ts b/frontend/src/hooks/api/getters/useChangeRequest/useChangeRequest.ts similarity index 69% rename from frontend/src/hooks/api/getters/useSuggestChange/useSuggestedChange.ts rename to frontend/src/hooks/api/getters/useChangeRequest/useChangeRequest.ts index 94983bbb08..e439e58e83 100644 --- a/frontend/src/hooks/api/getters/useSuggestChange/useSuggestedChange.ts +++ b/frontend/src/hooks/api/getters/useChangeRequest/useChangeRequest.ts @@ -2,16 +2,16 @@ import useSWR from 'swr'; import { formatApiPath } from 'utils/formatPath'; import handleErrorResponses from '../httpErrorResponseHandler'; -export const useSuggestedChange = (projectId: string, id: string) => { +export const useChangeRequest = (projectId: string, id: string) => { const { data, error, mutate } = useSWR( - formatApiPath(`api/admin/projects/${projectId}/suggest-changes/${id}`), + formatApiPath(`api/admin/projects/${projectId}/change-requests/${id}`), fetcher ); return { data, loading: !error && !data, - refetchSuggestedChange: () => mutate(), + refetchChangeRequest: () => mutate(), error, }; }; diff --git a/frontend/src/hooks/api/getters/useSuggestedChangesDraft/useSuggestedChangesDraft.ts b/frontend/src/hooks/api/getters/useChangeRequestDraft/useChangeRequestDraft.ts similarity index 76% rename from frontend/src/hooks/api/getters/useSuggestedChangesDraft/useSuggestedChangesDraft.ts rename to frontend/src/hooks/api/getters/useChangeRequestDraft/useChangeRequestDraft.ts index 46760f94ad..42bac75ebb 100644 --- a/frontend/src/hooks/api/getters/useSuggestedChangesDraft/useSuggestedChangesDraft.ts +++ b/frontend/src/hooks/api/getters/useChangeRequestDraft/useChangeRequestDraft.ts @@ -17,7 +17,7 @@ interface IChange { }; } -export interface ISuggestChangesResponse { +export interface IChangeRequestResponse { id: number; environment: string; state: string; @@ -36,13 +36,13 @@ export interface ISuggestChangesResponse { const fetcher = (path: string) => { return fetch(path) - .then(handleErrorResponses('SuggestedChanges')) + .then(handleErrorResponses('ChangeRequest')) .then(res => res.json()); }; -export const useSuggestedChangesDraft = (project: string) => { - const { data, error, mutate } = useSWR( - formatApiPath(`api/admin/projects/${project}/suggest-changes/draft`), +export const useChangeRequestDraft = (project: string) => { + const { data, error, mutate } = useSWR( + formatApiPath(`api/admin/projects/${project}/change-requests/draft`), fetcher ); diff --git a/frontend/src/hooks/api/getters/useProjectSuggestedChanges/useProjectSuggestedChanges.ts b/frontend/src/hooks/api/getters/useProjectChangeRequests/useProjectChangeRequests.ts similarity index 70% rename from frontend/src/hooks/api/getters/useProjectSuggestedChanges/useProjectSuggestedChanges.ts rename to frontend/src/hooks/api/getters/useProjectChangeRequests/useProjectChangeRequests.ts index f58e12aea7..b9fa709527 100644 --- a/frontend/src/hooks/api/getters/useProjectSuggestedChanges/useProjectSuggestedChanges.ts +++ b/frontend/src/hooks/api/getters/useProjectChangeRequests/useProjectChangeRequests.ts @@ -5,19 +5,19 @@ import handleErrorResponses from '../httpErrorResponseHandler'; const fetcher = (path: string) => { return fetch(path) - .then(handleErrorResponses('SuggestedChanges')) + .then(handleErrorResponses('ChangeRequest')) .then(res => res.json()); }; -export const useProjectSuggestedChanges = (project: string) => { +export const useProjectChangeRequests = (project: string) => { const { data, error, mutate } = useSWR( - formatApiPath(`api/admin/projects/${project}/suggest-changes`), + formatApiPath(`api/admin/projects/${project}/change-requests`), fetcher ); return useMemo( () => ({ - changesets: data, + changeRequests: data, loading: !error && !data, refetch: () => mutate(), error, diff --git a/frontend/src/hooks/useChangeRequestToggle.ts b/frontend/src/hooks/useChangeRequestToggle.ts new file mode 100644 index 0000000000..d949b083c5 --- /dev/null +++ b/frontend/src/hooks/useChangeRequestToggle.ts @@ -0,0 +1,67 @@ +import { useCallback, useState } from 'react'; +import useToast from 'hooks/useToast'; +import { formatUnknownError } from 'utils/formatUnknownError'; +import { useChangeRequestApi } from './api/actions/useChangeRequestApi/useChangeRequestApi'; +import { useChangeRequestDraft } from './api/getters/useChangeRequestDraft/useChangeRequestDraft'; + +export const useChangeRequestToggle = (project: string) => { + const { setToastData, setToastApiError } = useToast(); + const { addChangeRequest } = useChangeRequestApi(); + const { refetch: refetchChangeRequests } = useChangeRequestDraft(project); + + const [changeRequestDialogDetails, setChangeRequestDialogDetails] = + useState<{ + enabled?: boolean; + featureName?: string; + environment?: string; + isOpen: boolean; + }>({ isOpen: false }); + + const onChangeRequestToggle = useCallback( + (featureName: string, environment: string, enabled: boolean) => { + setChangeRequestDialogDetails({ + featureName, + environment, + enabled, + isOpen: true, + }); + }, + [] + ); + + const onChangeRequestToggleClose = useCallback(() => { + setChangeRequestDialogDetails({ isOpen: false }); + }, []); + + const onChangeRequestToggleConfirm = useCallback(async () => { + try { + await addChangeRequest( + project, + changeRequestDialogDetails.environment!, + { + feature: changeRequestDialogDetails.featureName!, + action: 'updateEnabled', + payload: { + enabled: Boolean(changeRequestDialogDetails.enabled), + }, + } + ); + refetchChangeRequests(); + setChangeRequestDialogDetails({ isOpen: false }); + setToastData({ + type: 'success', + title: 'Changes added to the draft!', + }); + } catch (error) { + setToastApiError(formatUnknownError(error)); + setChangeRequestDialogDetails({ isOpen: false }); + } + }, [addChangeRequest]); + + return { + onChangeRequestToggle, + onChangeRequestToggleClose, + onChangeRequestToggleConfirm, + changeRequestDialogDetails, + }; +}; diff --git a/frontend/src/hooks/useSuggestToggle.ts b/frontend/src/hooks/useSuggestToggle.ts deleted file mode 100644 index e6e776899e..0000000000 --- a/frontend/src/hooks/useSuggestToggle.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { useCallback, useState } from 'react'; -import useToast from 'hooks/useToast'; -import { formatUnknownError } from 'utils/formatUnknownError'; -import { useSuggestChangeApi } from './api/actions/useSuggestChangeApi/useSuggestChangeApi'; -import { useSuggestedChangesDraft } from './api/getters/useSuggestedChangesDraft/useSuggestedChangesDraft'; - -export const useSuggestToggle = (project: string) => { - const { setToastData, setToastApiError } = useToast(); - const { addSuggestion } = useSuggestChangeApi(); - const { refetch: refetchSuggestedChange } = - useSuggestedChangesDraft(project); - - const [suggestChangesDialogDetails, setSuggestChangesDialogDetails] = - useState<{ - enabled?: boolean; - featureName?: string; - environment?: string; - isOpen: boolean; - }>({ isOpen: false }); - - const onSuggestToggle = useCallback( - (featureName: string, environment: string, enabled: boolean) => { - setSuggestChangesDialogDetails({ - featureName, - environment, - enabled, - isOpen: true, - }); - }, - [] - ); - - const onSuggestToggleClose = useCallback(() => { - setSuggestChangesDialogDetails({ isOpen: false }); - }, []); - - const onSuggestToggleConfirm = useCallback(async () => { - try { - await addSuggestion( - project, - suggestChangesDialogDetails.environment!, - { - feature: suggestChangesDialogDetails.featureName!, - action: 'updateEnabled', - payload: { - enabled: Boolean(suggestChangesDialogDetails.enabled), - }, - } - ); - refetchSuggestedChange(); - setSuggestChangesDialogDetails({ isOpen: false }); - setToastData({ - type: 'success', - title: 'Changes added to the draft!', - }); - } catch (error) { - setToastApiError(formatUnknownError(error)); - setSuggestChangesDialogDetails({ isOpen: false }); - } - }, [addSuggestion]); - - return { - onSuggestToggle, - onSuggestToggleClose, - onSuggestToggleConfirm, - suggestChangesDialogDetails, - }; -}; diff --git a/frontend/src/interfaces/suggestChangeset.ts b/frontend/src/interfaces/suggestChangeset.ts index 68cf611155..f8b2a8d0a5 100644 --- a/frontend/src/interfaces/suggestChangeset.ts +++ b/frontend/src/interfaces/suggestChangeset.ts @@ -1,4 +1,4 @@ -export interface ISuggestChangeset { +export interface IChangeRequest { id: number; state: | 'CREATED' @@ -11,11 +11,10 @@ export interface ISuggestChangeset { environment: string; createdBy?: string; createdAt?: Date; - changes?: ISuggestChange[]; - events?: ISuggestChangeEvent[]; + changes?: IChangeRequestEvent[]; } -export interface ISuggestChange { +export interface IChangeRequestEvent { id: number; action: | 'updateEnabled' @@ -28,7 +27,7 @@ export interface ISuggestChange { createdAt?: Date; } -export enum SuggestChangesetEvent { +export enum ChangeRequestEvent { CREATED = 'CREATED', UPDATED = 'UPDATED', SUBMITTED = 'SUBMITTED', @@ -36,23 +35,3 @@ export enum SuggestChangesetEvent { REJECTED = 'REJECTED', CLOSED = 'CLOSED', } - -export enum SuggestChangeEvent { - UPDATE_ENABLED = 'updateFeatureEnabledEvent', - ADD_STRATEGY = 'addStrategyEvent', - UPDATE_STRATEGY = 'updateStrategyEvent', - DELETE_STRATEGY = 'deleteStrategyEvent', -} - -export interface ISuggestChangeEvent { - id: number; - event: SuggestChangesetEvent; - data: ISuggestChangeEventData; - createdBy?: string; - createdAt?: Date; -} - -export interface ISuggestChangeEventData { - feature: string; - data: unknown; -} diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index ddf36de905..5ab872fbdf 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -42,7 +42,7 @@ export interface IFlags { embedProxyFrontend?: boolean; publicSignup?: boolean; syncSSOGroups?: boolean; - suggestChanges?: boolean; + changeRequests?: boolean; cloneEnvironment?: boolean; } diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index 840700b78c..7d16e8f53e 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -69,12 +69,12 @@ exports[`should create default config 1`] = ` "ENABLE_DARK_MODE_SUPPORT": false, "anonymiseEventLog": false, "batchMetrics": false, + "changeRequests": false, "cloneEnvironment": false, "embedProxy": false, "embedProxyFrontend": false, "publicSignup": false, "responseTimeWithAppName": false, - "suggestChanges": false, "syncSSOGroups": false, }, }, @@ -83,12 +83,12 @@ exports[`should create default config 1`] = ` "ENABLE_DARK_MODE_SUPPORT": false, "anonymiseEventLog": false, "batchMetrics": false, + "changeRequests": false, "cloneEnvironment": false, "embedProxy": false, "embedProxyFrontend": false, "publicSignup": false, "responseTimeWithAppName": false, - "suggestChanges": false, "syncSSOGroups": false, }, "externalResolver": { diff --git a/src/lib/types/events.ts b/src/lib/types/events.ts index 426699d5bd..79ea3f0259 100644 --- a/src/lib/types/events.ts +++ b/src/lib/types/events.ts @@ -82,8 +82,6 @@ export const PUBLIC_SIGNUP_TOKEN_CREATED = 'public-signup-token-created'; export const PUBLIC_SIGNUP_TOKEN_USER_ADDED = 'public-signup-token-user-added'; export const PUBLIC_SIGNUP_TOKEN_TOKEN_UPDATED = 'public-signup-token-updated'; -export const SUGGEST_CHANGE_CREATED = 'suggest-change-created'; - export interface IBaseEvent { type: string; createdBy: string; diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index 5ee74b9bcb..a2c26b0ea7 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -10,8 +10,8 @@ export const defaultExperimentalOptions = { process.env.UNLEASH_EXPERIMENTAL_EMBED_PROXY, false, ), - suggestChanges: parseEnvVarBoolean( - process.env.UNLEASH_EXPERIMENTAL_SUGGEST_CHANGES, + changeRequests: parseEnvVarBoolean( + process.env.UNLEASH_EXPERIMENTAL_CHANGE_REQUESTS, false, ), syncSSOGroups: parseEnvVarBoolean( @@ -51,7 +51,7 @@ export interface IExperimentalOptions { batchMetrics?: boolean; anonymiseEventLog?: boolean; syncSSOGroups?: boolean; - suggestChanges?: boolean; + changeRequests?: boolean; cloneEnvironment?: boolean; }; externalResolver: IExternalFlagResolver; diff --git a/src/lib/types/model.ts b/src/lib/types/model.ts index 89a5c190fb..abe274446a 100644 --- a/src/lib/types/model.ts +++ b/src/lib/types/model.ts @@ -1,7 +1,7 @@ import { ITagType } from './stores/tag-type-store'; import { LogProvider } from '../logger'; import { IRole } from './stores/access-store'; -import User, { IUser } from './user'; +import { IUser } from './user'; import { ALL_OPERATORS } from '../util/constants'; export type Operator = typeof ALL_OPERATORS[number]; @@ -372,99 +372,3 @@ export interface IFeatureStrategySegment { featureStrategyId: string; segmentId: number; } - -export interface ISuggestChangeset { - id: number; - state: string; - project: string; - environment: string; - createdBy: Pick; - createdAt: Date; - features: ISuggestChangeFeature[]; -} - -export interface ISuggestChangeFeature { - name: string; - changes: ISuggestChange[]; -} - -export interface ISuggestChangeBase { - id?: number; - action: SuggestChangeAction; - payload: SuggestChangePayload; - createdBy?: Pick; - createdAt?: Date; -} - -export enum SuggestChangesetState { - DRAFT = 'Draft', - APPROVED = 'Approved', - IN_REVIEW = 'In review', - APPLIED = 'Applied', - CANCELLED = 'Cancelled', -} - -type SuggestChangePayload = - | SuggestChangeEnabled - | SuggestChangeAddStrategy - | SuggestChangeEditStrategy - | SuggestChangeDeleteStrategy; - -export interface ISuggestChangeAddStrategy extends ISuggestChangeBase { - action: 'addStrategy'; - payload: SuggestChangeAddStrategy; -} - -export interface ISuggestChangeDeleteStrategy extends ISuggestChangeBase { - action: 'deleteStrategy'; - payload: SuggestChangeDeleteStrategy; -} - -export interface ISuggestChangeUpdateStrategy extends ISuggestChangeBase { - action: 'updateStrategy'; - payload: SuggestChangeEditStrategy; -} - -export interface ISuggestChangeEnabled extends ISuggestChangeBase { - action: 'updateEnabled'; - payload: SuggestChangeEnabled; -} - -export type ISuggestChange = - | ISuggestChangeAddStrategy - | ISuggestChangeDeleteStrategy - | ISuggestChangeUpdateStrategy - | ISuggestChangeEnabled; - -type SuggestChangeEnabled = { enabled: boolean }; - -type SuggestChangeAddStrategy = Pick< - IFeatureStrategy, - 'parameters' | 'constraints' -> & { name: string }; - -type SuggestChangeEditStrategy = SuggestChangeAddStrategy & { id: string }; - -type SuggestChangeDeleteStrategy = { - deleteId: string; -}; - -export enum SuggestChangesetEvent { - CREATED = 'CREATED', - UPDATED = 'UPDATED', - SUBMITTED = 'SUBMITTED', - APPROVED = 'APPROVED', - REJECTED = 'REJECTED', - CLOSED = 'CLOSED', -} - -export type SuggestChangeAction = - | 'updateEnabled' - | 'addStrategy' - | 'updateStrategy' - | 'deleteStrategy'; - -export interface ISuggestChangeEventData { - feature: string; - data: unknown; -} diff --git a/src/lib/types/permissions.ts b/src/lib/types/permissions.ts index de02ab600b..24c471ac9c 100644 --- a/src/lib/types/permissions.ts +++ b/src/lib/types/permissions.ts @@ -37,4 +37,3 @@ export const MOVE_FEATURE_TOGGLE = 'MOVE_FEATURE_TOGGLE'; export const CREATE_SEGMENT = 'CREATE_SEGMENT'; export const UPDATE_SEGMENT = 'UPDATE_SEGMENT'; export const DELETE_SEGMENT = 'DELETE_SEGMENT'; -export const SUGGEST_CHANGE = 'SUGGEST_CHANGE'; diff --git a/src/migrations/20221901130645-add-change-requests-table.js b/src/migrations/20221901130645-add-change-requests-table.js new file mode 100644 index 0000000000..88e85ed0cd --- /dev/null +++ b/src/migrations/20221901130645-add-change-requests-table.js @@ -0,0 +1,40 @@ +'use strict'; + +exports.up = function (db, callback) { + db.runSql( + ` + DROP TABLE IF EXISTS suggest_change; + DROP TABLE IF EXISTS suggest_change_set; + CREATE TABLE IF NOT EXISTS change_requests ( + id serial primary key, + environment varchar(100) REFERENCES environments(name) ON DELETE CASCADE, + state varchar(255) NOT NULL, + project varchar(255) REFERENCES projects(id) ON DELETE CASCADE, + created_by integer not null references users (id) ON DELETE CASCADE, + created_at timestamp default now() + ); + + CREATE TABLE IF NOT EXISTS change_request_events ( + id serial primary key, + feature varchar(255) NOT NULL references features(name) on delete cascade, + action varchar(255) NOT NULL, + payload jsonb not null default '[]'::jsonb, + created_by integer not null references users (id) ON DELETE CASCADE, + created_at timestamp default now(), + change_request_id integer NOT NULL REFERENCES change_requests(id) ON DELETE CASCADE, + UNIQUE (feature, action, change_request_id) + ); + `, + callback, + ); +}; + +exports.down = function (db, callback) { + db.runSql( + ` + DROP TABLE IF EXISTS change_request_events; + DROP TABLE IF EXISTS change_requests; + `, + callback, + ); +}; diff --git a/src/server-dev.ts b/src/server-dev.ts index bc9a6a29c9..d94bd6b73b 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -39,7 +39,7 @@ process.nextTick(async () => { anonymiseEventLog: false, responseTimeWithAppName: true, syncSSOGroups: true, - suggestChanges: true, + changeRequests: true, cloneEnvironment: true, }, }, diff --git a/src/test/config/test-config.ts b/src/test/config/test-config.ts index 5d7ceb9345..79029869e3 100644 --- a/src/test/config/test-config.ts +++ b/src/test/config/test-config.ts @@ -28,7 +28,7 @@ export function createTestConfig(config?: IUnleashOptions): IUnleashConfig { embedProxyFrontend: true, batchMetrics: true, syncSSOGroups: true, - suggestChanges: true, + changeRequests: true, cloneEnvironment: true, }, }, diff --git a/src/test/e2e/api/admin/conditional-middleware.e2e.test.ts b/src/test/e2e/api/admin/conditional-middleware.e2e.test.ts index cb0f291fb5..4b4c141f43 100644 --- a/src/test/e2e/api/admin/conditional-middleware.e2e.test.ts +++ b/src/test/e2e/api/admin/conditional-middleware.e2e.test.ts @@ -11,7 +11,7 @@ test('disabled middleware should not block paths that use the same path', async conditionalMiddleware( () => false, (req, res) => { - res.send({ suggestChanges: 'hello' }); + res.send({ changeRequest: 'hello' }); }, ), ); @@ -30,11 +30,11 @@ test('should return 404 when path is not enabled', async () => { const path = '/api/admin/projects'; app.use( - `${path}/suggest-changes`, + `${path}/change-requests`, conditionalMiddleware( () => false, (req, res) => { - res.send({ suggestChanges: 'hello' }); + res.send({ changeRequest: 'hello' }); }, ), ); @@ -43,7 +43,7 @@ test('should return 404 when path is not enabled', async () => { res.json({ projects: [] }); }); - await supertest(app).get('/api/admin/projects/suggest-changes').expect(404); + await supertest(app).get('/api/admin/projects/change-requests').expect(404); }); test('should respect ordering of endpoints', async () => { @@ -55,7 +55,7 @@ test('should respect ordering of endpoints', async () => { conditionalMiddleware( () => true, (req, res) => { - res.json({ name: 'Suggest changes' }); + res.json({ name: 'Request changes' }); }, ), ); @@ -66,7 +66,7 @@ test('should respect ordering of endpoints', async () => { await supertest(app) .get('/api/admin/projects') - .expect(200, { name: 'Suggest changes' }); + .expect(200, { name: 'Request changes' }); }); test('disabled middleware should not block paths that use the same basepath', async () => { @@ -74,11 +74,11 @@ test('disabled middleware should not block paths that use the same basepath', as const path = '/api/admin/projects'; app.use( - `${path}/suggest-changes`, + `${path}/change-requests`, conditionalMiddleware( () => false, (req, res) => { - res.json({ name: 'Suggest changes' }); + res.json({ name: 'Request changes' }); }, ), ); diff --git a/website/docs/api/admin/events-api.md b/website/docs/api/admin/events-api.md index e890ba0d07..88e680ddf8 100644 --- a/website/docs/api/admin/events-api.md +++ b/website/docs/api/admin/events-api.md @@ -1562,7 +1562,7 @@ This event fires when you delete a segment. ### 'suggest-change-created' -This event fires when you create a a suggest-changes draft. +This event fires when you create a a change-request draft. ```json title="example event: suggest-change-created" {