Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Chaos experiments jobs UI #641

Merged
merged 3 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion ui/src/features/components/JobForm/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ export const testTypes = {
};

export const inputTypes = {
LIST: 'LIST',
INPUT_LIST: 'INPUT_LIST',
NUMERIC_INPUT: 'NUMERIC_INPUT',
NUMERIC_WITH_SWITCH: 'NUMERIC_WITH_SWITCH',
SWITCHER: 'SWITCHER',
SWITCHER_TWO_SIDES: 'SWITCHER_TWO_SIDES',
TEXT_FIELD: 'TEXT_FIELD',
RADIO: 'RADIO',
MULTI_SELECT: 'MULTI_SELECT'
MULTI_SELECT: 'MULTI_SELECT',
LINK_ACTION: 'LINK_ACTION',
DROPDOWN: 'DROPDOWN',
SUBMIT_BUTTON: 'SUBMIT_BUTTON'
};
213 changes: 182 additions & 31 deletions ui/src/features/components/JobForm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ import { faClock, faPlayCircle } from '@fortawesome/free-regular-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import IconButton from '../../../components/IconButton';
import { createJobRequest, createStateForEditJob } from './utils';
import Button from '../../../components/Button';
import SimpleTable from '../SimpleTable';
import { chaosExperimentsForDropdown } from '../../redux/selectors/chaosExperimentsSelector';
import Dropdown from '../../../components/Dropdown/Dropdown.export';

const DESCRIPTION = 'Predator executes tests through jobs. Use this form to specify the parameters for the job you want to execute.';

Expand All @@ -51,7 +55,6 @@ class Form extends React.Component {
optionToValue: { 'Load test': 'load_test', 'Functional test': 'functional_test' },
valueToOption: { load_test: 'Load test', functional_test: 'Functional test' }
},

{
group: 'section_a',
bottom: [
Expand Down Expand Up @@ -88,7 +91,6 @@ class Form extends React.Component {
}
],
children: [

{
name: 'arrival_rate',
key: 'arrival_rate',
Expand Down Expand Up @@ -175,6 +177,7 @@ class Form extends React.Component {
info: 'When the test finishes, a report will be sent to the emails included.',
element: 'Email',
type: inputTypes.INPUT_LIST,
values: (state) => state['emails'].map((value) => ({ value, label: value })),
flexBasis: '49%'
},
{
Expand All @@ -186,13 +189,110 @@ class Form extends React.Component {
type: inputTypes.MULTI_SELECT,
options: (props) => props.webhooks,
flexBasis: '49%'

}

]
},
{
...(props.experiments.length > 0 ? [{
group: 'section_c',
children:
[
{
name: 'experiments',
key: 'experiments',
floatingLabelText: 'Running Experiments',
info: 'Chaos experiments running within the test',
element: 'RunningExperiments',
type: inputTypes.LIST,
rows: (state) => state.experiments.map((experiment, index) => {
return [
<div key={'header' + index} className={style['list-container']}>
<span className={style['list-item__title']}>experiment name:</span>
<span className={style['list-item']}> {experiment.experiment_name}</span>
<span className={style['list-item__title']}>start at:</span>
<span className={style['list-item']}> {experiment.start_after}</span>
</div>
]
}),
flexBasis: '80%'
},
{
name: 'add_experiment_option',
key: 'add_experiment_option',
floatingLabelText: '+Add Experiment',
onClick: () => {
this.setState({
add_experiment_form_experiment_name: '',
add_experiment_form_experiment_id: '',
add_experiment_form_start_after: 0,
add_experiment_form_hidden: false
})
},
type: inputTypes.LINK_ACTION,
justifyContent: 'flex-end'
},
{
name: 'add_experiment_form_experiment_name',
key: 'add_experiment_form_experiment_name',
floatingLabelText: 'Experiment',
list: (props) => props.experiments,
placeholder: 'Select experiment to run',
element: 'new_experiment',
selectedOption: (state) => ({ key: state.add_experiment_form_experiment_id, value: state.add_experiment_form_experiment_name }),
type: inputTypes.DROPDOWN,
onChange: ({ value, key }) => {
this.setState({
add_experiment_form_experiment_name: value,
add_experiment_form_experiment_id: key
})
},
flexBasis: '49%',
hiddenCondition: (state) => state.add_experiment_form_hidden === true
},
{
name: 'add_experiment_form_start_after',
key: 'add_experiment_form_start_after',
floatingLabelText: 'Start At',
info: 'When to start the experiment within the test timeframe (milliseconds)',
element: 'StartAt',
type: inputTypes.NUMERIC_INPUT,
justifyContent: 'flex-end',
height: '35px',
hiddenCondition: (state) => state.add_experiment_form_hidden === true
},
{
name: 'add_experiment_submit',
key: 'add_experiment_submit',
floatingLabelText: 'Add',
element: 'AddExperiment',
type: inputTypes.SUBMIT_BUTTON,
inverted: true,
justifyContent: 'flex-end',
paddingTop: '22px',
height: '35px',
onClick: () => {
const newExperiment = {
experiment_id: this.state.add_experiment_form_experiment_id,
experiment_name: this.state.add_experiment_form_experiment_name,
start_after: this.state.add_experiment_form_start_after
}
const experiments = [...this.state.experiments, newExperiment]
this.state.experiments.push(newExperiment)
this.setState((prevState) => {
return {
...prevState,
experiments: experiments,
add_experiment_form_hidden: true
}
})
console.log(this.state.experiments)
},
disabled: (state) => state.add_experiment_form_experiment_name.trim().length === 0 || state.add_experiment_form_start_after === 0,
hiddenCondition: (state) => state.add_experiment_form_hidden === true
}
]
}] : []),
{
group: 'section_d',
flexDirection: 'column',
children: [
{
Expand Down Expand Up @@ -233,6 +333,7 @@ class Form extends React.Component {
run_immediately: false,
emails: [],
webhooks: [],
experiments: [],
helpInfo: undefined,
parallelism: undefined,
max_virtual_users: undefined,
Expand All @@ -252,7 +353,11 @@ class Form extends React.Component {
parallelism: true,
max_virtual_users: true
},
mode: 'Simple'
mode: 'Simple',
add_experiment_form_hidden: true,
add_experiment_form_experiment_id: '',
add_experiment_form_experiment_name: '',
add_experiment_form_start_after: 0
};

if (this.props.editMode) {
Expand Down Expand Up @@ -283,6 +388,7 @@ class Form extends React.Component {

componentDidMount () {
this.props.getWebhooks();
this.props.getChaosExperiments();
}

componentDidUpdate (prevProps, prevState, snapshot) {
Expand Down Expand Up @@ -424,6 +530,28 @@ class Form extends React.Component {

const { cron_expression } = this.state;
switch (oneItem.type) {
case inputTypes.LINK_ACTION:
return (
<RactangleAlignChildrenLeft>
<div
className={style['actions-style']}
onClick={oneItem.onClick}>
{oneItem.floatingLabelText}
</div>
</RactangleAlignChildrenLeft>
)
case inputTypes.SUBMIT_BUTTON:
return (
<RactangleAlignChildrenLeft style={{ flexBasis: oneItem.flexDirection, paddingTop: oneItem.paddingTop }}>
<Button
disabled={oneItem.disabled(this.state)}
className={style['actions-style']}
inverted={oneItem.inverted}
onClick={oneItem.onClick}>
{oneItem.floatingLabelText}
</Button>
</RactangleAlignChildrenLeft>
)
case inputTypes.SWITCHER:
return (
<RactangleAlignChildrenLeft>
Expand All @@ -440,7 +568,6 @@ class Form extends React.Component {
);
case inputTypes.SWITCHER_TWO_SIDES:
return (

<RactangleAlignChildrenLeft style={{ justifyContent: oneItem.justifyContent }}>
<div style={{ marginRight: '5px', fontSize: '10px', color: '#557eff' }}>{oneItem.leftOption}</div>
<UiSwitcher
Expand All @@ -456,22 +583,42 @@ class Form extends React.Component {
<div style={{ marginLeft: '5px', fontSize: '10px', color: '#557eff', marginRight: '5px' }}>{oneItem.rightOption}</div>
<InfoToolTip data={oneItem} iconSize={'10px'} />
</RactangleAlignChildrenLeft>

);
case inputTypes.LIST: {
return (
<TitleInput key={oneItem.key} title={oneItem.floatingLabelText}
rightComponent={<InfoToolTip data={oneItem} />}>
<SimpleTable style={{ paddingLeft: '40px', paddingRight: '40px', marginBottom: '5px' }}
rows={oneItem.rows(this.state)} />
</TitleInput>
)
}
case inputTypes.INPUT_LIST:
return (
<TitleInput key={oneItem.key} title={oneItem.floatingLabelText}
rightComponent={<InfoToolTip data={oneItem} />}>
<MultiValueInput
values={this.state[oneItem.name].map((value) => ({ value, label: value }))}
disabled={oneItem.disabled}
values={oneItem.values(this.state)}
onAddItem={(evt) => this.handleInputListAdd(oneItem.name, evt)}
onRemoveItem={evt => this.handleInputListRemove(oneItem.name, evt)}
// validationFunc={validateFunc}
/>
</TitleInput>

);

case inputTypes.DROPDOWN:
return (
<TitleInput key={oneItem.key} title={oneItem.floatingLabelText}
rightComponent={<InfoToolTip data={oneItem} />}>
<Dropdown
options={oneItem.list(this.props)}
selectedOption={oneItem.selectedOption(this.state)}
onChange={oneItem.onChange}
placeHolder={oneItem.placeholder}
/>
</TitleInput>
)
case inputTypes.TEXT_FIELD:
return (
<TitleInput key={oneItem.key} title={oneItem.floatingLabelText}
Expand All @@ -485,7 +632,6 @@ class Form extends React.Component {
</ErrorWrapper>
</TitleInput>
);

case inputTypes.RADIO:
return (
<TitleInput key={oneItem.key} title={oneItem.floatingLabelText}
Expand Down Expand Up @@ -525,24 +671,27 @@ class Form extends React.Component {
);
case inputTypes.NUMERIC_INPUT:
return (
<TitleInput labelStyle={{ marginRight: '5px' }} key={oneItem.key} title={oneItem.floatingLabelText}
rightComponent={<InfoToolTip data={oneItem} />}>
<ErrorWrapper errorText={this.state.errors[oneItem.name]}>
<NumericInput
minValue={0}
maxValue={10000000}
hideNumber={Number.isNaN(this.state[oneItem.name])}
value={this.state[oneItem.name]}
onChange={(value) => {
this.onChangeProperty(oneItem.name, value)
const newState = oneItem.newState && oneItem.newState(value);
this.setState(newState);
}}
disabled={this.state.disabled[oneItem.name]}
width={'80px'}
/>
</ErrorWrapper>
</TitleInput>
<RactangleAlignChildrenLeft style={{ justifyContent: oneItem.justifyContent }}>
<TitleInput labelStyle={{ marginRight: '5px' }} key={oneItem.key} title={oneItem.floatingLabelText}
rightComponent={<InfoToolTip data={oneItem} />}>
<ErrorWrapper errorText={this.state.errors[oneItem.name]}>
<NumericInput
minValue={0}
maxValue={10000000}
height={oneItem.height}
hideNumber={Number.isNaN(this.state[oneItem.name])}
value={this.state[oneItem.name]}
onChange={(value) => {
this.onChangeProperty(oneItem.name, value)
const newState = oneItem.newState && oneItem.newState(value);
this.setState(newState);
}}
disabled={this.state.disabled[oneItem.name]}
width={'80px'}
/>
</ErrorWrapper>
</TitleInput>
</RactangleAlignChildrenLeft>
);
case inputTypes.NUMERIC_WITH_SWITCH:
return (
Expand Down Expand Up @@ -628,15 +777,17 @@ function mapStateToProps (state) {
processingAction: processingCreateJob(state),
serverError: createJobFailure(state),
createJobSuccess: createJobSuccess(state),
webhooks: webhooksForDropdown(state)
webhooks: webhooksForDropdown(state),
experiments: chaosExperimentsForDropdown(state)
};
}

const mapDispatchToProps = {
clearErrorOnCreateJob: Actions.clearErrorOnCreateJob,
createJob: Actions.createJob,
editJob: Actions.editJob,
getWebhooks: Actions.getWebhooks
getWebhooks: Actions.getWebhooks,
getChaosExperiments: Actions.getChaosExperiments
};

export default connect(mapStateToProps, mapDispatchToProps)(Form);
27 changes: 26 additions & 1 deletion ui/src/features/components/JobForm/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,29 @@
letter-spacing: normal;
color: #557eff;
margin-left: 10px;
}
}

.actions-style {
cursor: pointer;
font-size: 13px;
font-weight: normal;
font-style: normal;
font-stretch: normal;
line-height: normal;
letter-spacing: normal;
color: #557eff;
margin-left: 10px;
}

.list-container {
display: flex;
align-items: center;
}

.list-item {
margin: 10px;
&__title {
color: #557eff;

}
}
Loading