Skip to content

Commit 5e2fcfb

Browse files
bastianschafferalexanderkiel
authored andcommitted
Ignore Non-Implemented Filters
Fixes: #293
1 parent 1a7791b commit 5e2fcfb

File tree

2 files changed

+51
-14
lines changed

2 files changed

+51
-14
lines changed

src/main/java/de/medizininformatikinitiative/torch/service/FilterService.java

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.Date;
2121
import java.util.List;
2222
import java.util.Map;
23+
import java.util.Optional;
2324
import java.util.function.Predicate;
2425
import java.util.stream.Collectors;
2526

@@ -37,6 +38,8 @@ public class FilterService {
3738
private final IFhirPath fhirPathEngine;
3839
private final String searchParametersFile;
3940

41+
public static final Predicate<Resource> ALWAYS_TRUE_FILTER = r -> true;
42+
4043
/**
4144
* Maps filter code, filter type and resource type (in this exact order) to an expression.
4245
*/
@@ -79,25 +82,42 @@ public Predicate<Resource> compileFilter(List<Filter> attributeGroupFilters, Str
7982
}
8083
var parsedFilters = attributeGroupFilters.stream()
8184
.map(filter ->
82-
new ParsedFilter(
83-
filter,
84-
parseFhirPath(searchParameters.get(List.of(filter.name(), filter.type(), resourceType)))))
85+
Optional.ofNullable(searchParameters.get(List.of(filter.name(), filter.type(), resourceType)))
86+
.or(() -> {
87+
logger.warn("Could not find search parameter for filter with name '{}' and type '{}' " +
88+
"for resource type '{}'. Ignoring this filter.", filter.name(), filter.type(), resourceType);
89+
return Optional.empty();
90+
})
91+
.map(this::parseFhirPath)
92+
.map(parsed -> new ParsedFilter(filter, parsed))
93+
)
94+
.filter(Optional::isPresent).map(Optional::get)
8595
.toList();
8696

97+
if (parsedFilters.isEmpty()) {
98+
return ALWAYS_TRUE_FILTER;
99+
}
100+
87101
return new CompiledFilter(parsedFilters, fhirPathEngine);
88102
}
89103

104+
/**
105+
* Parses a FHIRPath String into a parsed FHIRPath expression.
106+
*
107+
* @param path the FHIRPath to parse
108+
* @return the parsed FHIRPath expression
109+
* @throws IllegalArgumentException if {@code path} is null, empty or if it could not be parsed
110+
*/
90111
private IFhirPath.IParsedExpression parseFhirPath(String path) {
91112
if (path == null || path.isEmpty()) {
92-
throw new IllegalArgumentException("Could not find any matching search parameter for filter. This can later " +
93-
"result in unexpected false negative results of the 'test' method of 'CompiledFilter'.");
113+
throw new IllegalArgumentException("Path must not be null or empty.");
94114
}
95115

96116
try {
97117
return fhirPathEngine.parse(path);
98118
} catch (Exception e) {
99119
// FHIRPath expressions from FHIR search parameters should usually never fail parsing
100-
throw new RuntimeException("Unexpected parsing error occurred", e);
120+
throw new IllegalArgumentException("Unexpected parsing error occurred with path " + path, e);
101121
}
102122
}
103123

src/test/java/de/medizininformatikinitiative/torch/service/FilterServiceTest.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import ca.uhn.fhir.context.FhirContext;
44
import ca.uhn.fhir.fhirpath.IFhirPath;
5+
import ch.qos.logback.classic.Logger;
6+
import ch.qos.logback.classic.spi.ILoggingEvent;
7+
import ch.qos.logback.core.read.ListAppender;
58
import de.medizininformatikinitiative.torch.exceptions.ReferenceToPatientException;
69
import de.medizininformatikinitiative.torch.model.crtdl.AttributeGroup;
710
import de.medizininformatikinitiative.torch.model.crtdl.Code;
@@ -13,9 +16,12 @@
1316
import org.hl7.fhir.r4.model.Observation;
1417
import org.hl7.fhir.r4.model.Period;
1518
import org.hl7.fhir.r4.model.Questionnaire;
19+
import org.hl7.fhir.r4.model.Resource;
1620
import org.junit.jupiter.api.BeforeAll;
21+
import org.junit.jupiter.api.DisplayName;
1722
import org.junit.jupiter.api.Nested;
1823
import org.junit.jupiter.api.Test;
24+
import org.slf4j.LoggerFactory;
1925

2026
import java.text.ParseException;
2127
import java.text.SimpleDateFormat;
@@ -71,21 +77,32 @@ public void test_compile_MultipleResources() {
7177
assertFalse(result_3);
7278
}
7379

80+
@Test
81+
@DisplayName("This tests for example the case of 'chained filters'.")
82+
public void testInvalidSearchParam() {
83+
Resource anyResource = new Observation();
84+
Logger logger = (Logger) LoggerFactory.getLogger(FilterService.class);
85+
ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
86+
listAppender.start();
87+
logger.addAppender(listAppender);
88+
89+
var compiledFilter = filterService.compileFilter(List.of(new Filter("none-existing-type", "some-code",
90+
List.of(new Code(SYS, CODE_1)))), OBSERVATION);
91+
92+
assertThat(compiledFilter.test(anyResource)).isTrue();
93+
assertThat(listAppender.list.size()).isEqualTo(1);
94+
assertThat((listAppender.list.getFirst()).getFormattedMessage())
95+
.isEqualTo("Could not find search parameter for filter with name 'some-code' and type " +
96+
"'none-existing-type' for resource type 'Observation'. Ignoring this filter.");
97+
}
98+
7499
@Test
75100
public void testEmptyFilter() {
76101
assertThatThrownBy(() -> filterService.compileFilter(List.of(), OBSERVATION))
77102
.isInstanceOf(IllegalArgumentException.class)
78103
.hasMessageContaining("An empty list of filters can't be compiled.");
79104
}
80105

81-
@Test
82-
public void test_nonExistingSearchParam() {
83-
assertThatThrownBy(() -> filterService.compileFilter(List.of(new Filter("foo", "bar", List.of())), OBSERVATION))
84-
.isInstanceOf(RuntimeException.class)
85-
.hasMessageContaining("Could not find any matching search parameter for filter. This can later " +
86-
"result in unexpected false negative results of the 'test' method of 'CompiledFilter'.");
87-
}
88-
89106
@Nested
90107
class TestCodeableConcept {
91108

0 commit comments

Comments
 (0)