chore(litmus-portal): Added validation in probes modal and minor fix (#2842)

* Added validation in probes modal and minor fix
* Minor change in directory structure and fixed template graph not rendering issue
* Minor regex change for validating ssh links

Signed-off-by: Amit Kumar Das <amit@chaosnative.com>
This commit is contained in:
Amit Kumar Das 2021-05-31 09:34:15 +05:30 committed by GitHub
parent cd72a7a40a
commit c638304960
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 357 additions and 298 deletions

View File

@ -855,6 +855,7 @@ createWorkflow:
addProbe: addProbe:
heading: Add heading: Add
headingStrong: Probe headingStrong: Probe
validate: Probe name already exists
labels: labels:
probeName: Probe Name probeName: Probe Name
probeType: Probe Type probeType: Probe Type

View File

@ -84,6 +84,7 @@ export interface ListManifestTemplateArray {
project_name: string; project_name: string;
template_description: string; template_description: string;
template_name: string; template_name: string;
isCustomWorkflow: boolean;
} }
export interface ListManifestTemplate { export interface ListManifestTemplate {

View File

@ -41,6 +41,18 @@ export const validateWorkflowName = (value: string) => {
return false; return false;
}; };
export const validateProbeName = (allProbe: any, probeName: string) => {
if (allProbe.length) {
const filteredProbes = allProbe.filter(
(probe: any) => probe.name.toLowerCase() === probeName
);
if (filteredProbes.length) {
return true;
}
}
return false;
};
export const validatePassword = (value: string) => { export const validatePassword = (value: string) => {
const passValid = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,20}$/; const passValid = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,20}$/;
if (value.length > 0) { if (value.length > 0) {
@ -77,7 +89,7 @@ export const isValidWebUrl = (value: string) => {
const regExLocal = /^http:\/\/localhost:([0-9]){1,4}$/g; const regExLocal = /^http:\/\/localhost:([0-9]){1,4}$/g;
const regExIpv4 = /^http:\/\/(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]):([0-9]){1,4}$/g; const regExIpv4 = /^http:\/\/(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]):([0-9]){1,4}$/g;
const regExIpv6 = /^http:\/\/((([0-9a-fA-F]){1,4})\\:){7}([0-9a-fA-F]){1,4}:([0-9]){1,4}$/g; const regExIpv6 = /^http:\/\/((([0-9a-fA-F]){1,4})\\:){7}([0-9a-fA-F]){1,4}:([0-9]){1,4}$/g;
const sshRegEx = /^([A-Za-z0-9]+@|http(|s)\:\/\/)([A-Za-z0-9.]+(:\d+)?)(?::|\/)([\d\/\w.-]+?)(\.git)?$/i; const sshRegEx = /^([A-Za-z0-9]+@|http(|s)\:\/\/)([-a-zA-Z0-9@:%._\+~#=]+(:\d+)?)(?::|\/)([\d\/\w.-]+?)(\.git)?$/i;
if ( if (
value.match(regEx) || value.match(regEx) ||
value.match(regExLocal) || value.match(regExLocal) ||

View File

@ -19,7 +19,9 @@ import {
ListManifestTemplateArray, ListManifestTemplateArray,
} from '../../../models/graphql/workflowListData'; } from '../../../models/graphql/workflowListData';
import { getProjectID } from '../../../utils/getSearchParams'; import { getProjectID } from '../../../utils/getSearchParams';
import * as WorkflowActions from '../../../redux/actions/workflow';
import useStyles from './styles'; import useStyles from './styles';
import useActions from '../../../redux/actions';
interface ChooseWorkflowRadio { interface ChooseWorkflowRadio {
selected: string; selected: string;
@ -33,7 +35,7 @@ const ChooseWorkflowFromExisting = () => {
// Local States // Local States
const [search, setSearch] = useState<string | null>(null); const [search, setSearch] = useState<string | null>(null);
const [selected, setSelected] = useState<string>(''); const [selected, setSelected] = useState<string>('');
const workflowAction = useActions(WorkflowActions);
const { data: templateData } = useQuery<ListManifestTemplate>( const { data: templateData } = useQuery<ListManifestTemplate>(
LIST_MANIFEST_TEMPLATE, LIST_MANIFEST_TEMPLATE,
{ {
@ -74,7 +76,9 @@ const ChooseWorkflowFromExisting = () => {
const templateData = filteredExistingWorkflows.filter((workflow) => { const templateData = filteredExistingWorkflows.filter((workflow) => {
return workflow.template_id === event.target.value; return workflow.template_id === event.target.value;
})[0]; })[0];
workflowAction.setWorkflowManifest({
isCustomWorkflow: templateData.isCustomWorkflow,
});
localforage.setItem('selectedScheduleOption', selection); localforage.setItem('selectedScheduleOption', selection);
localforage.setItem('workflow', { localforage.setItem('workflow', {
name: templateData.template_name.toLowerCase(), name: templateData.template_name.toLowerCase(),

View File

@ -37,7 +37,7 @@ const ProbeDetails: React.FC<ProbeDetailsProps> = ({
| { name?: string | undefined; value: unknown } | { name?: string | undefined; value: unknown }
> >
) => { ) => {
if (e.target.name === 'url' || e.target.name === 'responseTimeout') { if (e.target.name === 'url') {
setProbeData({ setProbeData({
...probeData, ...probeData,
'httpProbe/inputs': { 'httpProbe/inputs': {
@ -46,6 +46,15 @@ const ProbeDetails: React.FC<ProbeDetailsProps> = ({
}, },
}); });
} }
if (e.target.name === 'responseTimeout') {
setProbeData({
...probeData,
'httpProbe/inputs': {
...probeData['httpProbe/inputs'],
[e.target.name]: parseInt(e.target.value as string, 10),
},
});
}
if (e.target.name === 'insecureSkipVerify') { if (e.target.name === 'insecureSkipVerify') {
setProbeData({ setProbeData({
...probeData, ...probeData,
@ -185,7 +194,7 @@ const ProbeDetails: React.FC<ProbeDetailsProps> = ({
width="50%" width="50%"
id="responseTimeout" id="responseTimeout"
name="responseTimeout" name="responseTimeout"
type="text" type="number"
value={probeData['httpProbe/inputs']?.responseTimeout} value={probeData['httpProbe/inputs']?.responseTimeout}
onChange={handleHttp} onChange={handleHttp}
/> />

View File

@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
import React from 'react'; import React from 'react';
import useStyles from './styles'; import useStyles from './styles';
import ProbeDetails from './ProbeDetails'; import ProbeDetails from './ProbeDetails';
import { validateProbeName } from '../../../../utils/validate';
interface AddProbeProps { interface AddProbeProps {
probesValue: any; probesValue: any;
@ -153,12 +154,21 @@ const AddProbe: React.FC<AddProbeProps> = ({
{t('createWorkflow.tuneWorkflow.addProbe.labels.probeName')} {t('createWorkflow.tuneWorkflow.addProbe.labels.probeName')}
</InputLabel> </InputLabel>
<InputField <InputField
variant="primary"
id="name" id="name"
name="name" name="name"
type="text" type="text"
required required
value={probeData.name} value={probeData.name}
helperText={
validateProbeName(allProbes, probeData.name)
? t('createWorkflow.tuneWorkflow.addProbe.validate')
: ''
}
variant={
validateProbeName(allProbes, probeData.name)
? 'error'
: 'primary'
}
onChange={(e) => onChange={(e) =>
setProbeData({ ...probeData, name: e.target.value }) setProbeData({ ...probeData, name: e.target.value })
} }
@ -328,7 +338,13 @@ const AddProbe: React.FC<AddProbeProps> = ({
> >
{t('createWorkflow.tuneWorkflow.addProbe.button.cancel')} {t('createWorkflow.tuneWorkflow.addProbe.button.cancel')}
</ButtonOutlined> </ButtonOutlined>
<ButtonFilled type="submit"> <ButtonFilled
type="submit"
disabled={
validateProbeName(allProbes, probeData.name) ||
probeData.name.trim().length === 0
}
>
{t('createWorkflow.tuneWorkflow.addProbe.button.addProbe')} {t('createWorkflow.tuneWorkflow.addProbe.button.addProbe')}
</ButtonFilled> </ButtonFilled>
</div> </div>

View File

@ -30,6 +30,7 @@ import useStyles from './styles';
interface WorkflowTableProps { interface WorkflowTableProps {
isCustom: boolean | undefined; isCustom: boolean | undefined;
namespace: string;
} }
interface ChaosCRDTable { interface ChaosCRDTable {
@ -41,7 +42,8 @@ interface ChaosCRDTable {
ChaosEngine: string; ChaosEngine: string;
} }
const WorkflowTable = forwardRef(({ isCustom }: WorkflowTableProps, ref) => { const WorkflowTable = forwardRef(
({ isCustom, namespace }: WorkflowTableProps, ref) => {
const classes = useStyles(); const classes = useStyles();
const { t } = useTranslation(); const { t } = useTranslation();
@ -85,7 +87,11 @@ const WorkflowTable = forwardRef(({ isCustom }: WorkflowTableProps, ref) => {
expData.push({ expData.push({
StepIndex: index, StepIndex: index,
Name: chaosEngine.metadata.generateName, Name: chaosEngine.metadata.generateName,
Namespace: chaosEngine.spec.appinfo?.appns ?? '', Namespace:
chaosEngine.spec.appinfo?.appns ===
'{{workflow.parameters.adminModeNamespace}}'
? namespace
: chaosEngine.spec.appinfo?.appns ?? '',
Application: chaosEngine.spec.appinfo?.applabel ?? '', Application: chaosEngine.spec.appinfo?.applabel ?? '',
Probes: chaosEngine.spec.experiments[0].spec.probe?.length ?? 0, Probes: chaosEngine.spec.experiments[0].spec.probe?.length ?? 0,
ChaosEngine: artifact.raw.data, ChaosEngine: artifact.raw.data,
@ -178,7 +184,10 @@ const WorkflowTable = forwardRef(({ isCustom }: WorkflowTableProps, ref) => {
}; };
} }
const updatedManifest = updateManifestImage(parsedYAML, imageRegistryData); const updatedManifest = updateManifestImage(
parsedYAML,
imageRegistryData
);
workflow.setWorkflowManifest({ workflow.setWorkflowManifest({
manifest: updatedManifest, manifest: updatedManifest,
}); });
@ -269,7 +278,9 @@ const WorkflowTable = forwardRef(({ isCustom }: WorkflowTableProps, ref) => {
{index + 1} {index + 1}
</TableCell> </TableCell>
<TableCell align="left">{experiment.Name}</TableCell> <TableCell align="left">{experiment.Name}</TableCell>
<TableCell align="left">{experiment.Namespace}</TableCell> <TableCell align="left">
{experiment.Namespace}
</TableCell>
<TableCell align="left"> <TableCell align="left">
{experiment.Application} {experiment.Application}
</TableCell> </TableCell>
@ -346,6 +357,7 @@ const WorkflowTable = forwardRef(({ isCustom }: WorkflowTableProps, ref) => {
)} )}
</div> </div>
); );
}); }
);
export default WorkflowTable; export default WorkflowTable;

View File

@ -788,7 +788,11 @@ const TuneWorkflow = forwardRef((_, ref) => {
</Width> </Width>
{/* Workflow Table */} {/* Workflow Table */}
<Width width="70%"> <Width width="70%">
<WorkflowTable ref={childRef} isCustom={isCustomWorkflow} /> <WorkflowTable
ref={childRef}
isCustom={isCustomWorkflow}
namespace={namespace}
/>
</Width> </Width>
</Row> </Row>
</div> </div>

View File

@ -3,7 +3,7 @@ import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import DeveloperGuide from '../../../components/DeveloperGuide'; import DeveloperGuide from '../../../components/DeveloperGuide';
import ExperimentHeader from '../../../components/ExperimentHeader'; import ExperimentHeader from '../ExperimentHeader';
import ExperimentInfo from '../../../components/ExperimentInfo'; import ExperimentInfo from '../../../components/ExperimentInfo';
import InstallChaos from '../../../components/InstallChaos'; import InstallChaos from '../../../components/InstallChaos';
import Loader from '../../../components/Loader'; import Loader from '../../../components/Loader';