Skip to content

Commit

Permalink
[Frontend|Backend]Be able to test emails and sms related injects (#1295)
Browse files Browse the repository at this point in the history
  • Loading branch information
johanah29 authored Aug 19, 2024
1 parent a1b7c10 commit 26709d2
Show file tree
Hide file tree
Showing 24 changed files with 1,453 additions and 644 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.openbas.migration;

import org.flywaydb.core.api.migration.BaseJavaMigration;
import org.flywaydb.core.api.migration.Context;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.sql.Statement;

@Component
public class V3_31__Add_Injects_tests_statuses extends BaseJavaMigration {

@Override
public void migrate(Context context) throws Exception {
Connection connection = context.getConnection();
Statement select = connection.createStatement();
// Create table
select.execute("""
CREATE TABLE injects_tests_statuses (
status_id varchar(255) NOT NULL CONSTRAINT inject_test_status_pkey PRIMARY KEY,
status_name VARCHAR(255) NOT NULL,
status_executions text,
tracking_sent_date timestamp,
tracking_ack_date timestamp,
tracking_end_date timestamp,
tracking_total_execution_time bigint,
tracking_total_count int,
tracking_total_error int,
tracking_total_success int,
status_inject VARCHAR(255) NOT NULL CONSTRAINT inject_test_status_inject_id_fkey REFERENCES injects(inject_id) ON DELETE SET NULL,
status_created_at timestamp not null default now(),
status_updated_at timestamp not null default now()
);
CREATE INDEX idx_inject_test_inject ON injects_tests_statuses(status_inject);
""");
}

}
29 changes: 1 addition & 28 deletions openbas-api/src/main/java/io/openbas/rest/inject/InjectApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.openbas.rest.inject.service.InjectDuplicateService;
import io.openbas.service.AtomicTestingService;
import io.openbas.service.InjectService;
import io.openbas.service.InjectTestStatusService;
import io.openbas.service.ScenarioService;
import io.openbas.utils.AtomicTestingMapper;
import io.openbas.utils.pagination.SearchPaginationInput;
Expand All @@ -29,8 +30,6 @@
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.java.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
Expand All @@ -49,9 +48,6 @@
import java.util.stream.StreamSupport;

import static io.openbas.config.SessionHelper.currentUser;

import io.openbas.execution.Injector;

import static io.openbas.database.model.User.ROLE_ADMIN;
import static io.openbas.database.specification.CommunicationSpecification.fromInject;
import static io.openbas.helper.DatabaseHelper.resolveOptionalRelation;
Expand All @@ -77,7 +73,6 @@ public class InjectApi extends RestBehavior {

private final Executor executor;
private final InjectorContractRepository injectorContractRepository;
private ApplicationContext context;
private final CommunicationRepository communicationRepository;
private final ExerciseRepository exerciseRepository;
private final UserRepository userRepository;
Expand All @@ -94,11 +89,6 @@ public class InjectApi extends RestBehavior {
private final AtomicTestingService atomicTestingService;
private final InjectDuplicateService injectDuplicateService;

@Autowired
public void setContext(ApplicationContext context) {
this.context = context;
}

// -- INJECTS --

@GetMapping(INJECT_URI + "/{injectId}")
Expand Down Expand Up @@ -169,23 +159,6 @@ public Inject tryInject(@PathVariable String injectId) {
return atomicTestingService.tryInject(injectId);
}

@GetMapping(INJECT_URI + "/test/{injectId}")
public InjectStatus testInject(@PathVariable String injectId) {
Inject inject = injectRepository.findById(injectId).orElseThrow();
User user = this.userRepository.findById(currentUser().getId()).orElseThrow();
List<ExecutionContext> userInjectContexts = List.of(
this.executionContextService.executionContext(user, inject, "Direct test")
);
Injector executor = context.getBean(
inject.getInjectorContract().map(injectorContract -> injectorContract.getInjector().getType()).orElseThrow(),
io.openbas.execution.Injector.class);
ExecutableInject injection = new ExecutableInject(false, true, inject, List.of(), inject.getAssets(),
inject.getAssetGroups(), userInjectContexts);
Execution execution = executor.executeInjection(injection);
return InjectStatus.fromExecutionTest(execution);

}

@Transactional(rollbackFor = Exception.class)
@PutMapping(INJECT_URI + "/{exerciseId}/{injectId}")
@PreAuthorize("isExercisePlanner(#exerciseId)")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package io.openbas.rest.inject_test_status;

import io.openbas.database.model.InjectTestStatus;
import io.openbas.rest.helper.RestBehavior;
import io.openbas.service.InjectTestStatusService;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@PreAuthorize("isAdmin()")
@RequiredArgsConstructor
public class InjectTestStatusApi extends RestBehavior {

private final InjectTestStatusService injectTestStatusService;

@GetMapping("/api/injects/{injectId}/test")
public InjectTestStatus testInject(@PathVariable @NotBlank String injectId) {
return injectTestStatusService.testInject(injectId);
}

@GetMapping("/api/exercise/{exerciseId}/injects/test")
public List<InjectTestStatus> findAllExerciseInjectTests(@PathVariable @NotBlank String exerciseId) {
return injectTestStatusService.findAllInjectTestsByExerciseId(exerciseId);
}

@GetMapping("/api/scenario/{scenarioId}/injects/test")
public List<InjectTestStatus> findAllScenarioInjectTests(@PathVariable @NotBlank String scenarioId) {
return injectTestStatusService.findAllInjectTestsByScenarioId(scenarioId);
}

@GetMapping("/api/injects/test/{testId}")
public InjectTestStatus findInjectTestStatus(@PathVariable @NotBlank String testId) {
return injectTestStatusService.findInjectTestStatusById(testId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package io.openbas.service;

import io.openbas.database.model.*;
import io.openbas.database.repository.InjectRepository;
import io.openbas.database.repository.InjectTestStatusRepository;
import io.openbas.database.repository.UserRepository;
import io.openbas.execution.ExecutableInject;
import io.openbas.execution.ExecutionContext;
import io.openbas.execution.ExecutionContextService;
import io.openbas.execution.Injector;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.java.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static io.openbas.config.SessionHelper.currentUser;

@Service
@Log
@RequiredArgsConstructor
public class InjectTestStatusService {

private ApplicationContext context;
private final UserRepository userRepository;
private final InjectRepository injectRepository;
private final ExecutionContextService executionContextService;
private final InjectTestStatusRepository injectTestStatusRepository;

@Autowired
public void setContext(ApplicationContext context) {
this.context = context;
}

@Transactional
public InjectTestStatus testInject(String injectId) {
Inject inject = injectRepository.findById(injectId).orElseThrow();
User user = this.userRepository.findById(currentUser().getId()).orElseThrow();
List<ExecutionContext> userInjectContexts = List.of(
this.executionContextService.executionContext(user, inject, "Direct test")
);
Injector executor = context.getBean(
inject.getInjectorContract().map(injectorContract -> injectorContract.getInjector().getType()).orElseThrow(),
io.openbas.execution.Injector.class);
ExecutableInject injection = new ExecutableInject(false, true, inject, List.of(), inject.getAssets(),
inject.getAssetGroups(), userInjectContexts);
Execution execution = executor.executeInjection(injection);

//Save inject test status
Optional<InjectTestStatus> injectTestStatus = this.injectTestStatusRepository.findByInject(inject);
InjectTestStatus injectTestStatusToSave = InjectTestStatus.fromExecutionTest(execution);
injectTestStatus.ifPresent(testStatus -> {
injectTestStatusToSave.setId(testStatus.getId());
injectTestStatusToSave.setTestCreationDate(testStatus.getTestCreationDate());
});
injectTestStatusToSave.setInject(inject);
this.injectTestStatusRepository.save(injectTestStatusToSave);

return injectTestStatusToSave;
}

public List<InjectTestStatus> findAllInjectTestsByExerciseId(String exerciseId) {
return injectTestStatusRepository.findAllExerciseInjectTests(exerciseId);
}

public List<InjectTestStatus> findAllInjectTestsByScenarioId(String scenarioId) {
return injectTestStatusRepository.findAllScenarioInjectTests(scenarioId);
}

public InjectTestStatus findInjectTestStatusById(String testId) {
return injectTestStatusRepository.findById(testId).orElseThrow();
}

}
2 changes: 1 addition & 1 deletion openbas-api/src/test/java/io/openbas/rest/TeamApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ void retrieveTeamsOnScenarioTest() throws Exception {
void addPlayerOnTeamOnScenarioTest() throws Exception {
// -- PREPARE --
User user = new User();
user.setEmail("test@gmail.com");
user.setEmail("testfiligran@gmail.com");
user = this.userRepository.save(user);
USER_ID = user.getId();
ScenarioTeamPlayersEnableInput input = new ScenarioTeamPlayersEnableInput();
Expand Down
2 changes: 1 addition & 1 deletion openbas-front/src/actions/Inject.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const tryInject = (injectId) => (dispatch) => {
};

export const testInject = (injectId) => {
const uri = `/api/injects/test/${injectId}`;
const uri = `/api/injects/${injectId}/test`;
return simpleCall(uri);
};

Expand Down
17 changes: 17 additions & 0 deletions openbas-front/src/actions/inject_test/inject-test-actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { simpleCall } from '../../utils/Action';

// eslint-disable-next-line import/prefer-default-export
export const searchExerciseInjectTests = (exerciseId: string) => {
const uri = `/api/exercise/${exerciseId}/injects/test`;
return simpleCall(uri);
};

export const searchScenarioInjectTests = (scenarioId: string) => {
const uri = `/api/scenario/${scenarioId}/injects/test`;
return simpleCall(uri);
};

export const fetchInjectTestStatus = (testId: string | undefined) => {
const uri = `/api/injects/test/${testId}`;
return simpleCall(uri);
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
import React, { FunctionComponent, useContext, useState } from 'react';
import { Alert, Button, Dialog, DialogActions, DialogContent, DialogContentText, IconButton, Menu, MenuItem, Table, TableBody, TableCell, TableRow } from '@mui/material';
import React, { FunctionComponent, useContext, useEffect, useState } from 'react';
import {
Alert,
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
IconButton,
Menu,
MenuItem,
Table,
TableBody,
TableCell,
TableRow,
SnackbarCloseReason,
Link,
} from '@mui/material';
import { MoreVert } from '@mui/icons-material';
import { useFormatter } from '../../../../components/i18n';
import Transition from '../../../../components/common/Transition';
Expand All @@ -9,20 +25,24 @@ import type { Inject, InjectStatus, InjectStatusExecution, Tag } from '../../../
import { duplicateInjectForExercise, duplicateInjectForScenario, tryInject, testInject } from '../../../../actions/Inject';
import { useAppDispatch } from '../../../../utils/hooks';
import DialogDuplicate from '../../../../components/common/DialogDuplicate';
import { useHelper } from '../../../../store';
import type { ExercisesHelper } from '../../../../actions/exercises/exercise-helper';

interface Props {
inject: InjectStore & { inject_testable?: boolean }; // FIXME: Inject object coming from multiple endpoints with different properties
inject: InjectStore;
tagsMap: Record<string, Tag>;
setSelectedInjectId: (injectId: Inject['inject_id']) => void;
isDisabled: boolean;
canBeTested?: boolean;
exerciseOrScenarioId?: string;
}

const InjectPopover: FunctionComponent<Props> = ({
inject,
setSelectedInjectId,
isDisabled,
canBeTested = false,
exerciseOrScenarioId,
}) => {
// Standard hooks
const { t } = useFormatter();
Expand All @@ -48,6 +68,8 @@ const InjectPopover: FunctionComponent<Props> = ({
const [_injectTestResult, setInjectTestResult] = useState<InjectStatus | null>(null);
const [anchorEl, setAnchorEl] = useState<Element | null>(null);

const isExercise = useHelper((helper: ExercisesHelper) => helper.getExercisesMap()[exerciseOrScenarioId!] !== undefined);

const handlePopoverOpen = (event: React.MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
setAnchorEl(event.currentTarget);
Expand Down Expand Up @@ -114,9 +136,36 @@ const InjectPopover: FunctionComponent<Props> = ({
setInjectTestResult(null);
};

const [openDialog, setOpenDialog] = React.useState<boolean>(false);
const handleCloseDialog = (
event?: React.SyntheticEvent | Event,
reason?: SnackbarCloseReason,
) => {
if (reason === 'clickaway') {
return;
}
setOpenDialog(false);
};
const [detailsLink, setDetailsLink] = React.useState<string>('');

useEffect(() => {
if (openDialog) {
setTimeout(() => {
handleCloseDialog();
setDetailsLink('');
}, 6000);
}
}, [openDialog]);

const submitTest = () => {
testInject(inject.inject_id).then((result: { data: InjectStatus }) => {
setInjectTestResult(result.data);
setOpenDialog(true);
if (isExercise) {
setDetailsLink(`/admin/exercises/${exerciseOrScenarioId}/tests/${result.data.status_id}`);
} else {
setDetailsLink(`/admin/scenarios/${exerciseOrScenarioId}/tests/${result.data.status_id}`);
}
});
handleCloseTest();
};
Expand Down Expand Up @@ -180,6 +229,31 @@ const InjectPopover: FunctionComponent<Props> = ({

return (
<>
<Dialog open={openDialog}
slotProps={{
backdrop: {
sx: {
backgroundColor: 'transparent',
},
},
}}
PaperProps={{
sx: {
position: 'fixed',
top: '20px',
left: '660px',
margin: 0,
},
}}
>
<Alert
onClose={handleCloseDialog}
severity="success"
sx={{ width: '100%' }}
>
{t('Inject test has been sent, you can view test logs details on ')} <Link href={detailsLink} underline="hover">{t('its dedicated page.')}</Link>
</Alert>
</Dialog>
<IconButton
onClick={handlePopoverOpen}
aria-haspopup="true"
Expand Down
Loading

0 comments on commit 26709d2

Please sign in to comment.