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

Release v6.0.1 alpha.1 #407

Merged
merged 13 commits into from
Nov 28, 2024
Merged
9 changes: 4 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [UNRELEASED] - yyyy-mm-dd
## [6.0.1-alpha.1] - 2024-11-28

### Added
### Changed
### Deprecated
### Removed
### Fixed
- Update Sq2CQL
### Security
- Update Spring Boot
- Update Fhir R4 Structures

## [6.0.0] - 2024-10-21

Expand Down
30 changes: 5 additions & 25 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>

<groupId>de.medizininformatik-initiative</groupId>
<artifactId>DataportalBackend</artifactId>
<version>6.0.0</version>
<version>6.0.1-alpha.1</version>

<name>Dataportal Backend</name>
<description>Backend of the Dataportal</description>
Expand All @@ -25,7 +25,7 @@

<properties>
<java.version>17</java.version>
<mockwebserver.version>4.10.0</mockwebserver.version>
<mockwebserver.version>4.12.0</mockwebserver.version>
<okhttp3.version>4.10.0</okhttp3.version>
<ontology-tag>v3.0.0</ontology-tag>
</properties>
Expand Down Expand Up @@ -72,7 +72,6 @@
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<!-- Fix CVE-2023-52428. Remove exclusion and manual inclusion when update is included in spring-boot-starter-oauth2-resource-server -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
Expand All @@ -81,18 +80,6 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
<exclusions>
<exclusion>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.46</version>
</dependency>

<dependency>
Expand Down Expand Up @@ -172,7 +159,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>7.4.5</version>
<version>7.6.0</version>
</dependency>

<dependency>
Expand All @@ -181,19 +168,12 @@
<version>6.4.0</version>
</dependency>

<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.utilities</artifactId>
<version>6.3.26</version>
</dependency>

<dependency>
<groupId>com.bucket4j</groupId>
<artifactId>bucket4j-core</artifactId>
<version>8.10.1</version>
</dependency>


<!--
This is for fixing an RCE vulnerability in commons-text <1.10.0. The dependency comes with the
dsf-fhir-webservice-client which cannot update the transitive dependency itself due to breaking
Expand Down Expand Up @@ -226,7 +206,7 @@
<dependency>
<groupId>de.medizininformatik-initiative</groupId>
<artifactId>sq2cql</artifactId>
<version>0.5.0</version>
<version>0.6.0</version>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
@Slf4j
@JsonSerialize(using = ValidationIssueSerializer.class)
public enum ValidationIssue {
TERMCODE_CONTEXT_COMBINATION_INVALID(20001, "The combination of context and termcode(s) is not found.");
TERMCODE_CONTEXT_COMBINATION_INVALID(20001, "The combination of context and termcode(s) is not found."),
TIMERESTRICTION_INVALID(20002, "The TimeRestriction is invalid. 'beforeDate' must not be before 'afterDate'");

private static final ValidationIssue[] VALUES;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package de.numcodex.feasibility_gui_backend.terminology.es;

import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.InlineScript;
import co.elastic.clients.elasticsearch._types.Script;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.json.JsonData;
import de.numcodex.feasibility_gui_backend.terminology.api.EsSearchResult;
import de.numcodex.feasibility_gui_backend.terminology.api.EsSearchResultEntry;
import de.numcodex.feasibility_gui_backend.terminology.es.model.*;
Expand Down Expand Up @@ -121,10 +119,12 @@ private SearchHits<OntologyListItemDocument> findByNameOrTermcode(String keyword
List<Query> filterTerms = new ArrayList<>();

if (availability) {
var availabilityFilter = new RangeQuery.Builder()
.field("availability")
.gt(JsonData.of("0"))
.build();
var availabilityFilter = RangeQuery.of(r -> r
.number(n -> n
.field("availability")
.gt(0.0)
)
);
filterTerms.add(availabilityFilter._toQuery());
}

Expand Down Expand Up @@ -164,12 +164,8 @@ private SearchHits<OntologyListItemDocument> findByNameOrTermcode(String keyword
.withPageable(pageRequest)
.build();

var inlineScript = new InlineScript.Builder()
.source("doc['availability'].value == 0 ? _score : _score + 100")
.build();

var availabilityScoreScript = new Script.Builder()
.inline(inlineScript)
.source("doc['availability'].value == 0 ? _score : _score + 100")
.build();

var function = FunctionScoreBuilders.scriptScore()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
import de.numcodex.feasibility_gui_backend.common.api.TermCode;
import de.numcodex.feasibility_gui_backend.query.api.MutableStructuredQuery;
import de.numcodex.feasibility_gui_backend.query.api.StructuredQuery;
import de.numcodex.feasibility_gui_backend.query.api.TimeRestriction;
import de.numcodex.feasibility_gui_backend.query.api.status.ValidationIssue;
import de.numcodex.feasibility_gui_backend.terminology.TerminologyService;

import java.time.LocalDate;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -86,6 +89,10 @@ private void annotateCriteria(List<MutableCriterion> criteria, boolean skipValid
criterion.setValidationIssues(List.of(ValidationIssue.TERMCODE_CONTEXT_COMBINATION_INVALID));
continue;
}
if (isTimeRestrictionInvalid(criterion.getTimeRestriction())) {
criterion.setValidationIssues(List.of(ValidationIssue.TIMERESTRICTION_INVALID));
continue;
}
for (TermCode termCode : criterion.getTermCodes()) {
if (terminologyService.isExistingTermCode(termCode.system(), termCode.code())) {
log.trace("termcode ok: {} - {}", termCode.system(), termCode.code());
Expand All @@ -103,6 +110,11 @@ private boolean containsInvalidCriteria(List<Criterion> criteria) {
if (criterion.context() == null) {
return true;
}
if (isTimeRestrictionInvalid(criterion.timeRestriction())) {
log.debug("TimeRestriction invalid. 'beforeDate' ({}) must be after 'afterDate' ({}) but is not",
criterion.timeRestriction().beforeDate(), criterion.timeRestriction().afterDate());
return true;
}
for (TermCode termCode : criterion.termCodes()) {
if (terminologyService.isExistingTermCode(termCode.system(), termCode.code())) {
log.trace("termcode ok: {} - {}", termCode.system(), termCode.code());
Expand All @@ -115,4 +127,12 @@ private boolean containsInvalidCriteria(List<Criterion> criteria) {
}
return false;
}

private boolean isTimeRestrictionInvalid(TimeRestriction timeRestriction) {
// If no timeRestriction is set, it is not invalid
if (timeRestriction == null) {
return false;
}
return LocalDate.parse(timeRestriction.beforeDate()).isBefore(LocalDate.parse(timeRestriction.afterDate()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ private SearchHits<CodeableConceptDocument> createDummySearchHitsPage(int totalH
)
);
}
return new SearchHitsImpl<>(totalHits, TotalHitsRelation.OFF, 10.0F, null, null, searchHitsList, null, null, null);
return new SearchHitsImpl<>(totalHits, TotalHitsRelation.OFF, 10.0F, null, null, null, searchHitsList, null, null, null);
}

private CodeableConceptDocument createDummyCodeableConceptDocument(String id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,6 @@ private SearchHits<OntologyListItemDocument> createDummySearchHitsPage(int total
)
);
}
return new SearchHitsImpl<>(totalHits, TotalHitsRelation.OFF, 10.0F, null, null, searchHitsList, null, null, null);
return new SearchHitsImpl<>(totalHits, TotalHitsRelation.OFF, 10.0F, null, null, null, searchHitsList, null, null, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import de.numcodex.feasibility_gui_backend.common.api.Criterion;
import de.numcodex.feasibility_gui_backend.common.api.TermCode;
import de.numcodex.feasibility_gui_backend.query.api.StructuredQuery;
import de.numcodex.feasibility_gui_backend.query.api.TimeRestriction;
import de.numcodex.feasibility_gui_backend.terminology.TerminologyService;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -62,6 +63,13 @@ void testIsValid_falseOnMissingContext() {
assertFalse(isValid);
}

@Test
void testIsValid_falseOnInvalidTimeRestriction() {
var isValid = structuredQueryValidation.isValid(createStructuredQueryWithInvalidTimeRestriction());

assertFalse(isValid);
}

@ParameterizedTest
@CsvSource({"true,true", "true,false", "false,true", "false,false"})
void testAnnotateStructuredQuery_emptyIssuesOnValidCriteriaOrSkippedValidation(String withExclusionCriteriaString, String skipValidationString) {
Expand Down Expand Up @@ -106,6 +114,18 @@ void testAnnotateStructuredQuery_nonEmptyIssuesOnMissingContext(boolean skipVali
}
}

@ParameterizedTest
@ValueSource(booleans = {true, false})
void testAnnotateStructuredQuery_nonEmptyIssuesOnInvalidTimeRestriction(boolean skipValidation) {
var annotatedStructuredQuery = structuredQueryValidation.annotateStructuredQuery(createStructuredQueryWithInvalidTimeRestriction(), skipValidation);

if (skipValidation) {
assertTrue(annotatedStructuredQuery.inclusionCriteria().get(0).get(0).validationIssues().isEmpty());
} else {
assertFalse(annotatedStructuredQuery.inclusionCriteria().get(0).get(0).validationIssues().isEmpty());
}
}

@NotNull
private static StructuredQuery createValidStructuredQuery(boolean withExclusionCriteria) {
var context = TermCode.builder()
Expand Down Expand Up @@ -150,4 +170,35 @@ private static StructuredQuery createStructuredQueryWithoutContext() {
.display("foo")
.build();
}

@NotNull
private static StructuredQuery createStructuredQueryWithInvalidTimeRestriction() {
var context = TermCode.builder()
.code("Laboruntersuchung")
.system("fdpg.mii.cds")
.display("Laboruntersuchung")
.version("1.0.0")
.build();
var termCode = TermCode.builder()
.code("19113-0")
.system("http://loinc.org")
.display("IgE")
.build();
var timeRestriction = TimeRestriction.builder()
.afterDate("1998-05-09")
.beforeDate("1991-06-15")
.build();
var criterion = Criterion.builder()
.termCodes(List.of(termCode))
.context(context)
.attributeFilters(List.of())
.timeRestriction(timeRestriction)
.build();
return StructuredQuery.builder()
.version(URI.create("http://to_be_decided.com/draft-2/schema#"))
.inclusionCriteria(List.of(List.of(criterion)))
.exclusionCriteria(List.of())
.display("foo")
.build();
}
}
Loading