diff --git a/pom.xml b/pom.xml index 3c7d0d8b..cbcfe9e8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ ca.gc.aafc seqdb.api - 2.19.0 + 2.20 jar seqdb-api @@ -34,7 +34,7 @@ 10.17.0 2.1.0 - 0.129 + 0.134 false diff --git a/src/main/java/ca/gc/aafc/seqdb/api/dto/RunSummaryDto.java b/src/main/java/ca/gc/aafc/seqdb/api/dto/RunSummaryDto.java new file mode 100644 index 00000000..3d563ae6 --- /dev/null +++ b/src/main/java/ca/gc/aafc/seqdb/api/dto/RunSummaryDto.java @@ -0,0 +1,47 @@ +package ca.gc.aafc.seqdb.api.dto; + +import java.util.List; +import java.util.UUID; + +import com.toedter.spring.hateoas.jsonapi.JsonApiId; +import com.toedter.spring.hateoas.jsonapi.JsonApiTypeForClass; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Singular; + + +/** + * Store the summary information about a run and its related information + */ +@AllArgsConstructor +@Getter +@Builder +@JsonApiTypeForClass(RunSummaryDto.TYPENAME) +public class RunSummaryDto { + + public static final String TYPENAME = "run-summary"; + + @JsonApiId + private UUID id; + + private String name; + + @Singular + private List items; + + public record RunSummaryItem(UUID uuid, MolecularAnalysisResultSummary result, GenericMolecularAnalysisItemSummary genericMolecularAnalysisItemSummary) { + } + + public record MolecularAnalysisResultSummary(UUID uuid, String type) { + } + + + public record GenericMolecularAnalysisItemSummary(UUID uuid, String name, GenericMolecularAnalysisSummary genericMolecularAnalysisSummary) { + } + + public record GenericMolecularAnalysisSummary(UUID uuid, String name, String analysisType) { + } + +} diff --git a/src/main/java/ca/gc/aafc/seqdb/api/repository/RunSummaryRepository.java b/src/main/java/ca/gc/aafc/seqdb/api/repository/RunSummaryRepository.java new file mode 100644 index 00000000..ed567901 --- /dev/null +++ b/src/main/java/ca/gc/aafc/seqdb/api/repository/RunSummaryRepository.java @@ -0,0 +1,66 @@ +package ca.gc.aafc.seqdb.api.repository; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.RepresentationModel; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.toedter.spring.hateoas.jsonapi.JsonApiModelBuilder; + +import ca.gc.aafc.dina.filter.FilterExpression; +import ca.gc.aafc.dina.filter.QueryStringParser; +import ca.gc.aafc.dina.filter.QueryComponent; +import ca.gc.aafc.seqdb.api.dto.RunSummaryDto; +import ca.gc.aafc.seqdb.api.service.RunSummaryService; + +import static com.toedter.spring.hateoas.jsonapi.JsonApiModelBuilder.jsonApiModel; +import static com.toedter.spring.hateoas.jsonapi.MediaTypes.JSON_API_VALUE; + +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Objects; +import javax.servlet.http.HttpServletRequest; + +@RestController +@RequestMapping(value = "/api", produces = JSON_API_VALUE) +public class RunSummaryRepository { + + private final RunSummaryService runSummaryService; + + public RunSummaryRepository(RunSummaryService runSummaryService) { + this.runSummaryService = runSummaryService; + } + + @GetMapping(RunSummaryDto.TYPENAME) + public ResponseEntity> handleFindAll(HttpServletRequest req) { + String queryString = decodeQueryString(req); + QueryComponent queryComponents = QueryStringParser.parse(queryString); + + if (queryComponents.getFilterExpression().isEmpty()) { + return ResponseEntity.badRequest().build(); + } + FilterExpression filterExpression = queryComponents.getFilterExpression().get(); + if ("".equals(filterExpression.attribute())) { + return ResponseEntity.badRequest().build(); + } + + List dtos = runSummaryService.findSummary(filterExpression); + JsonApiModelBuilder builder = jsonApiModel().model(CollectionModel.of(dtos)); + + return ResponseEntity.ok(builder.build()); + } + + protected static String decodeQueryString(HttpServletRequest req) { + Objects.requireNonNull(req); + + if (StringUtils.isBlank(req.getQueryString())) { + return ""; + } + return URLDecoder.decode(req.getQueryString(), StandardCharsets.UTF_8); + } + +} diff --git a/src/main/java/ca/gc/aafc/seqdb/api/service/RunSummaryService.java b/src/main/java/ca/gc/aafc/seqdb/api/service/RunSummaryService.java new file mode 100644 index 00000000..298e0fd3 --- /dev/null +++ b/src/main/java/ca/gc/aafc/seqdb/api/service/RunSummaryService.java @@ -0,0 +1,100 @@ +package ca.gc.aafc.seqdb.api.service; + +import org.springframework.stereotype.Service; + +import com.github.tennaito.rsql.misc.ArgumentParser; + +import ca.gc.aafc.dina.filter.DinaFilterArgumentParser; +import ca.gc.aafc.dina.filter.FilterExpression; +import ca.gc.aafc.dina.filter.SimpleFilterHandlerV2; +import ca.gc.aafc.seqdb.api.dto.RunSummaryDto; +import ca.gc.aafc.seqdb.api.entities.GenericMolecularAnalysis; +import ca.gc.aafc.seqdb.api.entities.GenericMolecularAnalysisItem; +import ca.gc.aafc.seqdb.api.entities.MolecularAnalysisRun; +import ca.gc.aafc.seqdb.api.entities.MolecularAnalysisRunItem; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import javax.persistence.criteria.Predicate; +import lombok.NonNull; + +@Service +public class RunSummaryService { + + private static final Set RELATIONSHIPS_TO_LOAD = Set.of( + "genericMolecularAnalysis", "molecularAnalysisRunItem", "molecularAnalysisRunItem.result"); + private static final ArgumentParser RSQL_ARGUMENT_PARSER = new DinaFilterArgumentParser(); + + private final GenericMolecularAnalysisItemService genericMolecularAnalysisItemService; + + public RunSummaryService(@NonNull GenericMolecularAnalysisItemService genericMolecularAnalysisItemService) { + this.genericMolecularAnalysisItemService = genericMolecularAnalysisItemService; + } + + public List findSummary(FilterExpression filterExpression) { + + List entities = genericMolecularAnalysisItemService.findAll( + GenericMolecularAnalysisItem.class, + (criteriaBuilder, root, em) -> { + Predicate restriction = + SimpleFilterHandlerV2.getRestriction(root, criteriaBuilder, RSQL_ARGUMENT_PARSER::parse, + em.getMetamodel(), List.of(filterExpression)); + return new Predicate[] {restriction}; + }, + (cb, root) -> List.of(), 0, 100, Set.of(), RELATIONSHIPS_TO_LOAD); + + // Collect all GenericMolecularAnalysis + Map uniqueGenericMolecularAnalysisRun = new HashMap<>(entities.size()); + for (GenericMolecularAnalysisItem item : entities) { + // start with the MolecularAnalysisRun + if (hasAnalysisRunItemAndRun(item)) { + MolecularAnalysisRun molecularAnalysisRun = item.getMolecularAnalysisRunItem().getRun(); + var builder = + uniqueGenericMolecularAnalysisRun.computeIfAbsent(molecularAnalysisRun.getUuid(), + u -> initBuilder(molecularAnalysisRun)); + builder.item(createRunSummaryItem(item.getMolecularAnalysisRunItem(), item, + item.getGenericMolecularAnalysis())); + } + } + + return uniqueGenericMolecularAnalysisRun.values().stream().map( + RunSummaryDto.RunSummaryDtoBuilder::build).toList(); + } + + /** + * Make sure the {@link GenericMolecularAnalysisItem} has a {@link MolecularAnalysisRunItem} + * and an associated {@link MolecularAnalysisRun} + * @param gmari + * @return + */ + private static boolean hasAnalysisRunItemAndRun(GenericMolecularAnalysisItem gmari) { + return gmari.getMolecularAnalysisRunItem() != null && + gmari.getMolecularAnalysisRunItem().getRun() != null; + } + + private static RunSummaryDto.RunSummaryDtoBuilder initBuilder(MolecularAnalysisRun molecularAnalysisRun) { + return RunSummaryDto.builder() + .id(molecularAnalysisRun.getUuid()) + .name(molecularAnalysisRun.getName()); + } + + private static RunSummaryDto.RunSummaryItem createRunSummaryItem( + MolecularAnalysisRunItem molecularAnalysisRunItem, + GenericMolecularAnalysisItem genericMolecularAnalysisItem, + GenericMolecularAnalysis genericMolecularAnalysis) { + + RunSummaryDto.MolecularAnalysisResultSummary molecularAnalysisResultSummary = + molecularAnalysisRunItem.getResult() != null ? + new RunSummaryDto.MolecularAnalysisResultSummary( + molecularAnalysisRunItem.getResult().getUuid(), "") : null; + + return new RunSummaryDto.RunSummaryItem(molecularAnalysisRunItem.getUuid(), + molecularAnalysisResultSummary, new RunSummaryDto.GenericMolecularAnalysisItemSummary(genericMolecularAnalysisItem.getUuid(), + molecularAnalysisRunItem.getName(), + new RunSummaryDto.GenericMolecularAnalysisSummary(genericMolecularAnalysis.getUuid(), + genericMolecularAnalysis.getName(), genericMolecularAnalysis.getAnalysisType()))); + } +} diff --git a/src/main/java/ca/gc/aafc/seqdb/api/validation/SequenceManagedAttributeValueValidator.java b/src/main/java/ca/gc/aafc/seqdb/api/validation/SequenceManagedAttributeValueValidator.java index 7b2185f7..e48b0545 100644 --- a/src/main/java/ca/gc/aafc/seqdb/api/validation/SequenceManagedAttributeValueValidator.java +++ b/src/main/java/ca/gc/aafc/seqdb/api/validation/SequenceManagedAttributeValueValidator.java @@ -2,7 +2,6 @@ import org.apache.commons.lang3.tuple.Pair; import org.springframework.context.MessageSource; -import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Component; import org.springframework.validation.Errors; @@ -102,9 +101,4 @@ public Object getValue() { return managedAttributeComponent; } } - - // to be replaced by dina-base whne 0.132 will be released - private String getMessageForKey(String key, Object... objects) { - return messageSource.getMessage(key, objects, LocaleContextHolder.getLocale()); - } }