From fcdfecf8a1f5d80d87b1598d31aef34561ba5d1c Mon Sep 17 00:00:00 2001 From: Amit Kumar Das <40661238+amityt@users.noreply.github.com> Date: Thu, 27 May 2021 11:21:20 +0530 Subject: [PATCH] chore(litmus-portal): Added pre-defined workflows in myhub and minor bug fix (#2837) * Added pre-defined experiments in myhub and bug fix * Minor change in query name Signed-off-by: Amit Kumar Das --- .../public/icons/default-experiment.svg | 44 +- .../public/icons/no-experiment-found.svg | 16 + .../public/locales/en/translation.yaml | 11 +- .../src/components/InstallChaos/index.tsx | 6 +- .../frontend/src/containers/app/App.tsx | 2 - litmus-portal/frontend/src/graphql/queries.ts | 6 + .../src/views/MyHub/MyHubCharts/chartCard.tsx | 5 +- .../src/views/MyHub/MyHubCharts/index.tsx | 185 +++++-- .../src/views/MyHub/MyHubCharts/styles.ts | 11 + .../src/views/MyHub/MyHubEdit/index.tsx | 509 ------------------ .../src/views/MyHub/MyHubEdit/styles.ts | 194 ------- .../src/views/MyHub/MyHubExperiment/index.tsx | 61 ++- .../src/views/MyHub/MyHubExperiment/styles.ts | 3 +- .../graph/generated/generated.go | 70 +-- .../graphql-server/graph/schema.graphqls | 2 +- .../graphql-server/graph/schema.resolvers.go | 4 +- .../pkg/myhub/handler/handler.go | 27 +- .../graphql-server/pkg/myhub/myhub.go | 36 +- 18 files changed, 338 insertions(+), 854 deletions(-) create mode 100644 litmus-portal/frontend/public/icons/no-experiment-found.svg delete mode 100644 litmus-portal/frontend/src/views/MyHub/MyHubEdit/index.tsx delete mode 100644 litmus-portal/frontend/src/views/MyHub/MyHubEdit/styles.ts diff --git a/litmus-portal/frontend/public/icons/default-experiment.svg b/litmus-portal/frontend/public/icons/default-experiment.svg index ef8b1e05c..923c368d7 100644 --- a/litmus-portal/frontend/public/icons/default-experiment.svg +++ b/litmus-portal/frontend/public/icons/default-experiment.svg @@ -1,39 +1,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + diff --git a/litmus-portal/frontend/public/icons/no-experiment-found.svg b/litmus-portal/frontend/public/icons/no-experiment-found.svg new file mode 100644 index 000000000..4ca4e26e4 --- /dev/null +++ b/litmus-portal/frontend/public/icons/no-experiment-found.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/litmus-portal/frontend/public/locales/en/translation.yaml b/litmus-portal/frontend/public/locales/en/translation.yaml index 42e32fe75..15cf09bfa 100644 --- a/litmus-portal/frontend/public/locales/en/translation.yaml +++ b/litmus-portal/frontend/public/locales/en/translation.yaml @@ -425,7 +425,7 @@ chaosWorkflows: experimentsPassed: 'Experiments Passed : ' showExperiments: Show Experiments showTheWorkflow: Show the workflow - showTheAnalytics: show the analytics + showTheAnalytics: Show the analytics na: NA analyticsDashboardViews: @@ -754,7 +754,7 @@ createWorkflow: header: Adjust the weights of the experiments in the workflow info: You have selected infoNext: tests in the “Kubernetes conformance test” workflow. Successful outcome of each test carries a certain weight. We have pre-selected weights for each test for you. However, you may review and modify the weightage against. - infoNextStrong: The weights are relative to each other. + infoNextStrong: These weights are used to calculate the resiliency score at the end of test. testHeading: Kubernetes conformance test testInfo: Compare the importance of the items above and launch a demo version of Kubernetes conformance test to see how it works. button: @@ -1068,6 +1068,11 @@ myhub: header: MyHub github: github.com/ syncingRepo: Loading Charts, Please Wait...! + preDefined: Pre-defined workflows + chaosCharts: Chaos-charts + noExp: No predefined workflows available with information in this Hub + lastSynced: 'Last synced at: ' + repoLink: 'Repository Link: ' connectHubPage: connectHub: Connect a new chaos hub editHub: Edit hub configuration @@ -1104,6 +1109,8 @@ myhub: installRBACDesc: Create a service account using the following command installEngine: Sample Chaos Engine installEngineDesc: Copy and edit this sample Chaos Engine yaml according to your application needs + checkPreDefined: Check the pre-defined workflow + checkPreDefinedDesc: You can view the workflow details here editPage: edit: Edit the Hub click: Then click on the update button diff --git a/litmus-portal/frontend/src/components/InstallChaos/index.tsx b/litmus-portal/frontend/src/components/InstallChaos/index.tsx index c407d4f18..104fb202c 100644 --- a/litmus-portal/frontend/src/components/InstallChaos/index.tsx +++ b/litmus-portal/frontend/src/components/InstallChaos/index.tsx @@ -9,17 +9,19 @@ interface InstallProps { title: string; description: string; yamlLink: string; + isPredefined?: boolean; } const InstallChaos: React.FC = ({ title, description, yamlLink, + isPredefined, }) => { const classes = useStyles(); const { t } = useTranslation(); const [copying, setCopying] = useState(false); - const yaml = `kubectl apply -f ${yamlLink}`; + const yaml = isPredefined ? yamlLink : `kubectl apply -f ${yamlLink}`; function copyTextToClipboard(text: string) { if (!navigator.clipboard) { @@ -40,7 +42,7 @@ const InstallChaos: React.FC = ({
{description}
- kubectl apply -f {yamlLink} + {isPredefined ? yamlLink : `kubectl apply -f ${yamlLink}`}
diff --git a/litmus-portal/frontend/src/containers/app/App.tsx b/litmus-portal/frontend/src/containers/app/App.tsx index 74bd99941..df6825c68 100644 --- a/litmus-portal/frontend/src/containers/app/App.tsx +++ b/litmus-portal/frontend/src/containers/app/App.tsx @@ -46,7 +46,6 @@ const DashboardPage = lazy(() => import('../../pages/ApplicationDashboard')); const MyHub = lazy(() => import('../../pages/ChaosHub')); const ChaosChart = lazy(() => import('../../views/MyHub/MyHubCharts')); const MyHubExperiment = lazy(() => import('../../views/MyHub/MyHubExperiment')); -const MyHubEdit = lazy(() => import('../../views/MyHub/MyHubEdit')); const Routes: React.FC = () => { const baseRoute = window.location.pathname.split('/')[1]; @@ -217,7 +216,6 @@ const Routes: React.FC = () => { - >; projectID: string; userRole: string; + isPredefined?: boolean; } const ChartCard: React.FC = ({ @@ -24,6 +25,7 @@ const ChartCard: React.FC = ({ setSearch, projectID, userRole, + isPredefined, }) => { const classes = useStyles(); const experimentDefaultImagePath = `${config.grahqlEndpoint}/icon`; @@ -31,7 +33,6 @@ const ChartCard: React.FC = ({ const [imageURL, setImageURL] = useState( `${experimentDefaultImagePath}/${projectID}/${UserHub?.HubName}/${expName.ChaosName}/${expName.ExperimentName}.png` ); - return (
= ({ }} className={classes.categoryName} > - {expName.ChaosName}/ + {!isPredefined && `${expName.ChaosName}/`} {expName.ExperimentName} diff --git a/litmus-portal/frontend/src/views/MyHub/MyHubCharts/index.tsx b/litmus-portal/frontend/src/views/MyHub/MyHubCharts/index.tsx index b99d579b9..3e76b2183 100644 --- a/litmus-portal/frontend/src/views/MyHub/MyHubCharts/index.tsx +++ b/litmus-portal/frontend/src/views/MyHub/MyHubCharts/index.tsx @@ -1,18 +1,30 @@ import { useQuery } from '@apollo/client'; -import { Backdrop, Typography } from '@material-ui/core'; +import { + Accordion, + AccordionDetails, + AccordionSummary, + Backdrop, + Typography, +} from '@material-ui/core'; import moment from 'moment'; import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router-dom'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import Loader from '../../../components/Loader'; import Center from '../../../containers/layouts/Center'; import Scaffold from '../../../containers/layouts/Scaffold'; -import { GET_CHARTS_DATA, GET_HUB_STATUS } from '../../../graphql'; +import { + GET_CHARTS_DATA, + GET_HUB_STATUS, + GET_PREDEFINED_WORKFLOW_LIST, +} from '../../../graphql'; import { Chart, Charts, HubStatus } from '../../../models/redux/myhub'; import { getProjectID, getProjectRole } from '../../../utils/getSearchParams'; import ChartCard from './chartCard'; import HeaderSection from './headerSection'; import useStyles from './styles'; +import BackButton from '../../../components/Button/BackButton'; interface ChartName { ChaosName: string; @@ -48,16 +60,34 @@ const MyHub: React.FC = () => { HubName: paramData.hubname, projectID, }, - fetchPolicy: 'cache-and-network', + fetchPolicy: 'network-only', }); + const { data: predefinedData, loading: predefinedLoading } = useQuery( + GET_PREDEFINED_WORKFLOW_LIST, + { + variables: { + hubname: paramData.hubname, + projectid: projectID, + }, + fetchPolicy: 'network-only', + } + ); + // State for searching charts const [search, setSearch] = useState(''); + const [searchPredefined, setSearchPredefined] = useState(''); + const changeSearch = ( event: React.ChangeEvent ) => { setSearch(event.target.value as string); }; + const handlePreDefinedSearch = ( + event: React.ChangeEvent + ) => { + setSearchPredefined(event.target.value as string); + }; const [totalExp, setTotalExperiment] = useState([]); const exp: ChartName[] = []; @@ -69,6 +99,15 @@ const MyHub: React.FC = () => { return 'Date not available'; }; + const [expanded, setExpanded] = React.useState('panel2'); + + const handleChange = (panel: string) => ( + event: React.ChangeEvent<{}>, + newExpanded: boolean + ) => { + setExpanded(newExpanded ? panel : false); + }; + useEffect(() => { if (data !== undefined) { const chartList = data.getCharts; @@ -84,7 +123,7 @@ const MyHub: React.FC = () => { } }, [data]); - return loading ? ( + return loading || predefinedLoading ? (
@@ -95,45 +134,131 @@ const MyHub: React.FC = () => { ) : ( +
{UserHub?.HubName} - + + {t('myhub.myhubChart.repoLink')} {UserHub?.RepoURL}/{UserHub?.RepoBranch} - Last synced at: {formatDate(UserHub ? UserHub.LastSyncedAt : '')} + {t('myhub.myhubChart.lastSynced')}{' '} + {formatDate(UserHub ? UserHub.LastSyncedAt : '')} {/*
*/}
-
- -
- {totalExp && - totalExp.length > 0 && - totalExp - .filter( - (data) => - data.ChaosName.toLowerCase().includes(search.trim()) || - data.ExperimentName.toLowerCase().includes(search.trim()) - ) - .map((expName: ChartName) => { - return ( - + } + aria-controls="panel1d-content" + id="panel1d-header" + > + + {t('myhub.myhubChart.preDefined')} + + + +
+ +
+ {predefinedData?.GetPredefinedWorkflowList.length > 0 ? ( + predefinedData?.GetPredefinedWorkflowList.filter( + (data: string) => + data.toLowerCase().includes(searchPredefined.trim()) + ).map((expName: string) => { + return ( + + ); + }) + ) : ( + <> + no experiment - ); - })} -
-
+ + {t('myhub.myhubChart.noExp')} + + + )} +
+
+ + + + } + aria-controls="panel2d-content" + id="panel2d-header" + > + + {t('myhub.myhubChart.chaosCharts')} + + + +
+ +
+ {totalExp && + totalExp.length > 0 && + totalExp + .filter( + (data) => + data.ChaosName.toLowerCase().includes(search.trim()) || + data.ExperimentName.toLowerCase().includes(search.trim()) + ) + .map((expName: ChartName) => { + return ( + + ); + })} +
+
+
+
); }; diff --git a/litmus-portal/frontend/src/views/MyHub/MyHubCharts/styles.ts b/litmus-portal/frontend/src/views/MyHub/MyHubCharts/styles.ts index dd20c97a3..65f3310c3 100644 --- a/litmus-portal/frontend/src/views/MyHub/MyHubCharts/styles.ts +++ b/litmus-portal/frontend/src/views/MyHub/MyHubCharts/styles.ts @@ -101,6 +101,17 @@ const useStyles = makeStyles((theme) => ({ marginTop: theme.spacing(1.875), fontSize: '0.875rem', }, + MuiAccordionroot: { + '&.MuiAccordion-root:before': { + backgroundColor: theme.palette.common.white, + }, + }, + noExp: { + margin: theme.spacing(6), + }, + chartAccordion: { + marginTop: theme.spacing(2.5), + }, })); export default useStyles; diff --git a/litmus-portal/frontend/src/views/MyHub/MyHubEdit/index.tsx b/litmus-portal/frontend/src/views/MyHub/MyHubEdit/index.tsx deleted file mode 100644 index 44a209a2d..000000000 --- a/litmus-portal/frontend/src/views/MyHub/MyHubEdit/index.tsx +++ /dev/null @@ -1,509 +0,0 @@ -import { useMutation, useQuery } from '@apollo/client'; -import { - FormControl, - FormControlLabel, - Radio, - RadioGroup, - Typography, -} from '@material-ui/core'; -import { Done } from '@material-ui/icons'; -import { ButtonFilled, ButtonOutlined, InputField, Modal } from 'litmus-ui'; -import React, { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useParams } from 'react-router-dom'; -import BackButton from '../../../components/Button/BackButton'; -import GithubInputFields from '../../../components/GitHubComponents/GithubInputFields/GithubInputFields'; -import GitHubToggleButton from '../../../components/GitHubComponents/GitHubToggleButtons/GitHubToggleButton'; -import Loader from '../../../components/Loader'; -import { LocalQuickActionCard } from '../../../components/LocalQuickActionCard'; -import VideoCarousel from '../../../components/VideoCarousel'; -import Scaffold from '../../../containers/layouts/Scaffold'; -import { GENERATE_SSH, GET_HUB_STATUS, UPDATE_MY_HUB } from '../../../graphql'; -import { - CreateMyHub, - MyHubData, - MyHubType, - SSHKey, - SSHKeys, -} from '../../../models/graphql/user'; -import { HubStatus } from '../../../models/redux/myhub'; -import { history } from '../../../redux/configureStore'; -import { getProjectID, getProjectRole } from '../../../utils/getSearchParams'; -import { - isValidWebUrl, - validateStartEmptySpacing, -} from '../../../utils/validate'; -import useStyles from './styles'; - -interface MyHubParams { - hubname: string; -} - -interface GitHub { - HubName: string; - GitURL: string; - GitBranch: string; -} - -interface MyHubToggleProps { - isPublicToggled: boolean; - isPrivateToggled: boolean; -} - -const MyHub: React.FC = () => { - const classes = useStyles(); - const { t } = useTranslation(); - const projectID = getProjectID(); - const userRole = getProjectRole(); - const params: MyHubParams = useParams(); - const { data, loading } = useQuery(GET_HUB_STATUS, { - variables: { data: projectID }, - fetchPolicy: 'cache-and-network', - }); - const hubData = data?.getHubStatus.filter( - (hubs) => hubs.HubName === params.hubname - )[0]; - - const [gitHub, setGitHub] = useState({ - HubName: '', - GitURL: '', - GitBranch: '', - }); - const [error, setError] = useState(''); - const [isOpen, setIsOpen] = useState(false); - const [cloningRepo, setCloningRepo] = useState(false); - const [isToggled, setIsToggled] = React.useState({ - isPublicToggled: true, - isPrivateToggled: false, - }); - const [privateHub, setPrivateHub] = useState('token'); - const [accessToken, setAccessToken] = useState(''); - const [sshKey, setSshKey] = useState({ - privateKey: '', - publicKey: '', - }); - const [copying, setCopying] = useState(false); - - const [updateMyHub] = useMutation(UPDATE_MY_HUB, { - onCompleted: () => { - setCloningRepo(false); - }, - onError: (error) => { - setCloningRepo(false); - setError(error.message); - }, - }); - - // Mutation to generate SSH key - const [generateSSHKey, { loading: sshLoading }] = useMutation( - GENERATE_SSH, - { - onCompleted: (data) => { - setSshKey({ - privateKey: data.generaterSSHKey.privateKey, - publicKey: data.generaterSSHKey.publicKey, - }); - }, - } - ); - - useEffect(() => { - if (hubData !== undefined) { - setGitHub({ - HubName: hubData.HubName, - GitURL: hubData.RepoURL, - GitBranch: hubData.RepoBranch, - }); - if (hubData.IsPrivate) { - setIsToggled({ - isPublicToggled: false, - isPrivateToggled: true, - }); - } else { - setIsToggled({ - isPublicToggled: true, - isPrivateToggled: false, - }); - } - if (hubData.AuthType === MyHubType.token) { - setPrivateHub('token'); - setAccessToken(hubData.Token); - } else if (hubData.AuthType === MyHubType.ssh) { - setPrivateHub('ssh'); - setSshKey({ - privateKey: hubData.SSHPrivateKey, - publicKey: hubData.SSHPublicKey, - }); - } else { - setPrivateHub('token'); - } - } - }, [hubData]); - - const handleGitURL = ( - event: React.ChangeEvent - ) => { - setGitHub({ - HubName: gitHub.HubName, - GitURL: event.target.value, - GitBranch: gitHub.GitBranch, - }); - }; - - const handleGitBranch = ( - event: React.ChangeEvent - ) => { - setGitHub({ - HubName: gitHub.HubName, - GitURL: gitHub.GitURL, - GitBranch: event.target.value, - }); - }; - - const handleSubmit = (event: React.FormEvent) => { - event.preventDefault(); - updateMyHub({ - variables: { - MyHubDetails: { - id: hubData?.id, - HubName: gitHub.HubName.trim(), - RepoURL: gitHub.GitURL, - RepoBranch: gitHub.GitBranch, - IsPrivate: isToggled.isPublicToggled - ? false - : !!isToggled.isPrivateToggled, - AuthType: isToggled.isPublicToggled - ? MyHubType.basic - : privateHub === 'token' - ? MyHubType.token - : privateHub === 'ssh' - ? MyHubType.ssh - : MyHubType.basic, - Token: accessToken, - UserName: 'user', - Password: 'user', - SSHPrivateKey: sshKey.privateKey, - SSHPublicKey: sshKey.publicKey, - }, - projectID, - }, - }); - setCloningRepo(true); - setIsOpen(true); - }; - - const handleClose = () => { - setIsOpen(false); - history.push({ - pathname: '/myhub', - search: `?projectID=${projectID}&projectRole=${userRole}`, - }); - }; - - // Function to copy the SSH key - const copyTextToClipboard = (text: string) => { - if (!navigator.clipboard) { - console.error('Oops Could not copy text: '); - return; - } - setCopying(true); - navigator.clipboard - .writeText(text) - .catch((err) => console.error('Async: Could not copy text: ', err)); - setTimeout(() => setCopying(false), 3000); - }; - return ( - -
-
- -
- - {t('myhub.editPage.edit')} - -
-
- {loading ? ( - - ) : ( -
- - - {t('myhub.connectHubPage.enterInfo')} - - - {t('myhub.editPage.click')} - -
-
-
- - setGitHub({ - HubName: e.target.value, - GitURL: gitHub.GitURL, - GitBranch: gitHub.GitBranch, - }) - } - /> -
-
-
-
- -
- {/* If Public Repo is clicked */} - {isToggled.isPublicToggled ? ( -
- -
- ) : null} - {/* If Private Repo is Clicked */} - {isToggled.isPrivateToggled ? ( -
-
- -
- - { - if (e.target.value === 'ssh') { - generateSSHKey(); - } - if (e.target.value === 'token') { - setSshKey({ - privateKey: '', - publicKey: '', - }); - } - setPrivateHub(e.target.value); - }} - > - - } - label={ - - {t('myhub.connectHubPage.accessToken')} - - } - /> - {privateHub === 'token' ? ( - setAccessToken(e.target.value)} - /> - ) : null} - - } - label={ - - {t('myhub.connectHubPage.ssh')} - - } - /> - {privateHub === 'ssh' ? ( -
- - {t('myhub.connectHubPage.sshAlert')} - - - {t('myhub.connectHubPage.sshText')} - -
- {sshLoading ? ( - - ) : ( - <> - - {sshKey.publicKey} - -
- - copyTextToClipboard( - sshKey.publicKey - ) - } - > - {!copying ? ( -
- copy - - {t('myhub.installChaos.copy')} - -
- ) : ( -
- - - {t('myhub.installChaos.copied')} - -
- )} -
-
- - )} -
-
- ) : null} -
-
-
- ) : null} -
-
-
- - {t('myhub.editPage.submit')} - -
- - ✕ - - } - > -
- {cloningRepo ? ( -
- - - {t('myhub.editPage.wait')} - -
- ) : ( -
- {error.length ? ( -
- - {t('myhub.editPage.error')}{' '} - - - Error: {error} - -
- ) : ( - <> - checkmark - - {t('myhub.editPage.success')} - - - {t('myhub.editPage.desc')} - - - {t('myhub.connectHubPage.myHub')} - - - )} -
- )} -
-
-
-
-
- )} -
- - - {t('myhub.connectHubPage.videoDesc')} - -
- -
-
-
-
- ); -}; - -export default MyHub; diff --git a/litmus-portal/frontend/src/views/MyHub/MyHubEdit/styles.ts b/litmus-portal/frontend/src/views/MyHub/MyHubEdit/styles.ts deleted file mode 100644 index 3f365e39b..000000000 --- a/litmus-portal/frontend/src/views/MyHub/MyHubEdit/styles.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { makeStyles } from '@material-ui/core'; - -const useStyles = makeStyles((theme) => ({ - mainDiv: { - display: 'flex', - flexDirection: 'row', - width: '100%', - marginTop: theme.spacing(3), - }, - root: { - minWidth: '28.125rem', - marginLeft: 'auto', - }, - header: { - width: '100%', - display: 'flex', - flexDirection: 'column', - color: theme.palette.text.primary, - margin: theme.spacing(4.5, 1.5, 2.5, 1.5), - }, - detailsDiv: { - flexGrow: 1, - display: 'flex', - flexDirection: 'column', - backgroundColor: theme.palette.background.paper, - border: `1px solid ${theme.palette.border.main}`, - padding: theme.spacing(2.5), - }, - connectText: { - fontSize: '0.875rem', - marginTop: theme.spacing(1.25), - }, - inputDiv: { - display: 'flex', - flexDirection: 'column', - marginTop: theme.spacing(2.5), - marginLeft: theme.spacing(-1.25), - }, - inputField: { - marginBottom: theme.spacing(2.5), - marginLeft: theme.spacing(2), - }, - inputFieldBranch: { - marginBottom: theme.spacing(2.5), - width: '10rem', - marginRight: theme.spacing(2.5), - }, - modalDiv: { - display: 'flex', - flexDirection: 'column', - height: '25rem', - alignItems: 'center', - justifyContent: 'center', - }, - modalHeading: { - fontSize: '2.25rem', - }, - modalDesc: { - fontSize: '1rem', - width: '21.875', - marginBottom: theme.spacing(2.5), - }, - videoDescription: { - marginTop: theme.spacing(-6.25), - marginLeft: theme.spacing(5.625), - width: '18.75rem', - marginBottom: theme.spacing(5), - fontSize: '1rem', - }, - backBtnDiv: { - marginLeft: theme.spacing(-1), - marginBottom: theme.spacing(2.5), - }, - submitBtnDiv: { - marginLeft: theme.spacing(2), - marginBottom: theme.spacing(2.5), - }, - enterInfoText: { - fontSize: '1.5rem', - }, - checkImg: { - marginBottom: theme.spacing(2.5), - }, - quickActionDiv: { - marginLeft: theme.spacing(5.625), - }, - rowDiv: { - display: 'flex', - flexDirection: 'row', - }, - - copyBtnImg: { - paddingRight: '0.625rem', - }, - done: { - color: theme.palette.text.secondary, - paddingRight: theme.spacing(0.625), - }, - mainPrivateRepo: { - border: `1px solid ${theme.palette.border.main}`, - padding: theme.spacing(2.5), - margin: theme.spacing(2.5, 3.75, 2.5, 2), - borderRadius: 4, - width: 'fit-content', - }, - privateRepoDiv: { - width: 'fit-content', - marginTop: theme.spacing(-6.25), - padding: theme.spacing(1.25), - backgroundColor: theme.palette.background.paper, - }, - inputFieldDiv: { - display: 'flex', - flexDirection: 'row', - marginTop: theme.spacing(2.5), - }, - privateRepoDetails: { - display: 'flex', - flexDirection: 'row', - marginTop: theme.spacing(2.5), - }, - formControl: { - marginLeft: theme.spacing(3.125), - }, - sshDiv: { - backgroundColor: theme.palette.disabledBackground, - padding: theme.spacing(2.5), - }, - sshAlert: { - color: theme.palette.error.main, - fontSize: '0.75rem', - }, - alertText: { - marginBottom: theme.spacing(2.5), - fontSize: '0.75rem', - color: theme.palette.common.black, - opacity: 0.4, - }, - sshDataDiv: { - display: 'flex', - flexDirection: 'row', - backgroundColor: theme.palette.cards.background, - padding: theme.spacing(2.5), - borderRadius: 4, - }, - sshText: { - wordBreak: 'break-all', - }, - copyBtn: { - margin: 'auto', - marginLeft: theme.spacing(2.5), - borderLeft: `1px solid ${theme.palette.secondary.main}`, - paddingLeft: theme.spacing(2.5), - }, - privateToggleDiv: { - display: 'flex', - flexDirection: 'column', - }, - sshRadioBtn: { - marginTop: theme.spacing(1.25), - }, - toggleActive: { - height: '2.25rem', - backgroundColor: theme.palette.text.secondary, - color: theme.palette.common.black, - width: '6.25rem', - textTransform: 'none', - '&:hover': { - backgroundColor: theme.palette.text.secondary, - }, - }, - toggleInactive: { - height: '2.25rem', - backgroundColor: theme.palette.primary.dark, - color: theme.palette.cards.background, - width: '6.25rem', - textTransform: 'none', - '&:hover': { - backgroundColor: theme.palette.primary.dark, - }, - }, - toggleFont: { - fontSize: '0.875rem', - }, - radio: { - color: theme.palette.primary.main, - '&$checked': { - color: theme.palette.primary.main, - }, - }, - checked: {}, -})); - -export default useStyles; diff --git a/litmus-portal/frontend/src/views/MyHub/MyHubExperiment/index.tsx b/litmus-portal/frontend/src/views/MyHub/MyHubExperiment/index.tsx index 0bbb5298f..579191e72 100644 --- a/litmus-portal/frontend/src/views/MyHub/MyHubExperiment/index.tsx +++ b/litmus-portal/frontend/src/views/MyHub/MyHubExperiment/index.tsx @@ -85,13 +85,15 @@ const MyHub = () => {
{/* Developer Guide Component */} -
- -
+ {paramData.chart.toLowerCase() !== 'predefined' && ( +
+ +
+ )} {/* Experiment Info */}
@@ -128,23 +130,34 @@ const MyHub = () => {
{/* Install Chaos Section */} -
- - - -
+ {paramData.chart.toLowerCase() !== 'predefined' ? ( +
+ + + +
+ ) : ( + <> + + + )} )} diff --git a/litmus-portal/frontend/src/views/MyHub/MyHubExperiment/styles.ts b/litmus-portal/frontend/src/views/MyHub/MyHubExperiment/styles.ts index 2cc99f313..12d1195a3 100644 --- a/litmus-portal/frontend/src/views/MyHub/MyHubExperiment/styles.ts +++ b/litmus-portal/frontend/src/views/MyHub/MyHubExperiment/styles.ts @@ -41,13 +41,14 @@ const useStyles = makeStyles((theme) => ({ }, expMain: { marginLeft: theme.spacing(1.25), + marginBottom: theme.spacing(2), }, linkText: { fontSize: '1rem', }, developerDiv: { marginLeft: theme.spacing(2.5), - marginTop: theme.spacing(6.25), + marginTop: theme.spacing(4.25), }, detailDiv: { backgroundColor: theme.palette.common.white, diff --git a/litmus-portal/graphql-server/graph/generated/generated.go b/litmus-portal/graphql-server/graph/generated/generated.go index a6df52a29..c0968ebc2 100644 --- a/litmus-portal/graphql-server/graph/generated/generated.go +++ b/litmus-portal/graphql-server/graph/generated/generated.go @@ -328,8 +328,8 @@ type ComplexityRoot struct { GetHubExperiment func(childComplexity int, experimentInput model.ExperimentInput) int GetHubStatus func(childComplexity int, projectID string) int GetImageRegistry func(childComplexity int, imageRegistryID string, projectID string) int - GetPredefinedExperimentList func(childComplexity int, hubName string, projectID string) int GetPredefinedExperimentYaml func(childComplexity int, experimentInput model.ExperimentInput) int + GetPredefinedWorkflowList func(childComplexity int, hubName string, projectID string) int GetProject func(childComplexity int, projectID string) int GetPromLabelNamesAndValues func(childComplexity int, series *model.PromSeriesInput) int GetPromQuery func(childComplexity int, query *model.PromInput) int @@ -618,7 +618,7 @@ type QueryResolver interface { GetHubExperiment(ctx context.Context, experimentInput model.ExperimentInput) (*model.Chart, error) GetHubStatus(ctx context.Context, projectID string) ([]*model.MyHubStatus, error) GetYAMLData(ctx context.Context, experimentInput model.ExperimentInput) (string, error) - GetPredefinedExperimentList(ctx context.Context, hubName string, projectID string) ([]string, error) + GetPredefinedWorkflowList(ctx context.Context, hubName string, projectID string) ([]string, error) GetPredefinedExperimentYaml(ctx context.Context, experimentInput model.ExperimentInput) (string, error) ListDataSource(ctx context.Context, projectID string) ([]*model.DSResponse, error) GetPromQuery(ctx context.Context, query *model.PromInput) (*model.PromResponse, error) @@ -2303,18 +2303,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.GetImageRegistry(childComplexity, args["image_registry_id"].(string), args["project_id"].(string)), true - case "Query.GetPredefinedExperimentList": - if e.complexity.Query.GetPredefinedExperimentList == nil { - break - } - - args, err := ec.field_Query_GetPredefinedExperimentList_args(context.TODO(), rawArgs) - if err != nil { - return 0, false - } - - return e.complexity.Query.GetPredefinedExperimentList(childComplexity, args["HubName"].(string), args["projectID"].(string)), true - case "Query.GetPredefinedExperimentYAML": if e.complexity.Query.GetPredefinedExperimentYaml == nil { break @@ -2327,6 +2315,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.GetPredefinedExperimentYaml(childComplexity, args["experimentInput"].(model.ExperimentInput)), true + case "Query.GetPredefinedWorkflowList": + if e.complexity.Query.GetPredefinedWorkflowList == nil { + break + } + + args, err := ec.field_Query_GetPredefinedWorkflowList_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.GetPredefinedWorkflowList(childComplexity, args["HubName"].(string), args["projectID"].(string)), true + case "Query.getProject": if e.complexity.Query.GetProject == nil { break @@ -4336,7 +4336,7 @@ type Query { getYAMLData(experimentInput: ExperimentInput!): String! - GetPredefinedExperimentList(HubName: String!, projectID: String!): [String!]! + GetPredefinedWorkflowList(HubName: String!, projectID: String!): [String!]! GetPredefinedExperimentYAML(experimentInput: ExperimentInput!): String! @@ -5186,7 +5186,21 @@ func (ec *executionContext) field_Query_GetImageRegistry_args(ctx context.Contex return args, nil } -func (ec *executionContext) field_Query_GetPredefinedExperimentList_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_GetPredefinedExperimentYAML_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 model.ExperimentInput + if tmp, ok := rawArgs["experimentInput"]; ok { + arg0, err = ec.unmarshalNExperimentInput2githubᚗcomᚋlitmuschaosᚋlitmusᚋlitmusᚑportalᚋgraphqlᚑserverᚋgraphᚋmodelᚐExperimentInput(ctx, tmp) + if err != nil { + return nil, err + } + } + args["experimentInput"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query_GetPredefinedWorkflowList_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} var arg0 string @@ -5208,20 +5222,6 @@ func (ec *executionContext) field_Query_GetPredefinedExperimentList_args(ctx con return args, nil } -func (ec *executionContext) field_Query_GetPredefinedExperimentYAML_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { - var err error - args := map[string]interface{}{} - var arg0 model.ExperimentInput - if tmp, ok := rawArgs["experimentInput"]; ok { - arg0, err = ec.unmarshalNExperimentInput2githubᚗcomᚋlitmuschaosᚋlitmusᚋlitmusᚑportalᚋgraphqlᚑserverᚋgraphᚋmodelᚐExperimentInput(ctx, tmp) - if err != nil { - return nil, err - } - } - args["experimentInput"] = arg0 - return args, nil -} - func (ec *executionContext) field_Query_GetPromLabelNamesAndValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -13863,7 +13863,7 @@ func (ec *executionContext) _Query_getYAMLData(ctx context.Context, field graphq return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) _Query_GetPredefinedExperimentList(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Query_GetPredefinedWorkflowList(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -13879,7 +13879,7 @@ func (ec *executionContext) _Query_GetPredefinedExperimentList(ctx context.Conte ctx = graphql.WithFieldContext(ctx, fc) rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Query_GetPredefinedExperimentList_args(ctx, rawArgs) + args, err := ec.field_Query_GetPredefinedWorkflowList_args(ctx, rawArgs) if err != nil { ec.Error(ctx, err) return graphql.Null @@ -13887,7 +13887,7 @@ func (ec *executionContext) _Query_GetPredefinedExperimentList(ctx context.Conte fc.Args = args resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().GetPredefinedExperimentList(rctx, args["HubName"].(string), args["projectID"].(string)) + return ec.resolvers.Query().GetPredefinedWorkflowList(rctx, args["HubName"].(string), args["projectID"].(string)) }) if err != nil { ec.Error(ctx, err) @@ -23539,7 +23539,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } return res }) - case "GetPredefinedExperimentList": + case "GetPredefinedWorkflowList": field := field out.Concurrently(i, func() (res graphql.Marshaler) { defer func() { @@ -23547,7 +23547,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Query_GetPredefinedExperimentList(ctx, field) + res = ec._Query_GetPredefinedWorkflowList(ctx, field) if res == graphql.Null { atomic.AddUint32(&invalids, 1) } diff --git a/litmus-portal/graphql-server/graph/schema.graphqls b/litmus-portal/graphql-server/graph/schema.graphqls index 32fb49293..5afc620f9 100644 --- a/litmus-portal/graphql-server/graph/schema.graphqls +++ b/litmus-portal/graphql-server/graph/schema.graphqls @@ -308,7 +308,7 @@ type Query { getYAMLData(experimentInput: ExperimentInput!): String! - GetPredefinedExperimentList(HubName: String!, projectID: String!): [String!]! + GetPredefinedWorkflowList(HubName: String!, projectID: String!): [String!]! GetPredefinedExperimentYAML(experimentInput: ExperimentInput!): String! diff --git a/litmus-portal/graphql-server/graph/schema.resolvers.go b/litmus-portal/graphql-server/graph/schema.resolvers.go index 05367181d..0c6e63255 100644 --- a/litmus-portal/graphql-server/graph/schema.resolvers.go +++ b/litmus-portal/graphql-server/graph/schema.resolvers.go @@ -381,8 +381,8 @@ func (r *queryResolver) GetYAMLData(ctx context.Context, experimentInput model.E return myhub.GetYAMLData(ctx, experimentInput) } -func (r *queryResolver) GetPredefinedExperimentList(ctx context.Context, hubName string, projectID string) ([]string, error) { - return myhub.GetPredefinedExperiementList(hubName, projectID) +func (r *queryResolver) GetPredefinedWorkflowList(ctx context.Context, hubName string, projectID string) ([]string, error) { + return myhub.GetPredefinedWorkflowList(hubName, projectID) } func (r *queryResolver) GetPredefinedExperimentYaml(ctx context.Context, experimentInput model.ExperimentInput) (string, error) { diff --git a/litmus-portal/graphql-server/pkg/myhub/handler/handler.go b/litmus-portal/graphql-server/pkg/myhub/handler/handler.go index 173c1b68a..cebca9a8a 100644 --- a/litmus-portal/graphql-server/pkg/myhub/handler/handler.go +++ b/litmus-portal/graphql-server/pkg/myhub/handler/handler.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "os" "gopkg.in/yaml.v2" @@ -104,6 +105,15 @@ func GetExperimentChartsVersionYamlPath(ctx context.Context, experimentInput mod return ExperimentPath } +// GetPredefinedWorkflowCSVPath is used to construct path for given chartsversion.yaml for pre-defined workflow. +func GetPreDefinedWorkflowCSVPath(ctx context.Context, experimentInput model.ExperimentInput) string { + ProjectID := experimentInput.ProjectID + HubName := experimentInput.HubName + experimentName := experimentInput.ExperimentName + ExperimentPath := defaultPath + ProjectID + "/" + HubName + "/workflows/" + experimentName + "/" + experimentName + ".chartserviceversion.yaml" + return ExperimentPath +} + // GetExperimentYAMLPath is used to construct path for given experiment/engine. func GetExperimentYAMLPath(ctx context.Context, experimentInput model.ExperimentInput) string { ProjectID := experimentInput.ProjectID @@ -179,7 +189,7 @@ func ReadExperimentYAMLFile(path string) (string, error) { } // GetPredefinedExperimentFileList reads the workflow directory for all the predefined experiments -func GetPredefinedExperimentFileList(hubname string, projectID string) ([]string, error) { +func GetPredefinedWorkflowFileList(hubname string, projectID string) ([]string, error) { ExperimentsPath := defaultPath + projectID + "/" + hubname + "/workflows" var expNames []string files, err := ioutil.ReadDir(ExperimentsPath) @@ -187,7 +197,20 @@ func GetPredefinedExperimentFileList(hubname string, projectID string) ([]string return nil, err } for _, file := range files { - expNames = append(expNames, file.Name()) + isExist, _ := IsFileExisting(ExperimentsPath + "/" + file.Name() + "/" + file.Name() + ".chartserviceversion.yaml") + if isExist { + expNames = append(expNames, file.Name()) + } } return expNames, nil } + +func IsFileExisting(path string) (bool, error) { + _, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + } + return true, nil +} diff --git a/litmus-portal/graphql-server/pkg/myhub/myhub.go b/litmus-portal/graphql-server/pkg/myhub/myhub.go index 9b30cf34e..e8dafba45 100644 --- a/litmus-portal/graphql-server/pkg/myhub/myhub.go +++ b/litmus-portal/graphql-server/pkg/myhub/myhub.go @@ -9,6 +9,7 @@ import ( "net/http" "os" "strconv" + "strings" "time" "github.com/google/uuid" @@ -231,13 +232,16 @@ func GetCharts(ctx context.Context, hubName string, projectID string) ([]*model. // GetExperiment is used for getting details of a given experiment using chartserviceversion.yaml. func GetExperiment(ctx context.Context, experimentInput model.ExperimentInput) (*model.Chart, error) { - - ExperimentPath := handler.GetExperimentChartsVersionYamlPath(ctx, experimentInput) + var ExperimentPath string + if strings.ToLower(experimentInput.ChartName) == "predefined" { + ExperimentPath = handler.GetPreDefinedWorkflowCSVPath(ctx, experimentInput) + } else { + ExperimentPath = handler.GetExperimentChartsVersionYamlPath(ctx, experimentInput) + } ExperimentData, err := handler.GetExperimentData(ExperimentPath) if err != nil { return nil, err } - return ExperimentData, nil } @@ -394,11 +398,23 @@ func DeleteMyHub(ctx context.Context, hubID string) (bool, error) { // GetIconHandler ... var GetIconHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - img, err := os.Open("/tmp/version/" + vars["ProjectID"] + "/" + vars["HubName"] + "/charts/" + vars["ChartName"] + "/icons/" + vars["IconName"]) - responseStatusCode := 200 - if err != nil { - responseStatusCode = 500 - fmt.Fprint(w, "icon cannot be fetched, err : "+err.Error()) + var img *os.File + var err error + var responseStatusCode int + if strings.ToLower(vars["ChartName"]) == "predefined" { + img, err = os.Open("/tmp/version/" + vars["ProjectID"] + "/" + vars["HubName"] + "/workflows/icons/" + vars["IconName"]) + responseStatusCode = 200 + if err != nil { + responseStatusCode = 500 + fmt.Fprint(w, "icon cannot be fetched, err : "+err.Error()) + } + } else { + img, err = os.Open("/tmp/version/" + vars["ProjectID"] + "/" + vars["HubName"] + "/charts/" + vars["ChartName"] + "/icons/" + vars["IconName"]) + responseStatusCode = 200 + if err != nil { + responseStatusCode = 500 + fmt.Fprint(w, "icon cannot be fetched, err : "+err.Error()) + } } defer img.Close() w.Header().Set("Access-Control-Allow-Origin", "*") @@ -436,8 +452,8 @@ func RecurringHubSync() { } } -func GetPredefinedExperiementList(hubname string, projectID string) ([]string, error) { - expList, err := handler.GetPredefinedExperimentFileList(hubname, projectID) +func GetPredefinedWorkflowList(hubname string, projectID string) ([]string, error) { + expList, err := handler.GetPredefinedWorkflowFileList(hubname, projectID) if err != nil { return nil, err }