From d5c0ec2628f96e5df675918ec86c18de98d6c673 Mon Sep 17 00:00:00 2001 From: Fredrik Strand Oseberg Date: Thu, 4 Nov 2021 14:24:36 +0100 Subject: [PATCH] fix: guard for disabling envs (#492) * fix: guard for disabling envs * fix: remove local function * fix: remove local type --- .../ProjectEnvironment/ProjectEnvironment.tsx | 44 ++++++++++++------- .../ProjectEnvironment/getEnabledEnvs.test.ts | 41 +++++++++++++++++ .../project/ProjectEnvironment/helpers.ts | 10 +++++ frontend/src/interfaces/environments.ts | 5 +++ 4 files changed, 85 insertions(+), 15 deletions(-) create mode 100644 frontend/src/component/project/ProjectEnvironment/getEnabledEnvs.test.ts create mode 100644 frontend/src/component/project/ProjectEnvironment/helpers.ts diff --git a/frontend/src/component/project/ProjectEnvironment/ProjectEnvironment.tsx b/frontend/src/component/project/ProjectEnvironment/ProjectEnvironment.tsx index 188adde45c..bd5738d0d0 100644 --- a/frontend/src/component/project/ProjectEnvironment/ProjectEnvironment.tsx +++ b/frontend/src/component/project/ProjectEnvironment/ProjectEnvironment.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import ConditionallyRender from '../../common/ConditionallyRender'; import { useStyles } from './ProjectEnvironment.styles'; @@ -18,11 +18,8 @@ import EnvironmentDisableConfirm from './EnvironmentDisableConfirm/EnvironmentDi import { Link } from 'react-router-dom'; import { Alert } from '@material-ui/lab'; import PermissionSwitch from '../../common/PermissionSwitch/PermissionSwitch'; - -export interface ProjectEnvironment { - name: string; - enabled: boolean; -} +import { IProjectEnvironment } from '../../../interfaces/environments'; +import { getEnabledEnvs } from './helpers'; interface ProjectEnvironmentListProps { projectId: string; @@ -30,6 +27,7 @@ interface ProjectEnvironmentListProps { const ProjectEnvironmentList = ({ projectId }: ProjectEnvironmentListProps) => { // api state + const [envs, setEnvs] = useState([]); const { toast, setToastData } = useToast(); const { uiConfig } = useUiConfig(); const { @@ -43,11 +41,20 @@ const ProjectEnvironmentList = ({ projectId }: ProjectEnvironmentListProps) => { useProjectApi(); // local state - const [selectedEnv, setSelectedEnv] = useState(); + const [selectedEnv, setSelectedEnv] = useState(); const [confirmName, setConfirmName] = useState(''); const ref = useLoading(loading); const styles = useStyles(); + useEffect(() => { + const envs = environments.map(e => ({ + name: e.name, + enabled: project?.environments.includes(e.name), + })); + + setEnvs(envs); + }, [environments, project?.environments]); + const refetch = () => { refetchEnvs(); refetchProject(); @@ -71,7 +78,17 @@ const ProjectEnvironmentList = ({ projectId }: ProjectEnvironmentListProps) => { const toggleEnv = async (env: ProjectEnvironment) => { if (env.enabled) { - setSelectedEnv(env); + const enabledEnvs = getEnabledEnvs(envs); + + if (enabledEnvs > 1) { + setSelectedEnv(env); + return; + } + setToastData({ + text: 'You must always have one active environment', + type: 'error', + show: true, + }); } else { try { await addEnvironmentToProject(projectId, env.name); @@ -119,11 +136,6 @@ const ProjectEnvironmentList = ({ projectId }: ProjectEnvironmentListProps) => { setConfirmName(''); }; - const envs = environments.map(e => ({ - name: e.name, - enabled: project?.environments.includes(e.name), - })); - const genLabel = (env: ProjectEnvironment) => ( <> {env.name} environment is{' '} @@ -140,12 +152,14 @@ const ProjectEnvironmentList = ({ projectId }: ProjectEnvironmentListProps) => { label={genLabel(env)} control={ toggleEnv(env)} /> } /> diff --git a/frontend/src/component/project/ProjectEnvironment/getEnabledEnvs.test.ts b/frontend/src/component/project/ProjectEnvironment/getEnabledEnvs.test.ts new file mode 100644 index 0000000000..cfff0f89dd --- /dev/null +++ b/frontend/src/component/project/ProjectEnvironment/getEnabledEnvs.test.ts @@ -0,0 +1,41 @@ +import { getEnabledEnvs } from './helpers'; + +const generateEnv = (enabled: boolean, name: string) => { + return { + name, + enabled, + }; +}; + +test('it returns 1 when one environment is enabled', () => { + const input = [ + generateEnv(true, 'test1'), + generateEnv(false, 'test2'), + generateEnv(false, 'test3'), + ]; + + const enabledEnvs = getEnabledEnvs(input); + expect(enabledEnvs).toBe(1); +}); + +test('it returns 3 when three environments are enabled', () => { + const input = [ + generateEnv(true, 'test1'), + generateEnv(true, 'test2'), + generateEnv(true, 'test3'), + ]; + + const enabledEnvs = getEnabledEnvs(input); + expect(enabledEnvs).toBe(3); +}); + +test('it returns 2 when tw environments are enabled', () => { + const input = [ + generateEnv(true, 'test1'), + generateEnv(true, 'test2'), + generateEnv(false, 'test3'), + ]; + + const enabledEnvs = getEnabledEnvs(input); + expect(enabledEnvs).toBe(2); +}); diff --git a/frontend/src/component/project/ProjectEnvironment/helpers.ts b/frontend/src/component/project/ProjectEnvironment/helpers.ts new file mode 100644 index 0000000000..ac422eff26 --- /dev/null +++ b/frontend/src/component/project/ProjectEnvironment/helpers.ts @@ -0,0 +1,10 @@ +import { IProjectEnvironment } from '../../../interfaces/environments'; + +export const getEnabledEnvs = (envs: IProjectEnvironment[]) => { + return envs.reduce((enabledEnvs, currentEnv) => { + if (currentEnv.enabled) { + return enabledEnvs + 1; + } + return enabledEnvs; + }, 0); +}; diff --git a/frontend/src/interfaces/environments.ts b/frontend/src/interfaces/environments.ts index 6abd9b49e8..5aef539fcb 100644 --- a/frontend/src/interfaces/environments.ts +++ b/frontend/src/interfaces/environments.ts @@ -7,6 +7,11 @@ export interface IEnvironment { protected: boolean; } +export interface IProjectEnvironment { + enabled: boolean; + name: string; +} + export interface IEnvironmentPayload { name: string; type: string;