Skip to content

Commit

Permalink
[backend/frontend] Add atomic testings (#642)
Browse files Browse the repository at this point in the history
  • Loading branch information
savacano28 authored Apr 18, 2024
1 parent fdfe1b5 commit 449390a
Show file tree
Hide file tree
Showing 123 changed files with 4,819 additions and 988 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package io.openbas.atomic_testing;

import io.openbas.atomic_testing.form.AtomicTestingDetailOutput;
import io.openbas.atomic_testing.form.AtomicTestingInput;
import io.openbas.atomic_testing.form.AtomicTestingOutput;
import io.openbas.atomic_testing.form.SimpleExpectationResultOutput;
import io.openbas.database.model.Inject;
import io.openbas.database.model.InjectStatus;
import io.openbas.database.repository.InjectorContractRepository;
import io.openbas.inject_expectation.InjectExpectationService;
import io.openbas.rest.helper.RestBehavior;
import io.openbas.utils.pagination.SearchPaginationInput;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/atomic_testings")
@PreAuthorize("isAdmin()")
public class AtomicTestingApi extends RestBehavior {

private AtomicTestingService atomicTestingService;
private InjectorContractRepository injectorContractRepository;
private InjectExpectationService injectExpectationService;

@Autowired
public void setAtomicTestingService(AtomicTestingService atomicTestingService) {
this.atomicTestingService = atomicTestingService;
}

@Autowired
public void setInjectorContractRepository(InjectorContractRepository injectorContractRepository) {
this.injectorContractRepository = injectorContractRepository;
}

@Autowired
public void setInjectExpectationService(InjectExpectationService injectExpectationService) {
this.injectExpectationService = injectExpectationService;
}

@PostMapping("/search")
public Page<AtomicTestingOutput> findAllAtomicTestings(@RequestBody @Valid final SearchPaginationInput searchPaginationInput) {
return atomicTestingService.findAllAtomicTestings(searchPaginationInput)
// Fixme: find a better way to have Contract inside Atomic object
.map((inject) -> this.injectorContractRepository.findById(inject.getContract()).map((c) -> {
inject.setInjectorContract(c);
return inject;
}).orElse(inject))
.map(AtomicTestingMapper::toDto);
}


@GetMapping("/{injectId}")
public AtomicTestingOutput findAtomicTesting(@PathVariable String injectId) {
return atomicTestingService.findById(injectId)
// Fixme: find a better way to have Contract inside Atomic object
.map((inject) -> this.injectorContractRepository.findById(inject.getContract()).map((c) -> {
inject.setInjectorContract(c);
return inject;
}).orElse(inject))
.map(AtomicTestingMapper::toDtoWithTargetResults)
.orElseThrow();
}

@GetMapping("/{injectId}/detail")
public AtomicTestingDetailOutput findAtomicTestingWithDetail(@PathVariable String injectId) {
return atomicTestingService.findById(injectId).map(AtomicTestingMapper::toDetailDto).orElseThrow();
}

@GetMapping("/{injectId}/update")
public Inject findAtomicTestingForUpdate(@PathVariable String injectId) {
return atomicTestingService.findById(injectId).orElseThrow();
}

@PostMapping()
public AtomicTestingOutput createAtomicTesting(@Valid @RequestBody AtomicTestingInput input) {
return AtomicTestingMapper.toDto(atomicTestingService.createOrUpdate(input, null));
}

@PutMapping("/{injectId}")
public AtomicTestingOutput updateAtomicTesting(
@PathVariable @NotBlank final String injectId,
@Valid @RequestBody final AtomicTestingInput input) {
return AtomicTestingMapper.toDto(atomicTestingService.createOrUpdate(input, injectId));
}

@DeleteMapping("/{injectId}")
public void deleteAtomicTesting(
@PathVariable @NotBlank final String injectId) {
atomicTestingService.deleteAtomicTesting(injectId);
}

@GetMapping("/try/{injectId}")
public InjectStatus tryAtomicTesting(@PathVariable String injectId) {
return atomicTestingService.tryInject(injectId);
}

@GetMapping("/{injectId}/target_results/{targetId}/types/{targetType}")
public List<SimpleExpectationResultOutput> findTargetResult(@PathVariable String targetId,
@PathVariable String injectId, @PathVariable String targetType) {
return injectExpectationService.findExpectationsByInjectAndTargetAndTargetType(injectId, targetId, targetType)
.stream()
.map(expectation -> AtomicTestingMapper.toTargetResultDto(expectation, targetId))
.toList();
}

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

import io.openbas.atomic_testing.form.AtomicTestingDetailOutput;
import io.openbas.atomic_testing.form.AtomicTestingOutput;
import io.openbas.atomic_testing.form.AtomicTestingOutput.AtomicTestingOutputBuilder;
import io.openbas.atomic_testing.form.SimpleExpectationResultOutput;
import io.openbas.database.model.*;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class AtomicTestingMapper {

public static AtomicTestingOutput toDtoWithTargetResults(Inject inject) {
return getAtomicTestingOutputBuilder(inject)
.targets(AtomicTestingUtils.getTargetsWithResults(inject))
.build();
}

public static AtomicTestingOutput toDto(Inject inject) {
return getAtomicTestingOutputBuilder(inject)
.targets(AtomicTestingUtils.getTargets(inject))
.build();
}

private static AtomicTestingOutputBuilder getAtomicTestingOutputBuilder(Inject inject) {
return AtomicTestingOutput
.builder()
.id(inject.getId())
.title(inject.getTitle())
.type(inject.getType())
.injectorContract(inject.getInjectorContract())
.contract(inject.getContract())
.lastExecutionStartDate(inject.getStatus().map(InjectStatus::getTrackingSentDate).orElse(null))
.lastExecutionEndDate(inject.getStatus().map(InjectStatus::getTrackingSentDate).orElse(null))
.status(inject.getStatus().map(InjectStatus::getName).orElse(ExecutionStatus.DRAFT))
.expectationResultByTypes(AtomicTestingUtils.getExpectations(inject.getExpectations()));
}

public static SimpleExpectationResultOutput toTargetResultDto(InjectExpectation injectExpectation, final String targetId) {
return SimpleExpectationResultOutput
.builder()
.id(injectExpectation.getId())
.injectId(injectExpectation.getInject().getId())
.type(ExpectationType.of(injectExpectation.getType().name()))
.targetId(targetId)
.subtype(injectExpectation.getType().name())
.startedAt(injectExpectation.getCreatedAt())
.endedAt(injectExpectation.getUpdatedAt())
.logs(Optional.ofNullable(
injectExpectation.getResults())
.map(results -> results.stream().map(InjectExpectationResult::getResult)
.collect(Collectors.joining(", ")))
.orElse(null))
.response(injectExpectation.getScore() == null ? ExpectationStatus.UNKNOWN : (injectExpectation.getScore() == 0 ? ExpectationStatus.FAILED : ExpectationStatus.VALIDATED))
.build();
}

public static AtomicTestingDetailOutput toDetailDto(Inject inject) {
return inject.getStatus().map(status ->
AtomicTestingDetailOutput
.builder()
.atomicId(inject.getId())
.status(status.getName())
.traces(status.getTraces().stream().map(trace -> trace.getStatus() + " " + trace.getMessage()).collect(Collectors.toList()))
.trackingAckDate(status.getTrackingAckDate())
.trackingSentDate(status.getTrackingSentDate())
.trackingEndDate(status.getTrackingEndDate())
.trackingTotalCount(status.getTrackingTotalCount())
.trackingTotalError(status.getTrackingTotalError())
.trackingTotalSuccess(status.getTrackingTotalSuccess())
.build()
).orElse(AtomicTestingDetailOutput.builder().status(ExecutionStatus.DRAFT).build());

}


public record ExpectationResultsByType(@NotNull ExpectationType type, @NotNull ExpectationStatus avgResult, @NotNull List<ResultDistribution> distribution) {

}

public record ResultDistribution(@NotNull String label, @NotNull Integer value) {

}

public record InjectTargetWithResult(@NotNull TargetType targetType, @NotNull String id, @NotNull String name, @NotNull List<ExpectationResultsByType> expectationResultsByTypes) {

}

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

import static io.openbas.config.SessionHelper.currentUser;
import static io.openbas.helper.StreamHelper.fromIterable;
import static io.openbas.utils.pagination.PaginationUtils.buildPaginationJPA;

import io.openbas.atomic_testing.form.AtomicTestingInput;
import io.openbas.database.model.Inject;
import io.openbas.database.model.InjectDocument;
import io.openbas.database.model.InjectStatus;
import io.openbas.database.model.User;
import io.openbas.database.repository.AssetGroupRepository;
import io.openbas.database.repository.AssetRepository;
import io.openbas.database.repository.DocumentRepository;
import io.openbas.database.repository.InjectDocumentRepository;
import io.openbas.database.repository.InjectRepository;
import io.openbas.database.repository.TagRepository;
import io.openbas.database.repository.TeamRepository;
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.Executor;
import io.openbas.utils.pagination.SearchPaginationInput;
import jakarta.transaction.Transactional;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.Optional;
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.stereotype.Service;

@Service
public class AtomicTestingService {

private Executor executor;
private ExecutionContextService executionContextService;

private AssetGroupRepository assetGroupRepository;

private AssetRepository assetRepository;
private InjectRepository injectRepository;
private InjectDocumentRepository injectDocumentRepository;
private UserRepository userRepository;
private TeamRepository teamRepository;
private TagRepository tagRepository;
private DocumentRepository documentRepository;

@Autowired
public void setExecutor(@NotNull final Executor executor) {
this.executor = executor;
}

@Autowired
public void setExecutionContextService(@NotNull final ExecutionContextService executionContextService) {
this.executionContextService = executionContextService;
}

@Autowired
public void setInjectRepository(@NotNull final InjectRepository injectRepository) {
this.injectRepository = injectRepository;
}

@Autowired
public void setAssetRepository(@NotNull final AssetRepository assetRepository) {
this.assetRepository = assetRepository;
}

@Autowired
public void setAssetGroupRepository(@NotNull final AssetGroupRepository assetGroupRepository) {
this.assetGroupRepository = assetGroupRepository;
}

@Autowired
public void setInjectDocumentRepository(@NotNull final InjectDocumentRepository injectDocumentRepository) {
this.injectDocumentRepository = injectDocumentRepository;
}

@Autowired
public void setUserRepository(@NotNull final UserRepository userRepository) {
this.userRepository = userRepository;
}

@Autowired
public void setTeamRepository(@NotNull final TeamRepository teamRepository) {
this.teamRepository = teamRepository;
}

@Autowired
public void setTagRepository(@NotNull final TagRepository tagRepository) {
this.tagRepository = tagRepository;
}

@Autowired
public void setDocumentRepository(@NotNull final DocumentRepository documentRepository) {
this.documentRepository = documentRepository;
}

public Page<Inject> findAllAtomicTestings(SearchPaginationInput searchPaginationInput) {
return buildPaginationJPA(
(Specification<Inject> specification, Pageable pageable) -> injectRepository.findAllAtomicTestings(specification, pageable),
searchPaginationInput,
Inject.class
);
}

public Optional<Inject> findById(String injectId) {
return injectRepository.findWithStatusById(injectId);
}

@Transactional
public Inject createOrUpdate(AtomicTestingInput input, String injectId) {
Inject injectToSave = new Inject();
if (injectId != null) {
injectToSave = injectRepository.findById(injectId).orElseThrow();
}
injectToSave.setTitle(input.getTitle());
injectToSave.setContent(input.getContent());
injectToSave.setType(input.getType());
injectToSave.setContract(input.getContract());
injectToSave.setAllTeams(input.isAllTeams());
injectToSave.setDescription(input.getDescription());
injectToSave.setDependsDuration(0L);
injectToSave.setUser(userRepository.findById(currentUser().getId()).orElseThrow());
injectToSave.setExercise(null);
// Set dependencies
injectToSave.setDependsOn(null);
injectToSave.setTeams(fromIterable(teamRepository.findAllById(input.getTeams())));
injectToSave.setTags(fromIterable(tagRepository.findAllById(input.getTagIds())));
Inject finalInjectToSave = injectToSave;
List<InjectDocument> injectDocuments = input.getDocuments().stream()
.map(i -> {
InjectDocument injectDocument = new InjectDocument();
injectDocument.setInject(finalInjectToSave);
injectDocument.setDocument(documentRepository.findById(i.getDocumentId()).orElseThrow());
injectDocument.setAttached(i.isAttached());
return injectDocument;
}).toList();
injectToSave.setDocuments(injectDocuments);
injectToSave.setAssets(fromIterable(this.assetRepository.findAllById(input.getAssets())));
injectToSave.setAssetGroups(fromIterable(this.assetGroupRepository.findAllById(input.getAssetGroups())));

return injectRepository.save(injectToSave);
}

@Transactional
public InjectStatus tryInject(String injectId) {
Inject inject = injectRepository.findById(injectId).orElseThrow();
User user = this.userRepository.findById(currentUser().getId()).orElseThrow();

// Reset injects outcome, communications and expectations
inject.clean();

List<ExecutionContext> userInjectContexts = List.of(
this.executionContextService.executionContext(user, inject, "Direct test")
);
ExecutableInject injection = new ExecutableInject(false, true, inject, inject.getTeams(), inject.getAssets(),
inject.getAssetGroups(), userInjectContexts);
// TODO Must be migrated to Atomic approach (Inject duplication and async tracing)
return executor.execute(injection);
}

@Transactional
public void deleteAtomicTesting(String injectId) {
injectDocumentRepository.deleteDocumentsFromInject(injectId);
injectRepository.deleteById(injectId);
}
}
Loading

0 comments on commit 449390a

Please sign in to comment.