Skip to content

Commit

Permalink
[frontend/backend] add a start now for scenario (#1368)
Browse files Browse the repository at this point in the history
  • Loading branch information
guillaumejparis committed Sep 10, 2024
1 parent cb7c0fd commit bc9c2fc
Show file tree
Hide file tree
Showing 23 changed files with 908 additions and 853 deletions.

Large diffs are not rendered by default.

476 changes: 243 additions & 233 deletions openbas-api/src/main/java/io/openbas/rest/scenario/ScenarioApi.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ private void createExercisesFromScenarios() {
validScenarios.stream()
.filter(scenario -> !alreadyExistIds.contains(scenario.getId()))
// Create simulation with start date provided by cron
.forEach(scenario -> this.scenarioToExerciseService.toExercise(scenario, cronToDate(scenario.getRecurrence())));
.forEach(scenario -> this.scenarioToExerciseService.toExercise(scenario, cronToDate(scenario.getRecurrence()),
null));
}

private void cleanOutdatedRecurringScenario() {
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import static io.openbas.utils.fixtures.InjectFixture.getInjectForEmailContract;
import static io.openbas.utils.fixtures.ObjectiveFixture.OBJECTIVE_NAME;
import static io.openbas.utils.fixtures.ObjectiveFixture.getObjective;
import static io.openbas.utils.fixtures.ScenarioFixture.getScenario;
import static io.openbas.utils.fixtures.TagFixture.getTag;
import static io.openbas.utils.fixtures.TeamFixture.getTeam;
import static io.openbas.utils.fixtures.UserFixture.getUser;
Expand Down Expand Up @@ -220,7 +219,7 @@ void scenarioToExerciseTest() {
VARIABLE_ID = variableSaved.getId();

// -- EXECUTE --
Exercise exercise = this.scenarioToExerciseService.toExercise(scenario, null);
Exercise exercise = this.scenarioToExerciseService.toExercise(scenario, null, null);
EXERCISE_ID = exercise.getId();
Exercise exerciseSaved = this.loadService.exercise(EXERCISE_ID);

Expand Down
5 changes: 0 additions & 5 deletions openbas-front/src/actions/Inject.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ export const fetchInject = (injectId) => (dispatch) => {
return getReferential(schema.inject, uri)(dispatch);
};

export const tryInject = (injectId) => (dispatch) => {
const uri = `/api/injects/try/${injectId}`;
return getReferential(null, uri, null)(dispatch);
};

// -- EXERCISES --

export const fetchExerciseInjects = (exerciseId) => (dispatch) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const fetchInjectResultDto = (injectId: string) => {

export const deleteAtomicTesting = (injectId: string) => {
const uri = `${ATOMIC_TESTING_URI}/${injectId}`;
return simpleDelCall(uri, injectId);
return simpleDelCall(uri);
};

export const updateAtomicTesting = (injectId: string, data: AtomicTestingInput) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ export const fetchInjectTestStatus = (testId: string | undefined) => {

export const deleteInjectTest = (testId: string | undefined) => {
const uri = `/api/injects/test/${testId}`;
return simpleDelCall(uri, testId);
return simpleDelCall(uri);
};
6 changes: 5 additions & 1 deletion openbas-front/src/actions/injects/inject-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Dispatch } from 'redux';
import { getReferential, simpleCall, simplePostCall } from '../../utils/Action';
import type { Exercise, Scenario } from '../../utils/api-types';
import * as schema from '../Schema';
import { MESSAGING$ } from '../../utils/Environment';

export const testInject = (injectId: string) => {
const uri = `/api/injects/${injectId}/test`;
Expand All @@ -11,7 +12,10 @@ export const testInject = (injectId: string) => {
export const bulkTestInjects = (injectIds: string[]) => {
const data = injectIds;
const uri = '/api/injects/bulk/test';
return simplePostCall(uri, data, "Can't be tested");
return simplePostCall(uri, data, false).catch((error) => {
MESSAGING$.notifyError('Can\'t be tested');
throw error;
});
};

// -- EXERCISES --
Expand Down
2 changes: 1 addition & 1 deletion openbas-front/src/actions/mapper/mapper-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const fetchMapper = (mapperId: string) => {

export const deleteMapper = (mapperId: RawPaginationImportMapper['import_mapper_id']) => {
const uri = `${XLS_MAPPER_URI}/${mapperId}`;
return simpleDelCall(uri, mapperId);
return simpleDelCall(uri);
};

export const createMapper = (data: ImportMapperAddInput) => {
Expand Down
7 changes: 7 additions & 0 deletions openbas-front/src/actions/scenarios/scenario-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ export const duplicateScenario = (scenarioId: string) => (dispatch: Dispatch) =>
return postReferential(scenario, uri, null)(dispatch);
};

// -- SCENARIO TO EXERCISE

export const createRunningExerciseFromScenario = (scenarioId: string) => {
const uri = `${SCENARIO_URI}/${scenarioId}/exercise/running`;
return simplePostCall(uri);
};

// -- TAGS --

export const updateScenarioTags = (scenarioId: Scenario['scenario_id'], data: ScenarioUpdateTagsInput) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { TeamsHelper } from '../../../../actions/teams/team-helper';
import useDataLoader from '../../../../utils/hooks/useDataLoader';
import { fetchTeams } from '../../../../actions/teams/team-actions';
import { useAppDispatch } from '../../../../utils/hooks';
import { MESSAGING$ } from '../../../../utils/Environment';

interface Props {
atomic: InjectResultDTO;
Expand Down Expand Up @@ -52,6 +53,7 @@ const AtomicTestingUpdate: FunctionComponent<Props> = ({
]),
)(data);
updateAtomicTesting(atomic.inject_id, toUpdate).then((result: { data: InjectResultDTO }) => {
MESSAGING$.notifySuccess('The element has been updated');
updateInjectResultDto(result.data);
});
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { FunctionComponent, useContext, useState } from 'react';
import { Link } from 'react-router-dom';
import { Alert, Button, Dialog, DialogActions, DialogContent, DialogContentText, IconButton, Menu, MenuItem, Table, TableBody, TableCell, TableRow } from '@mui/material';
import { Button, Dialog, DialogActions, DialogContent, DialogContentText, IconButton, Menu, MenuItem, Table, TableBody, TableCell, TableRow } from '@mui/material';
import { MoreVert } from '@mui/icons-material';
import { useFormatter } from '../../../../components/i18n';
import Transition from '../../../../components/common/Transition';
import { InjectContext, PermissionsContext } from '../Context';
import type { Inject, InjectStatus, InjectStatusExecution, InjectTestStatus } from '../../../../utils/api-types';
import { duplicateInjectForExercise, duplicateInjectForScenario, tryInject } from '../../../../actions/Inject';
import { duplicateInjectForExercise, duplicateInjectForScenario } from '../../../../actions/Inject';
import { testInject } from '../../../../actions/injects/inject-action';
import { useAppDispatch } from '../../../../utils/hooks';
import DialogDuplicate from '../../../../components/common/DialogDuplicate';
Expand Down Expand Up @@ -55,7 +55,6 @@ const InjectPopover: FunctionComponent<Props> = ({

const [openDelete, setOpenDelete] = useState(false);
const [duplicate, setDuplicate] = useState(false);
const [openTry, setOpenTry] = useState(false);
const [openTest, setOpenTest] = useState(false);
const [openEnable, setOpenEnable] = useState(false);
const [openDisable, setOpenDisable] = useState(false);
Expand Down Expand Up @@ -107,22 +106,11 @@ const InjectPopover: FunctionComponent<Props> = ({
handleCloseDelete();
};

const handleCloseTry = () => setOpenTry(false);

const handleCloseResult = () => {
setOpenResult(false);
setInjectResult(null);
};

const submitTry = () => {
// FIXME: remove try possibility
dispatch(tryInject(inject.inject_id)).then((payload: InjectStatus) => {
setInjectResult(payload);
setOpenResult(true);
});
handleCloseTry();
};

const handleOpenTest = () => {
setOpenTest(true);
handlePopoverClose();
Expand Down Expand Up @@ -250,15 +238,6 @@ const InjectPopover: FunctionComponent<Props> = ({
{t('Trigger now')}
</MenuItem>
)}
{/* TODO create an atomic testing when using this button */}
{/* {inject.inject_type !== 'openbas_manual' && ( */}
{/* <MenuItem */}
{/* onClick={handleOpenTry} */}
{/* disabled={isDisabled} */}
{/* > */}
{/* {t('Try the inject')} */}
{/* </MenuItem> */}
{/* )} */}
{inject.inject_enabled ? (
<MenuItem
onClick={handleOpenDisable}
Expand Down Expand Up @@ -324,29 +303,6 @@ const InjectPopover: FunctionComponent<Props> = ({
</Button>
</DialogActions>
</Dialog>
<Dialog
TransitionComponent={Transition}
open={openTry}
onClose={handleCloseTry}
PaperProps={{ elevation: 1 }}
>
<DialogContent>
<DialogContentText>
<p>{t(`Do you want to try this inject: ${inject.inject_title}?`)}</p>
<Alert severity="info">
{t('The inject will only be sent to you.')}
</Alert>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleCloseTry}>
{t('Cancel')}
</Button>
<Button color="secondary" onClick={submitTry}>
{t('Try')}
</Button>
</DialogActions>
</Dialog>
<DialogTest
open={openTest}
handleClose={handleCloseTest}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const IndexScenarioComponent: FunctionComponent<{ scenario: ScenarioStore }> = (
tabValue = `/admin/scenarios/${scenario.scenario_id}/tests`;
}
const [openScenarioRecurringFormDialog, setOpenScenarioRecurringFormDialog] = useState<boolean>(false);
const [openInstantiateSimulationAndStart, setOpenInstantiateSimulationAndStart] = useState<boolean>(false);
const [selectRecurring, setSelectRecurring] = useState('noRepeat');
const [cronExpression, setCronExpression] = useState<string | null>(scenario.scenario_recurrence || null);
const [parsedCronExpression, setParsedCronExpression] = useState<ParsedCron | null>(scenario.scenario_recurrence ? parseCron(scenario.scenario_recurrence) : null);
Expand All @@ -74,7 +75,7 @@ const IndexScenarioComponent: FunctionComponent<{ scenario: ScenarioStore }> = (
if (!cronExpression || !parsedCronExpression) {
return null;
}
let sentence = '';
let sentence: string;
if (noRepeat) {
sentence = `${fld(scenario.scenario_recurrence_start)} ${t('recurrence_at')} ${ft(new Date().setUTCHours(parsedCronExpression.h, parsedCronExpression.m, 0))}`;
} else {
Expand Down Expand Up @@ -110,6 +111,8 @@ const IndexScenarioComponent: FunctionComponent<{ scenario: ScenarioStore }> = (
selectRecurring={selectRecurring}
setOpenScenarioRecurringFormDialog={setOpenScenarioRecurringFormDialog}
openScenarioRecurringFormDialog={openScenarioRecurringFormDialog}
setOpenInstantiateSimulationAndStart={setOpenInstantiateSimulationAndStart}
openInstantiateSimulationAndStart={openInstantiateSimulationAndStart}
noRepeat={noRepeat}
/>
<Box
Expand Down Expand Up @@ -175,7 +178,7 @@ const IndexScenarioComponent: FunctionComponent<{ scenario: ScenarioStore }> = (
</Box>
<Suspense fallback={<Loader />}>
<Routes>
<Route path="" element={errorWrapper(Scenario)({ setOpenScenarioRecurringFormDialog })} />
<Route path="" element={errorWrapper(Scenario)({ setOpenInstantiateSimulationAndStart })} />
<Route path="definition" element={errorWrapper(ScenarioDefinition)()} />
<Route path="injects" element={errorWrapper(Injects)()} />
<Route path="tests/:statusId?" element={errorWrapper(Tests)()} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const useStyles = makeStyles(() => ({
},
}));

const Scenario = ({ setOpenScenarioRecurringFormDialog }: { setOpenScenarioRecurringFormDialog: React.Dispatch<React.SetStateAction<boolean>> }) => {
const Scenario = ({ setOpenInstantiateSimulationAndStart }: { setOpenInstantiateSimulationAndStart: React.Dispatch<React.SetStateAction<boolean>> }) => {
// Standard hooks
const classes = useStyles();
const theme = useTheme<Theme>();
Expand Down Expand Up @@ -245,9 +245,9 @@ const Scenario = ({ setOpenScenarioRecurringFormDialog }: { setOpenScenarioRecur
variant="contained"
color="primary"
size="large"
onClick={() => setOpenScenarioRecurringFormDialog(true)}
onClick={() => setOpenInstantiateSimulationAndStart(true)}
>
{t('Simulate Now')}
{t('Launch simulation now')}
</Button>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import { useNavigate, useParams } from 'react-router-dom';
import { Link, useNavigate, useParams } from 'react-router-dom';
import React, { useEffect } from 'react';
import { Button, Tooltip, Typography } from '@mui/material';
import { Button, Dialog, DialogActions, DialogContent, DialogContentText, Tooltip, Typography } from '@mui/material';
import { makeStyles, useTheme } from '@mui/styles';
import { PlayArrowOutlined, Stop } from '@mui/icons-material';
import { useAppDispatch } from '../../../../utils/hooks';
import { useHelper } from '../../../../store';
import type { ScenariosHelper } from '../../../../actions/scenarios/scenario-helper';
import useDataLoader from '../../../../utils/hooks/useDataLoader';
import { fetchScenario, updateScenarioRecurrence } from '../../../../actions/scenarios/scenario-actions';
import { createRunningExerciseFromScenario, fetchScenario, updateScenarioRecurrence } from '../../../../actions/scenarios/scenario-actions';
import type { ScenarioStore } from '../../../../actions/scenarios/Scenario';
import ScenarioPopover from './ScenarioPopover';
import { useFormatter } from '../../../../components/i18n';
import { parseCron, ParsedCron } from '../../../../utils/Cron';
import ScenarioRecurringFormDialog from './ScenarioRecurringFormDialog';
import { truncate } from '../../../../utils/String';
import type { Theme } from '../../../../components/Theme';
import Transition from '../../../../components/common/Transition';
import { MESSAGING$ } from '../../../../utils/Environment';
import type { Exercise } from '../../../../utils/api-types';

const useStyles = makeStyles(() => ({
title: {
Expand Down Expand Up @@ -51,7 +54,9 @@ interface ScenarioHeaderProps {
setSelectRecurring: React.Dispatch<React.SetStateAction<string>>;
selectRecurring: string;
setOpenScenarioRecurringFormDialog: React.Dispatch<React.SetStateAction<boolean>>;
setOpenInstantiateSimulationAndStart: React.Dispatch<React.SetStateAction<boolean>>;
openScenarioRecurringFormDialog: boolean,
openInstantiateSimulationAndStart: boolean,
noRepeat: boolean;
}

Expand All @@ -63,6 +68,8 @@ const ScenarioHeader = ({
noRepeat,
openScenarioRecurringFormDialog,
setOpenScenarioRecurringFormDialog,
openInstantiateSimulationAndStart,
setOpenInstantiateSimulationAndStart,
}: ScenarioHeaderProps) => {
// Standard hooks
const { t } = useFormatter();
Expand Down Expand Up @@ -138,9 +145,9 @@ const ScenarioHeader = ({
variant="contained"
color="primary"
size="small"
onClick={() => setOpenScenarioRecurringFormDialog(true)}
onClick={() => setOpenInstantiateSimulationAndStart(true)}
>
{t('Launch')}
{t('Launch now')}
</Button>
)}
<ScenarioPopover
Expand All @@ -157,6 +164,35 @@ const ScenarioHeader = ({
onSubmit={onSubmit}
initialValues={scenario}
/>
<Dialog
open={openInstantiateSimulationAndStart}
TransitionComponent={Transition}
onClose={() => setOpenInstantiateSimulationAndStart(false)}
PaperProps={{ elevation: 1 }}
>
<DialogContent>
<DialogContentText>
{t('A simulation will be launched based on this scenario and will start immediately. Are you sure you want to proceed?')}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => setOpenInstantiateSimulationAndStart(false)}>
{t('Cancel')}
</Button>
<Button
color="secondary"
onClick={async () => {
setOpenInstantiateSimulationAndStart(false);
const exercise: Exercise = (await createRunningExerciseFromScenario(scenarioId)).data;
MESSAGING$.notifySuccess(t('New simulation successfully created and started. Click {here} to view the simulation.', {
here: <Link to={`/admin/exercises/${exercise.exercise_id}`}>{t('here')}</Link>,
}));
}}
>
{t('Confirm')}
</Button>
</DialogActions>
</Dialog>
<div className="clearfix" />
</>
);
Expand Down
Loading

0 comments on commit bc9c2fc

Please sign in to comment.