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

[backend] fix contextual team while duplicating scenario #1303 #1343

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
44 changes: 37 additions & 7 deletions openbas-api/src/main/java/io/openbas/service/ScenarioService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import io.openbas.config.OpenBASConfig;
import io.openbas.database.criteria.GenericCriteria;
import io.openbas.database.model.*;
import io.openbas.database.raw.RawPaginationScenario;
import io.openbas.database.raw.RawScenario;
Expand Down Expand Up @@ -86,16 +85,18 @@ public class ScenarioService {
private EntityManager entityManager;

private final ScenarioRepository scenarioRepository;
private final GrantService grantService;
private final VariableService variableService;
private final ChallengeService challengeService;
private final DocumentRepository documentRepository;
private final TeamRepository teamRepository;
private final UserRepository userRepository;
private final DocumentRepository documentRepository;
private final ScenarioTeamUserRepository scenarioTeamUserRepository;
private final ArticleRepository articleRepository;

private final GrantService grantService;
private final VariableService variableService;
private final ChallengeService challengeService;
private final TeamService teamService;
private final FileService fileService;
private final InjectDuplicateService injectDuplicateService;
private final ArticleRepository articleRepository;

@Transactional
public Scenario createScenario(@NotNull final Scenario scenario) {
Expand Down Expand Up @@ -485,13 +486,43 @@ public Scenario getDuplicateScenario(@NotBlank String scenarioId) {
Scenario scenario = copyScenario(scenarioOrigin);
Scenario scenarioDuplicate = scenarioRepository.save(scenario);
getListOfDuplicatedInjects(scenarioDuplicate, scenarioOrigin);
getListOfScenarioTeams(scenarioDuplicate, scenarioOrigin);
getListOfArticles(scenarioDuplicate, scenarioOrigin);
getListOfVariables(scenarioDuplicate, scenarioOrigin);
return scenarioRepository.save(scenario);
}
throw new ElementNotFoundException();
}

private void getListOfScenarioTeams(@NotNull Scenario scenario, @NotNull Scenario scenarioOrigin) {
Map<String, Team> contextualTeams = new HashMap<>();
List<Team> scenarioTeams = new ArrayList<>();
scenarioOrigin.getTeams().forEach(scenarioTeam -> {
if (scenarioTeam.getContextual()) {
Team team = teamService.copyContextualTeam(scenarioTeam);
Team teamSaved = this.teamRepository.save(team);
scenarioTeams.add(teamSaved);
contextualTeams.put(scenarioTeam.getId(), teamSaved);
} else {
scenarioTeams.add(scenarioTeam);
}
});
scenario.setTeams(new ArrayList<>(scenarioTeams));

List<Inject> scenarioInjects = scenario.getInjects();
scenarioInjects.forEach(scenarioInject -> {
List<Team> teams = new ArrayList<>();
scenarioInject.getTeams().forEach(team -> {
if (team.getContextual()) {
teams.add(contextualTeams.get(team.getId()));
} else {
teams.add(team);
}
});
scenarioInject.setTeams(teams);
});
}

private Scenario copyScenario(Scenario scenario) {
Scenario scenarioDuplicate = new Scenario();
scenarioDuplicate.setName(duplicateString(scenario.getName()));
Expand All @@ -507,7 +538,6 @@ private Scenario copyScenario(Scenario scenario) {
scenarioDuplicate.setInjects(new HashSet<>(scenario.getInjects()));
scenarioDuplicate.setExternalReference(scenario.getExternalReference());
scenarioDuplicate.setTeamUsers(new ArrayList<>(scenario.getTeamUsers()));
scenarioDuplicate.setTeams(new ArrayList<>(scenario.getTeams()));
scenarioDuplicate.setReplyTos(new ArrayList<>(scenario.getReplyTos()));
scenarioDuplicate.setLessonsCategories(new ArrayList<>(scenario.getLessonsCategories()));
scenarioDuplicate.setObjectives(new ArrayList<>(scenario.getObjectives()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.openbas.database.repository.*;
import io.openbas.injectors.channel.ChannelContract;
import io.openbas.injectors.channel.model.ChannelContent;
import io.openbas.utils.CopyObjectListUtils;
import jakarta.annotation.Nullable;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotBlank;
Expand Down Expand Up @@ -37,6 +38,7 @@ public class ScenarioToExerciseService {
private final LessonsQuestionRepository lessonsQuestionRepository;
private final InjectRepository injectRepository;
private final InjectDocumentRepository injectDocumentRepository;
private final TeamService teamService;
private final VariableService variableService;

@Transactional(rollbackFor = Exception.class)
Expand All @@ -58,7 +60,7 @@ public Exercise toExercise(
exercise.setStart(start);

// Tags
exercise.setTags(copy(scenario.getTags(), Tag.class));
exercise.setTags(CopyObjectListUtils.copy(scenario.getTags(), Tag.class));

Exercise exerciseSaved = this.exerciseRepository.save(exercise);

Expand All @@ -78,17 +80,10 @@ public Exercise toExercise(
Map<String, Team> contextualTeams = new HashMap<>();
scenario.getTeams().forEach(scenarioTeam -> {
if (scenarioTeam.getContextual()) {
Team team = new Team();
team.setName(scenarioTeam.getName());
team.setDescription(scenarioTeam.getDescription());
team.setTags(copy(scenarioTeam.getTags(), Tag.class));
team.setOrganization(scenarioTeam.getOrganization());

team.setUsers(copy(scenarioTeam.getUsers(), User.class));
Team team = teamService.copyContextualTeam(scenarioTeam);
team.setExercises(new ArrayList<>() {{
add(exerciseSaved);
}});
team.setContextual(scenarioTeam.getContextual());
Team teamSaved = this.teamRepository.save(team);
contextualTeams.put(scenarioTeam.getId(), teamSaved);
} else {
Expand Down Expand Up @@ -198,7 +193,7 @@ public Exercise toExercise(
exerciseInject.setDependsDuration(scenarioInject.getDependsDuration());
exerciseInject.setUser(scenarioInject.getUser());
exerciseInject.setStatus(scenarioInject.getStatus().orElse(null));
exerciseInject.setTags(copy(scenarioInject.getTags(), Tag.class));
exerciseInject.setTags(CopyObjectListUtils.copy(scenarioInject.getTags(), Tag.class));
exerciseInject.setContent(scenarioInject.getContent());

// Content
Expand All @@ -223,8 +218,8 @@ public Exercise toExercise(
exerciseInject.setTeams(teams);

// Assets & Asset Groups
exerciseInject.setAssets(copy(scenarioInject.getAssets(), Asset.class));
exerciseInject.setAssetGroups(copy(scenarioInject.getAssetGroups(), AssetGroup.class));
exerciseInject.setAssets(CopyObjectListUtils.copy(scenarioInject.getAssets(), Asset.class));
exerciseInject.setAssetGroups(CopyObjectListUtils.copy(scenarioInject.getAssetGroups(), AssetGroup.class));
Inject injectSaved = this.injectRepository.save(exerciseInject);

// Documents
Expand Down Expand Up @@ -257,35 +252,7 @@ public Exercise toExercise(
return exerciseSaved;
}

private <T extends Base> List<T> copy(@NotNull final List<T> origins, Class<T> clazz) {
List<T> destinations = new ArrayList<>();
origins.forEach(origin -> {
try {
T destination = clazz.getDeclaredConstructor().newInstance();
BeanUtils.copyProperties(destination, origin);
destinations.add(destination);
} catch (IllegalAccessException | InvocationTargetException | InstantiationException |
NoSuchMethodException e) {
throw new RuntimeException(e);
}
});
return destinations;
}

private <T extends Base> Set<T> copy(@NotNull final Set<T> origins, Class<T> clazz) {
Set<T> destinations = new HashSet<>();
origins.forEach(origin -> {
try {
T destination = clazz.getDeclaredConstructor().newInstance();
BeanUtils.copyProperties(destination, origin);
destinations.add(destination);
} catch (IllegalAccessException | InvocationTargetException | InstantiationException |
NoSuchMethodException e) {
throw new RuntimeException(e);
}
});
return destinations;
}

private List<Document> addExerciseToDocuments(
@NotNull final List<Document> origDocuments,
Expand Down
21 changes: 21 additions & 0 deletions openbas-api/src/main/java/io/openbas/service/TeamService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.openbas.service;

import io.openbas.database.model.*;
import io.openbas.utils.CopyObjectListUtils;
import org.springframework.stereotype.Service;

@Service
public class TeamService {

public Team copyContextualTeam(Team teamToCopy) {
Team newTeam = new Team();
newTeam.setName(teamToCopy.getName());
newTeam.setDescription(teamToCopy.getDescription());
newTeam.setTags(CopyObjectListUtils.copy(teamToCopy.getTags(), Tag.class));
newTeam.setOrganization(teamToCopy.getOrganization());
newTeam.setUsers(CopyObjectListUtils.copy(teamToCopy.getUsers(), User.class));
newTeam.setContextual(teamToCopy.getContextual());
return newTeam;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.openbas.utils;

import io.openbas.database.model.Base;
import jakarta.validation.constraints.NotNull;
import org.apache.commons.beanutils.BeanUtils;

import java.lang.reflect.InvocationTargetException;
import java.util.*;

public class CopyObjectListUtils {

Check warning on line 10 in openbas-api/src/main/java/io/openbas/utils/CopyObjectListUtils.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/utils/CopyObjectListUtils.java#L10

Added line #L10 was not covered by tests

public static <T extends Base> List<T> copy(@NotNull final List<T> origins, Class<T> clazz) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice !

List<T> destinations = new ArrayList<>();
return copyCollection(origins, clazz, destinations);
}

public static <T extends Base> Set<T> copy(@NotNull final Set<T> origins, Class<T> clazz) {
Set<T> destinations = new HashSet<>();
return copyCollection(origins, clazz, destinations);
}

public static <T extends Base, C extends Collection<T>> C copyCollection(@NotNull final C origins, Class<T> clazz, C destinations) {
origins.forEach(origin -> {
try {
T destination = clazz.getDeclaredConstructor().newInstance();
BeanUtils.copyProperties(destination, origin);
destinations.add(destination);
} catch (IllegalAccessException | InvocationTargetException | InstantiationException |

Check warning on line 28 in openbas-api/src/main/java/io/openbas/utils/CopyObjectListUtils.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/utils/CopyObjectListUtils.java#L28

Added line #L28 was not covered by tests
NoSuchMethodException e) {
throw new RuntimeException(e);

Check warning on line 30 in openbas-api/src/main/java/io/openbas/utils/CopyObjectListUtils.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/utils/CopyObjectListUtils.java#L30

Added line #L30 was not covered by tests
}
});
return destinations;
}

}
127 changes: 127 additions & 0 deletions openbas-api/src/test/java/io/openbas/service/ScenarioServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package io.openbas.service;

import io.openbas.database.model.*;
import io.openbas.database.repository.*;
import io.openbas.rest.inject.service.InjectDuplicateService;
import jakarta.transaction.Transactional;
import jakarta.validation.constraints.NotBlank;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.*;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;

@SpringBootTest
public class ScenarioServiceTest {

@Autowired
ScenarioRepository scenarioRepository;
@Autowired
private TeamRepository teamRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private DocumentRepository documentRepository;
@Autowired
private ScenarioTeamUserRepository scenarioTeamUserRepository;
@Autowired
private ArticleRepository articleRepository;

@Autowired
InjectRepository injectRepository;

@Mock
GrantService grantService;
@Mock
VariableService variableService;
@Mock
ChallengeService challengeService;
@Autowired
private TeamService teamService;
@Mock
FileService fileService;
@Autowired
private InjectDuplicateService injectDuplicateService;


@InjectMocks
private ScenarioService scenarioService;

@BeforeEach
void setUp() {
scenarioService = new ScenarioService(scenarioRepository, teamRepository, userRepository, documentRepository,
scenarioTeamUserRepository, articleRepository, grantService, variableService, challengeService,
teamService, fileService, injectDuplicateService
);
}

@DisplayName("Should create new contextual teams during scenario duplication")
@Test
@Transactional(rollbackOn = Exception.class)
void createNewContextualTeamsDuringScenarioDuplication(){
// -- PREPARE --
List<Team> scenarioTeams = new ArrayList<>();;
Team contextualTeam = createTeam("fakeTeamName1", true);
scenarioTeams.add(contextualTeam);
Team noContextualTeam = createTeam("fakeTeamName2",false);
scenarioTeams.add(noContextualTeam);

Scenario scenario = createScenario(scenarioTeams);

// -- EXECUTE --
Scenario scenarioDuplicated = scenarioService.getDuplicateScenario(scenario.getId());

// -- ASSERT --
assertNotEquals(scenario.getId(), scenarioDuplicated.getId());
assertEquals(scenario.getFrom(), scenarioDuplicated.getFrom());
assertEquals(2, scenarioDuplicated.getTeams().size());
scenarioDuplicated.getTeams().forEach(team -> {
if (team.getContextual()){
assertNotEquals(contextualTeam.getId(), team.getId());
assertEquals(contextualTeam.getName(), team.getName());
} else {
assertEquals(noContextualTeam.getId(), team.getId());
}
});
assertEquals(1, scenarioDuplicated.getInjects().size());
assertEquals(2, scenario.getInjects().get(0).getTeams().size());
scenarioDuplicated.getInjects().get(0).getTeams().forEach(injectTeam -> {
if (injectTeam.getContextual()){
assertNotEquals(contextualTeam.getId(), injectTeam.getId());
assertEquals(
scenarioDuplicated.getTeams().stream().filter(team -> team.getContextual().equals(true)).findFirst().orElse(new Team()).getId(),
injectTeam.getId()
);
} else {
assertEquals(noContextualTeam.getId(), injectTeam.getId());
}
});
}

private Team createTeam(@NotBlank String name, Boolean isContextualTeam){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can add this in TeamFixture

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes my bad i saw the utils/fixtures folder only this morning i will correct that

Team team = new Team();
team.setName(name);
team.setContextual(isContextualTeam);
return this.teamRepository.save(team);
}

private Scenario createScenario(List<Team> scenarioTeams){
Scenario scenario = new Scenario();
scenario.setName("Scenario name");
scenario.setFrom("[email protected]");
scenario.setTeams(scenarioTeams);
Inject inject = new Inject();
inject.setTeams(scenarioTeams);
Set<Inject> injects = new HashSet<>();
injects.add(this.injectRepository.save(inject));
scenario.setInjects(injects);
return this.scenarioRepository.save(scenario);
}
}
Loading