-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[backend/frontend] Add atomic testings (#642)
- Loading branch information
1 parent
fdfe1b5
commit 449390a
Showing
123 changed files
with
4,819 additions
and
988 deletions.
There are no files selected for viewing
112 changes: 112 additions & 0 deletions
112
openbas-api/src/main/java/io/openbas/atomic_testing/AtomicTestingApi.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
|
||
} |
92 changes: 92 additions & 0 deletions
92
openbas-api/src/main/java/io/openbas/atomic_testing/AtomicTestingMapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
|
||
} | ||
|
||
} |
170 changes: 170 additions & 0 deletions
170
openbas-api/src/main/java/io/openbas/atomic_testing/AtomicTestingService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
Oops, something went wrong.