Skip to content

Commit

Permalink
[backend/frontend] Improv performance on teams (#1409)
Browse files Browse the repository at this point in the history
  • Loading branch information
RomuDeuxfois authored Sep 26, 2024
1 parent 5d42857 commit ee7c842
Show file tree
Hide file tree
Showing 35 changed files with 785 additions and 433 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,14 @@ public Iterable<TeamSimple> getExerciseTeams(@PathVariable String exerciseId) {
public Iterable<Team> addExerciseTeams(
@PathVariable String exerciseId,
@Valid @RequestBody ExerciseUpdateTeamsInput input) {
Exercise exercise = exerciseRepository.findById(exerciseId).orElseThrow(ElementNotFoundException::new);
Exercise exercise = this.exerciseService.exercise(exerciseId);
// Add teams to exercise
List<Team> teams = exercise.getTeams();
List<Team> teamsToAdd = fromIterable(teamRepository.findAllById(input.getTeamIds()));
List<Team> teamsToAdd = fromIterable(this.teamRepository.findAllById(input.getTeamIds()));
List<String> existingTeamIds = teams.stream().map(Team::getId).toList();
teams.addAll(teamsToAdd.stream().filter(t -> !existingTeamIds.contains(t.getId())).toList());
exercise.setTeams(teams);
exercise.setUpdatedAt(now());
this.exerciseService.updateExercise(exercise);
return teamsToAdd;
}

Expand All @@ -229,16 +230,30 @@ public Iterable<Team> addExerciseTeams(
@PreAuthorize("isExercisePlanner(#exerciseId)")
public Iterable<Team> removeExerciseTeams(@PathVariable String exerciseId,
@Valid @RequestBody ExerciseUpdateTeamsInput input) {
Exercise exercise = exerciseRepository.findById(exerciseId).orElseThrow(ElementNotFoundException::new);
Exercise exercise = this.exerciseService.exercise(exerciseId);
// Remove teams from exercise
List<Team> teams = exercise.getTeams().stream().filter(team -> !input.getTeamIds().contains(team.getId())).toList();
exercise.setTeams(fromIterable(teams));
exerciseRepository.save(exercise);
exercise.setTeams(new ArrayList<>(teams));
this.exerciseService.updateExercise(exercise);
// Remove all association between users / exercises / teams
input.getTeamIds().forEach(exerciseTeamUserRepository::deleteTeamFromAllReferences);
return teamRepository.findAllById(input.getTeamIds());
}

@Transactional(rollbackOn = Exception.class)
@PutMapping(EXERCISE_URI + "/{exerciseId}/teams/replace")
@PreAuthorize("isExercisePlanner(#exerciseId)")
public Iterable<Team> replaceExerciseTeams(
@PathVariable String exerciseId,
@Valid @RequestBody ExerciseUpdateTeamsInput input) {
Exercise exercise = this.exerciseService.exercise(exerciseId);
// Replace teams from exercise
List<Team> teams = fromIterable(this.teamRepository.findAllById(input.getTeamIds()));
exercise.setTeams(teams);
this.exerciseService.updateExercise(exercise);
return teams;
}

@Transactional(rollbackOn = Exception.class)
@PutMapping(EXERCISE_URI + "/{exerciseId}/teams/{teamId}/players/enable")
@PreAuthorize("isExercisePlanner(#exerciseId)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
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 Down Expand Up @@ -153,7 +152,6 @@ public Inject injectExecutionCallback(@PathVariable String injectId, @Valid @Req
return injectRepository.save(inject);
}


@GetMapping("/api/injects/try/{injectId}")
public Inject tryInject(@PathVariable String injectId) {
return atomicTestingService.tryInject(injectId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.openbas.rest.exception.ElementNotFoundException;
import io.openbas.rest.exercise.form.LessonsInput;
import io.openbas.rest.exercise.form.ScenarioTeamPlayersEnableInput;
import io.openbas.rest.helper.RestBehavior;
import io.openbas.rest.helper.TeamHelper;
import io.openbas.rest.scenario.form.*;
import io.openbas.service.ImportService;
Expand Down Expand Up @@ -43,7 +44,7 @@
@RestController
@Secured(ROLE_USER)
@RequiredArgsConstructor
public class ScenarioApi {
public class ScenarioApi extends RestBehavior {

public static final String SCENARIO_URI = "/api/scenarios";

Expand Down Expand Up @@ -155,6 +156,14 @@ public void importScenario(@RequestPart("file") @NotNull MultipartFile file) thr

// -- TEAMS --

@GetMapping(SCENARIO_URI + "/{scenarioId}/teams")
@PreAuthorize("isScenarioObserver(#scenarioId)")
public Iterable<TeamSimple> scenarioTeams(@PathVariable @NotBlank final String scenarioId) {
return TeamHelper.rawTeamToSimplerTeam(teamRepository.rawTeamByScenarioId(scenarioId),
injectExpectationRepository, injectRepository, communicationRepository, exerciseTeamUserRepository,
scenarioRepository);
}

@Transactional(rollbackOn = Exception.class)
@PutMapping(SCENARIO_URI + "/{scenarioId}/teams/add")
@PreAuthorize("isScenarioPlanner(#scenarioId)")
Expand All @@ -164,14 +173,6 @@ public Iterable<Team> addScenarioTeams(
return this.scenarioService.addTeams(scenarioId, input.getTeamIds());
}

@GetMapping(SCENARIO_URI + "/{scenarioId}/teams")
@PreAuthorize("isScenarioObserver(#scenarioId)")
public Iterable<TeamSimple> scenarioTeams(@PathVariable @NotBlank final String scenarioId) {
return TeamHelper.rawTeamToSimplerTeam(teamRepository.rawTeamByScenarioId(scenarioId),
injectExpectationRepository, injectRepository, communicationRepository, exerciseTeamUserRepository,
scenarioRepository);
}

@Transactional(rollbackOn = Exception.class)
@PutMapping(SCENARIO_URI + "/{scenarioId}/teams/remove")
@PreAuthorize("isScenarioPlanner(#scenarioId)")
Expand All @@ -181,6 +182,15 @@ public Iterable<Team> removeScenarioTeams(
return this.scenarioService.removeTeams(scenarioId, input.getTeamIds());
}

@Transactional(rollbackOn = Exception.class)
@PutMapping(SCENARIO_URI + "/{scenarioId}/teams/replace")
@PreAuthorize("isScenarioPlanner(#scenarioId)")
public Iterable<Team> replaceScenarioTeams(
@PathVariable @NotBlank final String scenarioId,
@Valid @RequestBody final ScenarioUpdateTeamsInput input) {
return this.scenarioService.replaceTeams(scenarioId, input.getTeamIds());
}

@Transactional(rollbackOn = Exception.class)
@PutMapping(SCENARIO_URI + "/{scenarioId}/teams/{teamId}/players/enable")
@PreAuthorize("isScenarioPlanner(#scenarioId)")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.openbas.rest.team;

import io.openbas.database.model.Team;
import io.openbas.rest.helper.RestBehavior;
import io.openbas.rest.team.output.TeamOutput;
import io.openbas.service.TeamService;
import io.openbas.telemetry.Tracing;
import io.openbas.utils.pagination.SearchPaginationInput;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import static io.openbas.database.model.User.ROLE_USER;
import static io.openbas.database.specification.TeamSpecification.contextual;
import static io.openbas.database.specification.TeamSpecification.fromExercise;
import static io.openbas.rest.exercise.ExerciseApi.EXERCISE_URI;

@RequiredArgsConstructor
@RestController
@Secured(ROLE_USER)
public class ExerciseTeamApi extends RestBehavior {

private final TeamService teamService;

@PostMapping(EXERCISE_URI + "/{exerciseId}/teams/search")
@PreAuthorize("isExerciseObserver(#exerciseId)")
@Transactional(readOnly = true)
@Tracing(name = "Paginate teams for exercise", layer = "api", operation = "POST")
public Page<TeamOutput> searchTeams(
@PathVariable @NotBlank final String exerciseId,
@RequestBody @Valid SearchPaginationInput searchPaginationInput) {
final Specification<Team> teamSpecification = contextual(false).or(fromExercise(exerciseId).and(contextual(true)));
return this.teamService.teamPagination(searchPaginationInput, teamSpecification);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.openbas.rest.team;

import io.openbas.database.model.Team;
import io.openbas.rest.helper.RestBehavior;
import io.openbas.rest.team.output.TeamOutput;
import io.openbas.service.TeamService;
import io.openbas.telemetry.Tracing;
import io.openbas.utils.pagination.SearchPaginationInput;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import static io.openbas.database.model.User.ROLE_USER;
import static io.openbas.database.specification.TeamSpecification.contextual;
import static io.openbas.database.specification.TeamSpecification.fromScenario;
import static io.openbas.rest.scenario.ScenarioApi.SCENARIO_URI;

@RequiredArgsConstructor
@RestController
@Secured(ROLE_USER)
public class ScenarioTeamApi extends RestBehavior {

private final TeamService teamService;

@PostMapping(SCENARIO_URI + "/{scenarioId}/teams/search")
@PreAuthorize("isScenarioObserver(#scenarioId)")
@Transactional(readOnly = true)
@Tracing(name = "Paginate teams for scenario", layer = "api", operation = "POST")
public Page<TeamOutput> teams(
@PathVariable @NotBlank final String scenarioId,
@RequestBody @Valid SearchPaginationInput searchPaginationInput) {
final Specification<Team> teamSpecification = contextual(false).or(fromScenario(scenarioId).and(contextual(true)));
return this.teamService.teamPagination(searchPaginationInput, teamSpecification);
}

}
54 changes: 26 additions & 28 deletions openbas-api/src/main/java/io/openbas/rest/team/TeamApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import io.openbas.database.model.Team;
import io.openbas.database.model.TeamSimple;
import io.openbas.database.model.User;
import io.openbas.database.raw.RawPaginationTeam;
import io.openbas.database.raw.RawTeam;
import io.openbas.database.repository.*;
import io.openbas.rest.exception.AlreadyExistingException;
Expand All @@ -15,12 +14,14 @@
import io.openbas.rest.team.form.TeamCreateInput;
import io.openbas.rest.team.form.TeamUpdateInput;
import io.openbas.rest.team.form.UpdateUsersTeamInput;
import io.openbas.rest.team.output.TeamOutput;
import io.openbas.service.TeamService;
import io.openbas.telemetry.Tracing;
import io.openbas.utils.pagination.SearchPaginationInput;
import jakarta.validation.Valid;
import org.jetbrains.annotations.NotNull;
import jakarta.validation.constraints.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
Expand All @@ -29,16 +30,13 @@

import java.util.List;
import java.util.Optional;
import java.util.function.BiFunction;

import static io.openbas.config.SessionHelper.currentUser;
import static io.openbas.database.model.User.ROLE_USER;
import static io.openbas.database.specification.TeamSpecification.contextual;
import static io.openbas.database.specification.TeamSpecification.teamsAccessibleFromOrganizations;
import static io.openbas.database.specification.TeamSpecification.*;
import static io.openbas.helper.DatabaseHelper.updateRelation;
import static io.openbas.helper.StreamHelper.fromIterable;
import static io.openbas.helper.StreamHelper.iterableToSet;
import static io.openbas.utils.pagination.PaginationUtils.buildPaginationJPA;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static java.time.Instant.now;
Expand All @@ -59,6 +57,8 @@ public class TeamApi extends RestBehavior {
private TagRepository tagRepository;
private ExerciseTeamUserRepository exerciseTeamUserRepository;

private TeamService teamService;

@Autowired
public void setExerciseRepository(ExerciseRepository exerciseRepository) {
this.exerciseRepository = exerciseRepository;
Expand Down Expand Up @@ -109,6 +109,12 @@ public void setTagRepository(TagRepository tagRepository) {
this.tagRepository = tagRepository;
}


@Autowired
public void setTeamService(TeamService teamService) {
this.teamService = teamService;
}

@GetMapping("/api/teams")
@PreAuthorize("isObserver()")
public Iterable<TeamSimple> getTeams() {
Expand All @@ -118,7 +124,7 @@ public Iterable<TeamSimple> getTeams() {
//We get all the teams as raw
teams = fromIterable(teamRepository.rawTeams());
} else {
//We get the teams that are linked to the oragnizations we are part of
//We get the teams that are linked to the organizations we are part of
User local = userRepository.findById(currentUser.getId()).orElseThrow(ElementNotFoundException::new);
List<String> organizationIds = local.getGroups().stream()
.flatMap(group -> group.getOrganizations().stream())
Expand All @@ -134,26 +140,18 @@ public Iterable<TeamSimple> getTeams() {
@PostMapping("/api/teams/search")
@PreAuthorize("isObserver()")
@Transactional(readOnly = true)
public Page<RawPaginationTeam> teams(@RequestBody @Valid SearchPaginationInput searchPaginationInput) {
BiFunction<Specification<Team>, Pageable, Page<Team>> teamsFunction;
OpenBASPrincipal currentUser = currentUser();
if (currentUser.isAdmin()) {
teamsFunction = (Specification<Team> specification, Pageable pageable) -> this.teamRepository
.findAll(contextual(false).and(specification), pageable);
} else {
User local = this.userRepository.findById(currentUser.getId()).orElseThrow(ElementNotFoundException::new);
List<String> organizationIds = local.getGroups().stream()
.flatMap(group -> group.getOrganizations().stream())
.map(Organization::getId)
.toList();
teamsFunction = (Specification<Team> specification, Pageable pageable) -> this.teamRepository
.findAll(contextual(false).and(teamsAccessibleFromOrganizations(organizationIds).and(specification)), pageable);
}
return buildPaginationJPA(
teamsFunction,
searchPaginationInput,
Team.class
).map(RawPaginationTeam::new);
@Tracing(name = "Paginate teams", layer = "api", operation = "POST")
public Page<TeamOutput> searchTeams(@RequestBody @Valid SearchPaginationInput searchPaginationInput) {
final Specification<Team> teamSpecification = contextual(false);
return this.teamService.teamPagination(searchPaginationInput, teamSpecification);
}

@PostMapping("/api/teams/find")
@PreAuthorize("isObserver()")
@Transactional(readOnly = true)
@Tracing(name = "Find teams", layer = "api", operation = "POST")
public List<TeamOutput> findTeams(@RequestBody @Valid @NotNull final List<String> teamIds) {
return this.teamService.find(fromIds(teamIds));
}

@GetMapping("/api/teams/{teamId}")
Expand Down
Loading

0 comments on commit ee7c842

Please sign in to comment.