From c57baeb35e2b78b4d600aa80d23c1c58edd17112 Mon Sep 17 00:00:00 2001
From: andreas-unleash <104830839+andreas-unleash@users.noreply.github.com>
Date: Fri, 11 Nov 2022 14:03:30 +0200
Subject: [PATCH] integrate change request settings be (#2403)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Integrates frontend with the change request settings backedn
## About the changes
Closes #
### Important files
## Discussion points
---
.../changeRequest/changeRequest.types.ts | 6 ++
.../ChangeRequestConfiguration.tsx | 101 ++++++++++++------
.../useChangeRequestApi.ts | 18 ++++
.../useChangeRequestConfig.ts | 24 +++++
yarn.lock | 24 ++---
5 files changed, 127 insertions(+), 46 deletions(-)
create mode 100644 frontend/src/hooks/api/getters/useChangeRequestConfig/useChangeRequestConfig.ts
diff --git a/frontend/src/component/changeRequest/changeRequest.types.ts b/frontend/src/component/changeRequest/changeRequest.types.ts
index 6ffb49eff4..89765d9106 100644
--- a/frontend/src/component/changeRequest/changeRequest.types.ts
+++ b/frontend/src/component/changeRequest/changeRequest.types.ts
@@ -12,6 +12,12 @@ export interface IChangeRequest {
approvals: IChangeRequestApproval[];
}
+export interface IChangeRequestEnvironmentConfig {
+ environment: string;
+ type: string;
+ changeRequestEnabled: boolean;
+}
+
export interface IChangeRequestFeature {
name: string;
conflict?: string;
diff --git a/frontend/src/component/project/Project/ProjectSettings/ChangeRequestConfiguration/ChangeRequestConfiguration.tsx b/frontend/src/component/project/Project/ProjectSettings/ChangeRequestConfiguration/ChangeRequestConfiguration.tsx
index 80a8a0681c..be3f998455 100644
--- a/frontend/src/component/project/Project/ProjectSettings/ChangeRequestConfiguration/ChangeRequestConfiguration.tsx
+++ b/frontend/src/component/project/Project/ProjectSettings/ChangeRequestConfiguration/ChangeRequestConfiguration.tsx
@@ -1,43 +1,71 @@
import { useMemo, useState, VFC } from 'react';
-import { HeaderGroup, Row } from 'react-table';
+import { HeaderGroup, useGlobalFilter, useTable } from 'react-table';
import { Alert, Box, Typography } from '@mui/material';
import {
SortableTableHeader,
Table,
- TableCell,
TableBody,
+ TableCell,
TableRow,
} from 'component/common/Table';
-import { useGlobalFilter, useTable } from 'react-table';
import { sortTypes } from 'utils/sortTypes';
import { PageContent } from 'component/common/PageContent/PageContent';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
import PermissionSwitch from 'component/common/PermissionSwitch/PermissionSwitch';
-import { UPDATE_FEATURE_ENVIRONMENT } from 'component/providers/AccessProvider/permissions';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { Dialogue } from 'component/common/Dialogue/Dialogue';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+import { useChangeRequestConfig } from '../../../../../hooks/api/getters/useChangeRequestConfig/useChangeRequestConfig';
+import { useChangeRequestApi } from '../../../../../hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
+import { UPDATE_PROJECT } from '@server/types/permissions';
+import useToast from '../../../../../hooks/useToast';
+import { formatUnknownError } from '../../../../../utils/formatUnknownError';
export const ChangeRequestConfiguration: VFC = () => {
const [dialogState, setDialogState] = useState<{
isOpen: boolean;
enableEnvironment?: string;
+ isEnabled: boolean;
}>({
isOpen: false,
enableEnvironment: '',
+ isEnabled: false,
});
const projectId = useRequiredPathParam('projectId');
- const data = [
- {
- environment: 'dev',
- type: 'test',
- isEnabled: false,
- },
- ] as any[]; // FIXME: type
+ const { data, loading, refetchChangeRequestConfig } =
+ useChangeRequestConfig(projectId);
+ const { updateChangeRequestEnvironmentConfig } = useChangeRequestApi();
+ const { setToastData, setToastApiError } = useToast();
- const onClick = (enableEnvironment: string) => () => {
- setDialogState({ isOpen: true, enableEnvironment });
+ const onClick = (enableEnvironment: string, isEnabled: boolean) => () => {
+ setDialogState({ isOpen: true, enableEnvironment, isEnabled });
+ };
+
+ const onConfirm = async () => {
+ if (dialogState.enableEnvironment) {
+ try {
+ await updateChangeRequestEnvironmentConfig(
+ projectId,
+ dialogState.enableEnvironment,
+ !dialogState.isEnabled
+ );
+ setToastData({
+ type: 'success',
+ title: 'Updated change request status',
+ text: 'Successfully updated change request status.',
+ });
+ refetchChangeRequestConfig();
+ } catch (error) {
+ const message = formatUnknownError(error);
+ setToastApiError(message);
+ }
+ }
+ setDialogState({
+ isOpen: false,
+ enableEnvironment: '',
+ isEnabled: false,
+ });
};
const columns = useMemo(
@@ -55,7 +83,8 @@ export const ChangeRequestConfiguration: VFC = () => {
},
{
Header: 'Status',
- accessor: 'isEnabled',
+ accessor: 'changeRequestEnabled',
+ id: 'changeRequestEnabled',
align: 'center',
Cell: ({ value, row: { original } }: any) => (
@@ -67,9 +96,12 @@ export const ChangeRequestConfiguration: VFC = () => {
checked={value}
environmentId={original.environment}
projectId={projectId}
- permission={UPDATE_FEATURE_ENVIRONMENT} // FIXME: permission - enable change request
+ permission={UPDATE_PROJECT}
inputProps={{ 'aria-label': original.environment }}
- onClick={onClick(original.environment)}
+ onClick={onClick(
+ original.environment,
+ original.changeRequestEnabled
+ )}
/>
),
@@ -84,6 +116,7 @@ export const ChangeRequestConfiguration: VFC = () => {
const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
useTable(
{
+ // @ts-ignore
columns,
data,
sortTypes,
@@ -95,11 +128,10 @@ export const ChangeRequestConfiguration: VFC = () => {
},
useGlobalFilter
);
-
return (
}
- // isLoading={loading}
+ isLoading={loading}
>
If change request is enabled for an environment, then any change
@@ -128,20 +160,21 @@ export const ChangeRequestConfiguration: VFC = () => {
{
- alert('clicked');
- /* FIXME: API action */
- }}
+ onClick={() => onConfirm()}
open={dialogState.isOpen}
onClose={() =>
setDialogState(state => ({ ...state, isOpen: false }))
}
- primaryButtonText="Enable"
+ primaryButtonText={dialogState.isEnabled ? 'Disable' : 'Enable'}
secondaryButtonText="Cancel"
- title="Enable change request"
+ title={`${
+ dialogState.isEnabled ? 'Disable' : 'Enable'
+ } change requests`}
>
- You are about to enable “Change request”
+ You are about to{' '}
+ {dialogState.isEnabled ? 'disable' : 'enable'} “Change
+ request”
{
/>
.
-
- When enabling change request for an environment, you need to
- be sure that your Unleash Admin already have created the
- custom project roles in your Unleash instance so you can
- assign your project members from the project access page.
-
+
+ When enabling change request for an environment, you
+ need to be sure that your Unleash Admin already have
+ created the custom project roles in your Unleash
+ instance so you can assign your project members from
+ the project access page.
+
+ }
+ />
);
diff --git a/frontend/src/hooks/api/actions/useChangeRequestApi/useChangeRequestApi.ts b/frontend/src/hooks/api/actions/useChangeRequestApi/useChangeRequestApi.ts
index 490683f95a..9c8d20ffd0 100644
--- a/frontend/src/hooks/api/actions/useChangeRequestApi/useChangeRequestApi.ts
+++ b/frontend/src/hooks/api/actions/useChangeRequestApi/useChangeRequestApi.ts
@@ -67,10 +67,28 @@ export const useChangeRequestApi = () => {
}
};
+ const updateChangeRequestEnvironmentConfig = async (
+ project: string,
+ environment: string,
+ enabled: boolean
+ ) => {
+ const path = `api/admin/projects/${project}/environments/${environment}/change-requests/config`;
+ const req = createRequest(path, {
+ method: 'PUT',
+ body: JSON.stringify({ changeRequestsEnabled: enabled }),
+ });
+ try {
+ return await makeRequest(req.caller, req.id);
+ } catch (e) {
+ throw e;
+ }
+ };
+
return {
addChangeRequest,
changeState,
discardChangeRequestEvent,
+ updateChangeRequestEnvironmentConfig,
errors,
loading,
};
diff --git a/frontend/src/hooks/api/getters/useChangeRequestConfig/useChangeRequestConfig.ts b/frontend/src/hooks/api/getters/useChangeRequestConfig/useChangeRequestConfig.ts
new file mode 100644
index 0000000000..1d8f2580e1
--- /dev/null
+++ b/frontend/src/hooks/api/getters/useChangeRequestConfig/useChangeRequestConfig.ts
@@ -0,0 +1,24 @@
+import useSWR from 'swr';
+import { formatApiPath } from 'utils/formatPath';
+import handleErrorResponses from '../httpErrorResponseHandler';
+import { IChangeRequestEnvironmentConfig } from 'component/changeRequest/changeRequest.types';
+
+export const useChangeRequestConfig = (projectId: string) => {
+ const { data, error, mutate } = useSWR(
+ formatApiPath(`api/admin/projects/${projectId}/change-requests/config`),
+ fetcher
+ );
+
+ return {
+ data: data || [],
+ loading: !error && !data,
+ refetchChangeRequestConfig: () => mutate(),
+ error,
+ };
+};
+
+const fetcher = (path: string) => {
+ return fetch(path)
+ .then(handleErrorResponses('Request changes'))
+ .then(res => res.json());
+};
diff --git a/yarn.lock b/yarn.lock
index 793527b2c8..f935f808c7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2126,13 +2126,12 @@ body-parser@1.19.0:
raw-body "2.4.0"
type-is "~1.6.17"
-brace-expansion@^1.1.7:
- version "1.1.11"
- resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz"
- integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+brace-expansion@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+ integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
dependencies:
balanced-match "^1.0.0"
- concat-map "0.0.1"
braces@^3.0.1, braces@^3.0.2:
version "3.0.2"
@@ -2505,11 +2504,6 @@ compression@^1.7.4:
safe-buffer "5.1.2"
vary "~1.1.2"
-concat-map@0.0.1:
- version "0.0.1"
- resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
- integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
-
concat-stream@^1.5.2:
version "1.6.2"
resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz"
@@ -5396,12 +5390,12 @@ min-indent@^1.0.1:
resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
-minimatch@^3.0.3, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2, minimatch@^5.0.1:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
- integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+minimatch@^3.0.3, minimatch@^3.0.4, minimatch@^3.1.2, minimatch@^5.0.0, minimatch@^5.0.1:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7"
+ integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==
dependencies:
- brace-expansion "^1.1.7"
+ brace-expansion "^2.0.1"
minimist-options@4.1.0:
version "4.1.0"