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 <amit@chaosnative.com>
This commit is contained in:
Amit Kumar Das 2021-05-27 11:21:20 +05:30 committed by GitHub
parent 26d5bbb3c3
commit fcdfecf8a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 338 additions and 854 deletions

View File

@ -1,39 +1,7 @@
<svg width="59" height="63" viewBox="0 0 59 63" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.8001 41.3007L42.1001 37.8008L56.8002 59.5007H2.90015L14.8001 41.3007Z" fill="url(#paint0_linear)"/>
<path d="M38.9014 27.3728V3.69141H42.6619V0H16.3381V3.69141H20.0987V27.3728L1.74897 54.3915C0.592729 56.0939 0.485554 58.2719 1.46906 60.0758C2.45244 61.8795 4.35803 63 6.44225 63H52.5578C54.642 63 56.5476 61.8795 57.5311 60.0757C58.5146 58.2718 58.4073 56.0937 57.2512 54.3914L38.9014 27.3728ZM23.8592 28.4905V3.69141H35.1408V28.4905L39.9736 35.6064C39.0321 35.4304 38.0304 35.3145 37.0211 35.3145C32.8644 35.3145 28.8289 37.2716 28.6592 37.3549C27.7235 37.8142 24.6998 39.0059 21.9789 39.0059C20.2966 39.0059 18.5773 38.5684 17.3032 38.1437L23.8592 28.4905ZM54.2154 58.3339C54.0557 58.6269 53.5742 59.3086 52.5576 59.3086H6.44225C5.42578 59.3086 4.9443 58.6269 4.78448 58.3339C4.62478 58.0408 4.31391 57.2695 4.87774 56.4391L15.1552 41.3065C16.7371 41.9044 19.3325 42.6973 21.9789 42.6973C26.1356 42.6973 30.1711 40.7401 30.3409 40.6568C31.2764 40.1976 34.3001 39.0059 37.0211 39.0059C39.5961 39.0059 42.2589 40.0301 43.2734 40.4648L54.1223 56.439C54.686 57.2695 54.3752 58.0407 54.2154 58.3339Z" fill="white"/>
<path d="M38.9014 27.3728V3.69141H42.6619V0H16.3381V3.69141H20.0987V27.3728L1.74897 54.3915C0.592729 56.0939 0.485554 58.2719 1.46906 60.0758C2.45244 61.8795 4.35803 63 6.44225 63H52.5578C54.642 63 56.5476 61.8795 57.5311 60.0757C58.5146 58.2718 58.4073 56.0937 57.2512 54.3914L38.9014 27.3728ZM23.8592 28.4905V3.69141H35.1408V28.4905L39.9736 35.6064C39.0321 35.4304 38.0304 35.3145 37.0211 35.3145C32.8644 35.3145 28.8289 37.2716 28.6592 37.3549C27.7235 37.8142 24.6998 39.0059 21.9789 39.0059C20.2966 39.0059 18.5773 38.5684 17.3032 38.1437L23.8592 28.4905ZM54.2154 58.3339C54.0557 58.6269 53.5742 59.3086 52.5576 59.3086H6.44225C5.42578 59.3086 4.9443 58.6269 4.78448 58.3339C4.62478 58.0408 4.31391 57.2695 4.87774 56.4391L15.1552 41.3065C16.7371 41.9044 19.3325 42.6973 21.9789 42.6973C26.1356 42.6973 30.1711 40.7401 30.3409 40.6568C31.2764 40.1976 34.3001 39.0059 37.0211 39.0059C39.5961 39.0059 42.2589 40.0301 43.2734 40.4648L54.1223 56.439C54.686 57.2695 54.3752 58.0407 54.2154 58.3339Z" fill="url(#paint1_linear)"/>
<path d="M35.1409 44.543H38.9015V48.2344H35.1409V44.543Z" fill="white"/>
<path d="M35.1409 44.543H38.9015V48.2344H35.1409V44.543Z" fill="url(#paint2_linear)"/>
<path d="M27.6199 51.9258H31.3804V55.6172H27.6199V51.9258Z" fill="white"/>
<path d="M27.6199 51.9258H31.3804V55.6172H27.6199V51.9258Z" fill="url(#paint3_linear)"/>
<path d="M20.0988 48.2344H23.8593V51.9258H20.0988V48.2344Z" fill="white"/>
<path d="M20.0988 48.2344H23.8593V51.9258H20.0988V48.2344Z" fill="url(#paint4_linear)"/>
<path d="M27.6199 29.7773H31.3804V33.4687H27.6199V29.7773Z" fill="white"/>
<path d="M27.6199 29.7773H31.3804V33.4687H27.6199V29.7773Z" fill="url(#paint5_linear)"/>
<defs>
<linearGradient id="paint0_linear" x1="28.0002" y1="45.501" x2="29.8501" y2="59.5007" gradientUnits="userSpaceOnUse">
<stop stop-color="#2DA660"/>
<stop offset="1" stop-color="#52F995"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="29.5" y1="0" x2="29.5" y2="63" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint2_linear" x1="29.5" y1="0" x2="29.5" y2="63" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint3_linear" x1="29.5" y1="0" x2="29.5" y2="63" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint4_linear" x1="29.5" y1="0" x2="29.5" y2="63" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint5_linear" x1="29.5" y1="0" x2="29.5" y2="63" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
</defs>
<svg width="43" height="47" viewBox="0 0 43 47" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28.5428 20.4209V2.75391H31.3599V0H11.64V2.75391H14.4571V20.4209L0.710864 40.5778C-0.155309 41.8478 -0.235597 43.4727 0.501176 44.8184C1.23786 46.1641 2.66539 47 4.22674 47H38.7732C40.3346 47 41.7621 46.1641 42.4989 44.8184C43.2356 43.4726 43.1552 41.8477 42.2892 40.5777L28.5428 20.4209ZM17.2743 21.2548V2.75391H25.7257V21.2548L29.3461 26.5635C28.6407 26.4322 27.8903 26.3457 27.1342 26.3457C24.0204 26.3457 20.9972 27.8058 20.8701 27.868C20.1692 28.2106 17.904 29.0996 15.8657 29.0996C14.6054 29.0996 13.3174 28.7733 12.363 28.4564L17.2743 21.2548ZM40.015 43.519C39.8954 43.7375 39.5347 44.2461 38.7731 44.2461H4.22674C3.46527 44.2461 3.10458 43.7375 2.98485 43.519C2.86522 43.3003 2.63234 42.7248 3.05472 42.1054L10.7538 30.8159C11.9389 31.262 13.8832 31.8535 15.8657 31.8535C18.9796 31.8535 22.0027 30.3934 22.1299 30.3312C22.8307 29.9887 25.0958 29.0996 27.1342 29.0996C29.0632 29.0996 31.058 29.8637 31.818 30.188L39.9452 42.1053C40.3675 42.7248 40.1347 43.3002 40.015 43.519Z" fill="#5B44BA"/>
<path d="M25.7258 33.2305H28.5429V35.9844H25.7258V33.2305Z" fill="#5B44BA"/>
<path d="M20.0915 38.7383H22.9086V41.4922H20.0915V38.7383Z" fill="#5B44BA"/>
<path d="M14.4572 35.9844H17.2744V38.7383H14.4572V35.9844Z" fill="#5B44BA"/>
<path d="M20.0915 22.2148H22.9086V24.9688H20.0915V22.2148Z" fill="#5B44BA"/>
</svg>

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,16 @@
<svg width="46" height="50" viewBox="0 0 46 50" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28.5428 20.4209V2.75391H31.3599V0H11.64V2.75391H14.4571V20.4209L0.710864 40.5778C-0.155309 41.8478 -0.235597 43.4727 0.501176 44.8184C1.23786 46.1641 2.66539 47 4.22674 47H38.7732C40.3346 47 41.7621 46.1641 42.4989 44.8184C43.2356 43.4726 43.1552 41.8477 42.2892 40.5777L28.5428 20.4209ZM17.2743 21.2548V2.75391H25.7257V21.2548L29.3461 26.5635C28.6407 26.4322 27.8903 26.3457 27.1342 26.3457C24.0204 26.3457 20.9972 27.8058 20.8701 27.868C20.1692 28.2106 17.904 29.0996 15.8657 29.0996C14.6054 29.0996 13.3174 28.7733 12.363 28.4564L17.2743 21.2548ZM40.015 43.519C39.8954 43.7375 39.5347 44.2461 38.7731 44.2461H4.22674C3.46527 44.2461 3.10458 43.7375 2.98485 43.519C2.86522 43.3003 2.63234 42.7248 3.05472 42.1054L10.7538 30.8159C11.9389 31.262 13.8832 31.8535 15.8657 31.8535C18.9796 31.8535 22.0027 30.3934 22.1299 30.3312C22.8307 29.9887 25.0958 29.0996 27.1342 29.0996C29.0632 29.0996 31.058 29.8637 31.818 30.188L39.9452 42.1053C40.3675 42.7248 40.1347 43.3002 40.015 43.519Z" fill="#CA2C2C"/>
<path d="M25.7258 33.2305H28.5429V35.9844H25.7258V33.2305Z" fill="#CA2C2C"/>
<path d="M20.0915 38.7383H22.9086V41.4922H20.0915V38.7383Z" fill="#CA2C2C"/>
<path d="M14.4572 35.9844H17.2744V38.7383H14.4572V35.9844Z" fill="#CA2C2C"/>
<path d="M20.0915 22.2148H22.9086V24.9688H20.0915V22.2148Z" fill="#CA2C2C"/>
<rect x="30.5" y="34.5" width="15.0004" height="15.0004" rx="7.50021" fill="#CA2C2C" stroke="white"/>
<g clip-path="url(#clip0)">
<path d="M38.1635 43.2002L40.1063 45.143C40.321 45.3577 40.669 45.3577 40.8833 45.143L41.1423 44.884C41.357 44.6693 41.357 44.3213 41.1423 44.1069L39.1995 42.1642L41.1427 40.2213C41.3574 40.0067 41.3574 39.6587 41.1427 39.4443L40.8837 39.1849C40.669 38.9703 40.321 38.9703 40.1066 39.1849L38.1635 41.1281L36.2207 39.1853C36.006 38.9706 35.6579 38.9706 35.4436 39.1853L35.1846 39.4443C34.9699 39.659 34.9699 40.007 35.1846 40.2213L37.1274 42.1642L35.1846 44.1069C34.9699 44.3217 34.9699 44.6697 35.1846 44.884L35.4436 45.143C35.6583 45.3577 36.0064 45.3577 36.2207 45.143L38.1635 43.2002Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="7.67066" height="7.67066" fill="white" transform="translate(41.8545 38.1826) rotate(90)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -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

View File

@ -9,17 +9,19 @@ interface InstallProps {
title: string;
description: string;
yamlLink: string;
isPredefined?: boolean;
}
const InstallChaos: React.FC<InstallProps> = ({
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<InstallProps> = ({
<div className={classes.description}>{description}</div>
<div className={classes.linkBox}>
<Typography variant="subtitle1" className={classes.yamlLink}>
kubectl apply -f {yamlLink}
{isPredefined ? yamlLink : `kubectl apply -f ${yamlLink}`}
</Typography>
<div className={classes.buttonBox}>

View File

@ -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 = () => {
<Route exact path="/targets" component={Targets} />
<Route exact path="/target-connect" component={ConnectTargets} />
<Route exact path="/myhub" component={MyHub} />
<Route exact path="/myhub/edit/:hubname" component={MyHubEdit} />
<Route exact path="/myhub/:hubname" component={ChaosChart} />
<Route
exact

View File

@ -459,6 +459,12 @@ export const GET_TEMPLATE_BY_ID = gql`
}
`;
export const GET_PREDEFINED_WORKFLOW_LIST = gql`
query GetPredefinedWorkflowList($hubname: String!, $projectid: String!) {
GetPredefinedWorkflowList(HubName: $hubname, projectID: $projectid)
}
`;
export const GET_PREDEFINED_EXPERIMENT_YAML = gql`
query GetPredefinedExperimentYAML($experimentInput: ExperimentInput!) {
GetPredefinedExperimentYAML(experimentInput: $experimentInput)

View File

@ -16,6 +16,7 @@ interface ChartCardProps {
setSearch: React.Dispatch<React.SetStateAction<string>>;
projectID: string;
userRole: string;
isPredefined?: boolean;
}
const ChartCard: React.FC<ChartCardProps> = ({
@ -24,6 +25,7 @@ const ChartCard: React.FC<ChartCardProps> = ({
setSearch,
projectID,
userRole,
isPredefined,
}) => {
const classes = useStyles();
const experimentDefaultImagePath = `${config.grahqlEndpoint}/icon`;
@ -31,7 +33,6 @@ const ChartCard: React.FC<ChartCardProps> = ({
const [imageURL, setImageURL] = useState(
`${experimentDefaultImagePath}/${projectID}/${UserHub?.HubName}/${expName.ChaosName}/${expName.ExperimentName}.png`
);
return (
<div>
<Card
@ -61,7 +62,7 @@ const ChartCard: React.FC<ChartCardProps> = ({
}}
className={classes.categoryName}
>
{expName.ChaosName}/
{!isPredefined && `${expName.ChaosName}/`}
</Link>
<Typography className={classes.expName} variant="h6" align="center">
{expName.ExperimentName}

View File

@ -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<HTMLTextAreaElement | HTMLInputElement>
) => {
setSearch(event.target.value as string);
};
const handlePreDefinedSearch = (
event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
) => {
setSearchPredefined(event.target.value as string);
};
const [totalExp, setTotalExperiment] = useState<ChartName[]>([]);
const exp: ChartName[] = [];
@ -69,6 +99,15 @@ const MyHub: React.FC = () => {
return 'Date not available';
};
const [expanded, setExpanded] = React.useState<string | false>('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 ? (
<Backdrop open className={classes.backdrop}>
<Loader />
<Center>
@ -95,45 +134,131 @@ const MyHub: React.FC = () => {
</Backdrop>
) : (
<Scaffold>
<BackButton />
<div className={classes.header}>
<Typography variant="h3" gutterBottom>
{UserHub?.HubName}
</Typography>
<Typography variant="h4">
<Typography variant="h5">
{t('myhub.myhubChart.repoLink')}
<strong>
{UserHub?.RepoURL}/{UserHub?.RepoBranch}
</strong>
</Typography>
<Typography className={classes.lastSyncText}>
Last synced at: {formatDate(UserHub ? UserHub.LastSyncedAt : '')}
{t('myhub.myhubChart.lastSynced')}{' '}
{formatDate(UserHub ? UserHub.LastSyncedAt : '')}
</Typography>
{/* </div> */}
</div>
<div className={classes.mainDiv}>
<HeaderSection searchValue={search} changeSearch={changeSearch} />
<div className={classes.chartsGroup}>
{totalExp &&
totalExp.length > 0 &&
totalExp
.filter(
(data) =>
data.ChaosName.toLowerCase().includes(search.trim()) ||
data.ExperimentName.toLowerCase().includes(search.trim())
)
.map((expName: ChartName) => {
return (
<ChartCard
key={`${expName.ChaosName}-${expName.ExperimentName}`}
expName={expName}
UserHub={UserHub}
setSearch={setSearch}
projectID={projectID}
userRole={getProjectRole()}
<Accordion
square
classes={{
root: classes.MuiAccordionroot,
}}
expanded={expanded === 'panel1'}
onChange={handleChange('panel1')}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1d-content"
id="panel1d-header"
>
<Typography variant="h4">
<strong>{t('myhub.myhubChart.preDefined')}</strong>
</Typography>
</AccordionSummary>
<AccordionDetails>
<div className={classes.mainDiv}>
<HeaderSection
searchValue={searchPredefined}
changeSearch={handlePreDefinedSearch}
/>
<div className={classes.chartsGroup}>
{predefinedData?.GetPredefinedWorkflowList.length > 0 ? (
predefinedData?.GetPredefinedWorkflowList.filter(
(data: string) =>
data.toLowerCase().includes(searchPredefined.trim())
).map((expName: string) => {
return (
<ChartCard
key={expName}
expName={{
ChaosName: 'predefined',
ExperimentName: expName,
}}
UserHub={UserHub}
setSearch={setSearchPredefined}
projectID={projectID}
userRole={getProjectRole()}
isPredefined
/>
);
})
) : (
<>
<img
src="/icons/no-experiment-found.svg"
alt="no experiment"
width="80px"
height="80px"
/>
);
})}
</div>
</div>
<Typography variant="h5" className={classes.noExp}>
{t('myhub.myhubChart.noExp')}
</Typography>
</>
)}
</div>
</div>
</AccordionDetails>
</Accordion>
<Accordion
square
classes={{
root: classes.MuiAccordionroot,
}}
expanded={expanded === 'panel2'}
onChange={handleChange('panel2')}
className={classes.chartAccordion}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel2d-content"
id="panel2d-header"
>
<Typography variant="h4">
<strong>{t('myhub.myhubChart.chaosCharts')}</strong>
</Typography>
</AccordionSummary>
<AccordionDetails>
<div className={classes.mainDiv}>
<HeaderSection searchValue={search} changeSearch={changeSearch} />
<div className={classes.chartsGroup}>
{totalExp &&
totalExp.length > 0 &&
totalExp
.filter(
(data) =>
data.ChaosName.toLowerCase().includes(search.trim()) ||
data.ExperimentName.toLowerCase().includes(search.trim())
)
.map((expName: ChartName) => {
return (
<ChartCard
key={`${expName.ChaosName}-${expName.ExperimentName}`}
expName={expName}
UserHub={UserHub}
setSearch={setSearch}
projectID={projectID}
userRole={getProjectRole()}
isPredefined={false}
/>
);
})}
</div>
</div>
</AccordionDetails>
</Accordion>
</Scaffold>
);
};

View File

@ -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;

View File

@ -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<HubStatus>(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<GitHub>({
HubName: '',
GitURL: '',
GitBranch: '',
});
const [error, setError] = useState('');
const [isOpen, setIsOpen] = useState(false);
const [cloningRepo, setCloningRepo] = useState(false);
const [isToggled, setIsToggled] = React.useState<MyHubToggleProps>({
isPublicToggled: true,
isPrivateToggled: false,
});
const [privateHub, setPrivateHub] = useState('token');
const [accessToken, setAccessToken] = useState('');
const [sshKey, setSshKey] = useState<SSHKey>({
privateKey: '',
publicKey: '',
});
const [copying, setCopying] = useState(false);
const [updateMyHub] = useMutation<MyHubData, CreateMyHub>(UPDATE_MY_HUB, {
onCompleted: () => {
setCloningRepo(false);
},
onError: (error) => {
setCloningRepo(false);
setError(error.message);
},
});
// Mutation to generate SSH key
const [generateSSHKey, { loading: sshLoading }] = useMutation<SSHKeys>(
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<HTMLTextAreaElement | HTMLInputElement>
) => {
setGitHub({
HubName: gitHub.HubName,
GitURL: event.target.value,
GitBranch: gitHub.GitBranch,
});
};
const handleGitBranch = (
event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
) => {
setGitHub({
HubName: gitHub.HubName,
GitURL: gitHub.GitURL,
GitBranch: event.target.value,
});
};
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
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 (
<Scaffold>
<div className={classes.header}>
<div className={classes.backBtnDiv}>
<BackButton />
</div>
<Typography variant="h3" gutterBottom>
{t('myhub.editPage.edit')}
</Typography>
</div>
<div className={classes.mainDiv}>
{loading ? (
<Loader />
) : (
<div className={classes.detailsDiv}>
<Typography variant="h4" gutterBottom />
<Typography className={classes.enterInfoText}>
<strong>{t('myhub.connectHubPage.enterInfo')}</strong>
</Typography>
<Typography className={classes.connectText}>
{t('myhub.editPage.click')}
</Typography>
<form id="login-form" autoComplete="on" onSubmit={handleSubmit}>
<div className={classes.inputDiv}>
<div className={classes.inputField}>
<InputField
label="Hub Name"
value={gitHub.HubName}
helperText={
validateStartEmptySpacing(gitHub.HubName)
? t('myhub.validationEmptySpace')
: ''
}
variant={
validateStartEmptySpacing(gitHub.HubName)
? 'error'
: 'primary'
}
required
onChange={(e) =>
setGitHub({
HubName: e.target.value,
GitURL: gitHub.GitURL,
GitBranch: gitHub.GitBranch,
})
}
/>
</div>
<div>
<div className={classes.mainPrivateRepo}>
<div className={classes.privateRepoDiv}>
<GitHubToggleButton
isToggled={isToggled}
setIsToggled={setIsToggled}
/>
</div>
{/* If Public Repo is clicked */}
{isToggled.isPublicToggled ? (
<div className={classes.inputFieldDiv}>
<GithubInputFields
gitURL={gitHub.GitURL}
gitBranch={gitHub.GitBranch}
setGitURL={handleGitURL}
setGitBranch={handleGitBranch}
/>
</div>
) : null}
{/* If Private Repo is Clicked */}
{isToggled.isPrivateToggled ? (
<div className={classes.privateToggleDiv}>
<div className={classes.privateRepoDetails}>
<GithubInputFields
gitURL={gitHub.GitURL}
gitBranch={gitHub.GitBranch}
setGitURL={handleGitURL}
setGitBranch={handleGitBranch}
/>
</div>
<FormControl
component="fieldset"
className={classes.formControl}
>
<RadioGroup
aria-label="privateHub"
name="privateHub"
value={privateHub}
onChange={(e) => {
if (e.target.value === 'ssh') {
generateSSHKey();
}
if (e.target.value === 'token') {
setSshKey({
privateKey: '',
publicKey: '',
});
}
setPrivateHub(e.target.value);
}}
>
<FormControlLabel
value="token"
control={
<Radio
classes={{
root: classes.radio,
checked: classes.checked,
}}
/>
}
label={
<Typography>
{t('myhub.connectHubPage.accessToken')}
</Typography>
}
/>
{privateHub === 'token' ? (
<InputField
label="Access Token"
value={accessToken}
helperText={
validateStartEmptySpacing(accessToken)
? t('myhub.validationEmptySpace')
: ''
}
variant={
validateStartEmptySpacing(accessToken)
? 'error'
: 'primary'
}
onChange={(e) => setAccessToken(e.target.value)}
/>
) : null}
<FormControlLabel
className={classes.sshRadioBtn}
value="ssh"
control={
<Radio
classes={{
root: classes.radio,
checked: classes.checked,
}}
/>
}
label={
<Typography>
{t('myhub.connectHubPage.ssh')}
</Typography>
}
/>
{privateHub === 'ssh' ? (
<div className={classes.sshDiv}>
<Typography className={classes.sshAlert}>
{t('myhub.connectHubPage.sshAlert')}
</Typography>
<Typography className={classes.alertText}>
{t('myhub.connectHubPage.sshText')}
</Typography>
<div className={classes.sshDataDiv}>
{sshLoading ? (
<Loader />
) : (
<>
<Typography className={classes.sshText}>
{sshKey.publicKey}
</Typography>
<div className={classes.copyBtn}>
<ButtonOutlined
onClick={() =>
copyTextToClipboard(
sshKey.publicKey
)
}
>
{!copying ? (
<div className={classes.rowDiv}>
<img
src="/icons/copy.svg"
className={classes.copyBtnImg}
alt="copy"
/>
<Typography>
{t('myhub.installChaos.copy')}
</Typography>
</div>
) : (
<div className={classes.rowDiv}>
<Done className={classes.done} />
<Typography>
{t('myhub.installChaos.copied')}
</Typography>
</div>
)}
</ButtonOutlined>
</div>
</>
)}
</div>
</div>
) : null}
</RadioGroup>
</FormControl>
</div>
) : null}
</div>
</div>
<div className={classes.submitBtnDiv}>
<ButtonFilled
variant="success"
type="submit"
disabled={
!isValidWebUrl(gitHub.GitURL) ||
validateStartEmptySpacing(gitHub.GitBranch)
}
>
{t('myhub.editPage.submit')}
</ButtonFilled>
</div>
<Modal
open={isOpen}
onClose={handleClose}
modalActions={
<ButtonOutlined onClick={handleClose}>
&#x2715;
</ButtonOutlined>
}
>
<div className={classes.modalDiv}>
{cloningRepo ? (
<div>
<Loader />
<Typography className={classes.modalDesc}>
{t('myhub.editPage.wait')}
</Typography>
</div>
) : (
<div>
{error.length ? (
<div>
<Typography
gutterBottom
className={classes.modalHeading}
>
<strong>{t('myhub.editPage.error')}</strong>{' '}
</Typography>
<Typography className={classes.modalDesc}>
Error: {error}
</Typography>
</div>
) : (
<>
<img
src="/icons/checkmark.svg"
alt="checkmark"
className={classes.checkImg}
/>
<Typography
gutterBottom
className={classes.modalHeading}
>
{t('myhub.editPage.success')}
</Typography>
<Typography className={classes.modalDesc}>
{t('myhub.editPage.desc')}
</Typography>
<ButtonFilled
variant="success"
onClick={handleClose}
>
{t('myhub.connectHubPage.myHub')}
</ButtonFilled>
</>
)}
</div>
)}
</div>
</Modal>
</div>
</form>
</div>
)}
<div className={classes.root}>
<VideoCarousel />
<Typography className={classes.videoDescription}>
{t('myhub.connectHubPage.videoDesc')}
</Typography>
<div className={classes.quickActionDiv}>
<LocalQuickActionCard variant="homePage" />
</div>
</div>
</div>
</Scaffold>
);
};
export default MyHub;

View File

@ -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;

View File

@ -85,13 +85,15 @@ const MyHub = () => {
</div>
</div>
{/* Developer Guide Component */}
<div className={classes.developerDiv}>
<DeveloperGuide
expAvailable
header={t('myhub.experimentPage.congrats')}
description=""
/>
</div>
{paramData.chart.toLowerCase() !== 'predefined' && (
<div className={classes.developerDiv}>
<DeveloperGuide
expAvailable
header={t('myhub.experimentPage.congrats')}
description=""
/>
</div>
)}
{/* Experiment Info */}
<div className={classes.detailDiv}>
<div className={classes.expInfo}>
@ -128,23 +130,34 @@ const MyHub = () => {
</div>
</div>
{/* Install Chaos Section */}
<div className={classes.installLinks}>
<InstallChaos
title={t('myhub.experimentPage.installExp')}
description={t('myhub.experimentPage.installExpDesc')}
yamlLink={`${UserHub?.RepoURL}/raw/${UserHub?.RepoBranch}/charts/${paramData.chart}/${paramData.experiment}/experiment.yaml`}
/>
<InstallChaos
title={t('myhub.experimentPage.installRBAC')}
description={t('myhub.experimentPage.installRBACDesc')}
yamlLink={`${UserHub?.RepoURL}/raw/${UserHub?.RepoBranch}/charts/${paramData.chart}/${paramData.experiment}/rbac.yaml`}
/>
<InstallChaos
title={t('myhub.experimentPage.installEngine')}
description={t('myhub.experimentPage.installEngineDesc')}
yamlLink={`${UserHub?.RepoURL}/raw/${UserHub?.RepoBranch}/charts/${paramData.chart}/${paramData.experiment}/engine.yaml`}
/>
</div>
{paramData.chart.toLowerCase() !== 'predefined' ? (
<div className={classes.installLinks}>
<InstallChaos
title={t('myhub.experimentPage.installExp')}
description={t('myhub.experimentPage.installExpDesc')}
yamlLink={`${UserHub?.RepoURL}/raw/${UserHub?.RepoBranch}/charts/${paramData.chart}/${paramData.experiment}/experiment.yaml`}
/>
<InstallChaos
title={t('myhub.experimentPage.installRBAC')}
description={t('myhub.experimentPage.installRBACDesc')}
yamlLink={`${UserHub?.RepoURL}/raw/${UserHub?.RepoBranch}/charts/${paramData.chart}/${paramData.experiment}/rbac.yaml`}
/>
<InstallChaos
title={t('myhub.experimentPage.installEngine')}
description={t('myhub.experimentPage.installEngineDesc')}
yamlLink={`${UserHub?.RepoURL}/raw/${UserHub?.RepoBranch}/charts/${paramData.chart}/${paramData.experiment}/engine.yaml`}
/>
</div>
) : (
<>
<InstallChaos
title={t('myhub.experimentPage.checkPreDefined')}
description={t('myhub.experimentPage.checkPreDefinedDesc')}
yamlLink={`${UserHub?.RepoURL}/raw/${UserHub?.RepoBranch}/workflows/${paramData.experiment}`}
isPredefined
/>
</>
)}
</div>
</div>
)}

View File

@ -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,

View File

@ -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)
}

View File

@ -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!

View File

@ -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) {

View File

@ -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
}

View File

@ -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
}