From ac9c08adc761c5a09438e5eb1b6f81bec038c033 Mon Sep 17 00:00:00 2001 From: Hauke Hund Date: Tue, 4 Feb 2020 10:16:04 +0100 Subject: [PATCH 1/4] rev_include work in progress, search param fix for #73 --- .../dao/jdbc/AbstractResourceDaoJdbc.java | 29 +++- .../AbstractStructureDefinitionDaoJdbc.java | 6 +- .../dsf/fhir/dao/jdbc/BinaryDaoJdbc.java | 2 +- .../dsf/fhir/dao/jdbc/BundleDaoJdbc.java | 3 +- .../dsf/fhir/dao/jdbc/CodeSystemDaoJdbc.java | 6 +- .../dsf/fhir/dao/jdbc/EndpointDaoJdbc.java | 4 +- .../dsf/fhir/dao/jdbc/GroupDaoJdbc.java | 2 +- .../dao/jdbc/HealthcareServiceDaoJdbc.java | 5 +- .../dsf/fhir/dao/jdbc/LocationDaoJdbc.java | 3 +- .../fhir/dao/jdbc/NamingSystemDaoJdbc.java | 2 +- .../fhir/dao/jdbc/OrganizationDaoJdbc.java | 4 +- .../dsf/fhir/dao/jdbc/PatientDaoJdbc.java | 4 +- .../fhir/dao/jdbc/PractitionerDaoJdbc.java | 2 +- .../dao/jdbc/PractitionerRoleDaoJdbc.java | 5 +- .../dsf/fhir/dao/jdbc/ProvenanceDaoJdbc.java | 2 +- .../fhir/dao/jdbc/ResearchStudyDaoJdbc.java | 2 +- .../fhir/dao/jdbc/SubscriptionDaoJdbc.java | 5 +- .../dsf/fhir/dao/jdbc/TaskDaoJdbc.java | 4 +- .../dsf/fhir/dao/jdbc/ValueSetDaoJdbc.java | 6 +- .../function/BiConsumerWithSqlException.java | 40 +++++ .../highmed/dsf/fhir/search/IncludeParts.java | 74 ++++++++++ .../highmed/dsf/fhir/search/SearchQuery.java | 138 ++++++++++++++---- .../search/SearchQueryIncludeParameter.java | 111 ++++---------- .../dsf/fhir/search/SearchQueryParameter.java | 5 +- ...SearchQueryRevIncludeParameterFactory.java | 12 ++ .../parameters/EndpointOrganization.java | 9 +- .../parameters/OrganizationEndpoint.java | 6 +- .../parameters/ResearchStudyEnrollment.java | 18 +-- .../fhir/search/parameters/TaskRequester.java | 4 +- .../basic/AbstractReferenceParameter.java | 32 ++-- .../basic/AbstractSearchParameter.java | 13 +- .../AbstractRevIncludeParameterFactory.java | 118 +++++++++++++++ .../EndpointOrganizationRevInclude.java | 26 ++++ .../OrganizationEndpointRevInclude.java | 26 ++++ 34 files changed, 531 insertions(+), 197 deletions(-) create mode 100644 dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/function/BiConsumerWithSqlException.java create mode 100644 dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/IncludeParts.java create mode 100644 dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQueryRevIncludeParameterFactory.java create mode 100644 dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/AbstractRevIncludeParameterFactory.java create mode 100644 dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/EndpointOrganizationRevInclude.java create mode 100644 dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/OrganizationEndpointRevInclude.java diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/AbstractResourceDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/AbstractResourceDaoJdbc.java index 65e372e33..33e08ea83 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/AbstractResourceDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/AbstractResourceDaoJdbc.java @@ -27,6 +27,7 @@ import org.highmed.dsf.fhir.search.SearchQuery; import org.highmed.dsf.fhir.search.SearchQuery.SearchQueryBuilder; import org.highmed.dsf.fhir.search.SearchQueryParameter; +import org.highmed.dsf.fhir.search.SearchQueryRevIncludeParameterFactory; import org.highmed.dsf.fhir.search.parameters.ResourceId; import org.highmed.dsf.fhir.search.parameters.ResourceLastUpdated; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -106,33 +107,40 @@ public Resource getResource() private final String resourceColumn; private final String resourceIdColumn; - private final List>> searchParameterFactories; + private final List>> searchParameterFactories = new ArrayList<>(); + private final List> searchRevIncludeParameterFactories = new ArrayList<>(); private final PreparedStatementFactory preparedStatementFactory; + @SafeVarargs + protected static List with(T... t) + { + return Arrays.asList(t); + } + /* * Using a suppliers for SearchParameters, implementations are not thread safe and because of that they need to be * created on a request basis */ - @SafeVarargs AbstractResourceDaoJdbc(DataSource dataSource, FhirContext fhirContext, Class resourceType, String resourceTable, String resourceColumn, String resourceIdColumn, - Supplier>... searchParameterFactories) + List>> searchParameterFactories, + List> searchRevIncludeParameterFactories) { this(dataSource, fhirContext, resourceType, resourceTable, resourceColumn, resourceIdColumn, new PreparedStatementFactoryDefault<>(fhirContext, resourceType, resourceTable, resourceIdColumn, resourceColumn), - searchParameterFactories); + searchParameterFactories, searchRevIncludeParameterFactories); } /* * Using a suppliers for SearchParameters, implementations are not thread safe and because of that they need to be * created on a request basis */ - @SafeVarargs AbstractResourceDaoJdbc(DataSource dataSource, FhirContext fhirContext, Class resourceType, String resourceTable, String resourceColumn, String resourceIdColumn, PreparedStatementFactory preparedStatementFactory, - Supplier>... searchParameterFactories) + List>> searchParameterFactories, + List> searchRevIncludeParameterFactories) { this.dataSource = dataSource; this.fhirContext = fhirContext; @@ -145,7 +153,10 @@ public Resource getResource() this.preparedStatementFactory = preparedStatementFactory; - this.searchParameterFactories = Arrays.asList(searchParameterFactories); + if (searchParameterFactories != null) + this.searchParameterFactories.addAll(searchParameterFactories); + if (searchRevIncludeParameterFactories != null) + this.searchRevIncludeParameterFactories.addAll(searchRevIncludeParameterFactories); } @Override @@ -789,7 +800,7 @@ public PartialResult searchWithTransaction(Connection connection, DbSearchQue List partialResult = new ArrayList<>(); List includes = new ArrayList<>(); - + if (!query.isCountOnly(overallCount)) // TODO ask db if count 0 { try (PreparedStatement statement = connection.prepareStatement(query.getSearchSql())) @@ -868,6 +879,8 @@ public final SearchQuery createSearchQuery(int page, int count) return SearchQueryBuilder.create(resourceType, getResourceTable(), getResourceColumn(), page, count) .with(new ResourceId(getResourceIdColumn()), new ResourceLastUpdated(getResourceColumn())) .with(searchParameterFactories.stream().map(Supplier::get).toArray(SearchQueryParameter[]::new)) + .withRevInclude(searchRevIncludeParameterFactories.stream().map(Supplier::get) + .toArray(SearchQueryRevIncludeParameterFactory[]::new)) .build(); } } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/AbstractStructureDefinitionDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/AbstractStructureDefinitionDaoJdbc.java index 136345ff7..ae5ac1c6f 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/AbstractStructureDefinitionDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/AbstractStructureDefinitionDaoJdbc.java @@ -28,8 +28,10 @@ public AbstractStructureDefinitionDaoJdbc(BasicDataSource dataSource, FhirContex String resourceColumn, String resourceIdColumn) { super(dataSource, fhirContext, StructureDefinition.class, resourceTable, resourceColumn, resourceIdColumn, - () -> new StructureDefinitionIdentifier(resourceColumn), - () -> new StructureDefinitionUrl(resourceColumn), () -> new StructureDefinitionVersion(resourceColumn)); + with(() -> new StructureDefinitionIdentifier(resourceColumn), + () -> new StructureDefinitionUrl(resourceColumn), + () -> new StructureDefinitionVersion(resourceColumn)), + with()); readByUrl = new ReadByUrlDaoJdbc(this::getDataSource, this::getResource, resourceTable, resourceColumn, resourceIdColumn); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/BinaryDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/BinaryDaoJdbc.java index 78846610a..0b7b5325a 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/BinaryDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/BinaryDaoJdbc.java @@ -18,7 +18,7 @@ public class BinaryDaoJdbc extends AbstractResourceDaoJdbc implements Bi public BinaryDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { super(dataSource, fhirContext, Binary.class, "binaries", "binary_json", "binary_id", - new PreparedStatementFactoryBinary(fhirContext), BinaryContentType::new); + new PreparedStatementFactoryBinary(fhirContext), with(BinaryContentType::new), with()); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/BundleDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/BundleDaoJdbc.java index 73ecbb1a1..0e0306033 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/BundleDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/BundleDaoJdbc.java @@ -15,7 +15,8 @@ public class BundleDaoJdbc extends AbstractResourceDaoJdbc implements Bu { public BundleDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { - super(dataSource, fhirContext, Bundle.class, "bundles", "bundle", "bundle_id", BundleIdentifier::new); + super(dataSource, fhirContext, Bundle.class, "bundles", "bundle", "bundle_id", with(BundleIdentifier::new), + with()); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/CodeSystemDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/CodeSystemDaoJdbc.java index 25cca4d18..61e884b96 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/CodeSystemDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/CodeSystemDaoJdbc.java @@ -19,10 +19,10 @@ public class CodeSystemDaoJdbc extends AbstractResourceDaoJdbc imple public CodeSystemDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { super(dataSource, fhirContext, CodeSystem.class, "code_systems", "code_system", "code_system_id", - CodeSystemUrl::new, CodeSystemVersion::new, CodeSystemIdentifier::new); + with(CodeSystemUrl::new, CodeSystemVersion::new, CodeSystemIdentifier::new), with()); - readByUrl = new ReadByUrlDaoJdbc<>(this::getDataSource, this::getResource, getResourceTable(), getResourceColumn(), - getResourceIdColumn()); + readByUrl = new ReadByUrlDaoJdbc<>(this::getDataSource, this::getResource, getResourceTable(), + getResourceColumn(), getResourceIdColumn()); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/EndpointDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/EndpointDaoJdbc.java index 9b09c57de..bb6d3f0e5 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/EndpointDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/EndpointDaoJdbc.java @@ -11,6 +11,7 @@ import org.highmed.dsf.fhir.search.parameters.EndpointName; import org.highmed.dsf.fhir.search.parameters.EndpointOrganization; import org.highmed.dsf.fhir.search.parameters.EndpointStatus; +import org.highmed.dsf.fhir.search.parameters.rev.include.OrganizationEndpointRevInclude; import org.hl7.fhir.r4.model.Endpoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,7 +25,8 @@ public class EndpointDaoJdbc extends AbstractResourceDaoJdbc implement public EndpointDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { super(dataSource, fhirContext, Endpoint.class, "endpoints", "endpoint", "endpoint_id", - EndpointOrganization::new, EndpointIdentifier::new, EndpointName::new, EndpointStatus::new); + with(EndpointOrganization::new, EndpointIdentifier::new, EndpointName::new, EndpointStatus::new), + with(OrganizationEndpointRevInclude::new)); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/GroupDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/GroupDaoJdbc.java index 4148b628e..5904010d7 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/GroupDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/GroupDaoJdbc.java @@ -10,7 +10,7 @@ public class GroupDaoJdbc extends AbstractResourceDaoJdbc implements Grou { public GroupDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { - super(dataSource, fhirContext, Group.class, "groups", "group_json", "group_id"); + super(dataSource, fhirContext, Group.class, "groups", "group_json", "group_id", with(), with()); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/HealthcareServiceDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/HealthcareServiceDaoJdbc.java index 37c47f2e3..3ecb8640f 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/HealthcareServiceDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/HealthcareServiceDaoJdbc.java @@ -8,13 +8,12 @@ import ca.uhn.fhir.context.FhirContext; -public class HealthcareServiceDaoJdbc extends AbstractResourceDaoJdbc - implements HealthcareServiceDao +public class HealthcareServiceDaoJdbc extends AbstractResourceDaoJdbc implements HealthcareServiceDao { public HealthcareServiceDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { super(dataSource, fhirContext, HealthcareService.class, "healthcare_services", "healthcare_service", - "healthcare_service_id", HealthcareServiceIdentifier::new, HealthcareServiceActive::new); + "healthcare_service_id", with(HealthcareServiceIdentifier::new, HealthcareServiceActive::new), with()); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/LocationDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/LocationDaoJdbc.java index 6d6c75106..712dd43b0 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/LocationDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/LocationDaoJdbc.java @@ -11,7 +11,8 @@ public class LocationDaoJdbc extends AbstractResourceDaoJdbc implement { public LocationDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { - super(dataSource, fhirContext, Location.class, "locations", "location", "location_id", LocationIdentifier::new); + super(dataSource, fhirContext, Location.class, "locations", "location", "location_id", + with(LocationIdentifier::new), with()); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/NamingSystemDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/NamingSystemDaoJdbc.java index d50a935ab..3f3eb33bf 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/NamingSystemDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/NamingSystemDaoJdbc.java @@ -12,7 +12,7 @@ public class NamingSystemDaoJdbc extends AbstractResourceDaoJdbc i public NamingSystemDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { super(dataSource, fhirContext, NamingSystem.class, "naming_systems", "naming_system", "naming_system_id", - NamingSystemName::new); + with(NamingSystemName::new), with()); } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/OrganizationDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/OrganizationDaoJdbc.java index 894f6e9a2..694acc169 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/OrganizationDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/OrganizationDaoJdbc.java @@ -26,7 +26,9 @@ public class OrganizationDaoJdbc extends AbstractResourceDaoJdbc i public OrganizationDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { super(dataSource, fhirContext, Organization.class, "organizations", "organization", "organization_id", - OrganizationName::new, OrganizationEndpoint::new, OrganizationIdentifier::new, OrganizationActive::new, OrganizationType::new); + with(OrganizationName::new, OrganizationEndpoint::new, OrganizationIdentifier::new, + OrganizationActive::new, OrganizationType::new), + with()); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/PatientDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/PatientDaoJdbc.java index 74fbd702c..c5c0bfbcf 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/PatientDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/PatientDaoJdbc.java @@ -12,8 +12,8 @@ public class PatientDaoJdbc extends AbstractResourceDaoJdbc implements { public PatientDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { - super(dataSource, fhirContext, Patient.class, "patients", "patient", "patient_id", PatientIdentifier::new, - PatientActive::new); + super(dataSource, fhirContext, Patient.class, "patients", "patient", "patient_id", + with(PatientIdentifier::new, PatientActive::new), with()); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/PractitionerDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/PractitionerDaoJdbc.java index c9eb45380..1dd82998b 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/PractitionerDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/PractitionerDaoJdbc.java @@ -13,7 +13,7 @@ public class PractitionerDaoJdbc extends AbstractResourceDaoJdbc i public PractitionerDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { super(dataSource, fhirContext, Practitioner.class, "practitioners", "practitioner", "practitioner_id", - PractitionerIdentifier::new, PractitionerActive::new); + with(PractitionerIdentifier::new, PractitionerActive::new), with()); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/PractitionerRoleDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/PractitionerRoleDaoJdbc.java index 81bdbfcf6..f7b5b9488 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/PractitionerRoleDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/PractitionerRoleDaoJdbc.java @@ -8,13 +8,12 @@ import ca.uhn.fhir.context.FhirContext; -public class PractitionerRoleDaoJdbc extends AbstractResourceDaoJdbc - implements PractitionerRoleDao +public class PractitionerRoleDaoJdbc extends AbstractResourceDaoJdbc implements PractitionerRoleDao { public PractitionerRoleDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { super(dataSource, fhirContext, PractitionerRole.class, "practitioner_roles", "practitioner_role", - "practitioner_role_id", PractitionerRoleIdentifier::new, PractitionerRoleActive::new); + "practitioner_role_id", with(PractitionerRoleIdentifier::new, PractitionerRoleActive::new), with()); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/ProvenanceDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/ProvenanceDaoJdbc.java index 708750c1e..e35d40919 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/ProvenanceDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/ProvenanceDaoJdbc.java @@ -10,7 +10,7 @@ public class ProvenanceDaoJdbc extends AbstractResourceDaoJdbc imple { public ProvenanceDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { - super(dataSource, fhirContext, Provenance.class, "provenances", "provenance", "provenance_id"); + super(dataSource, fhirContext, Provenance.class, "provenances", "provenance", "provenance_id", with(), with()); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/ResearchStudyDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/ResearchStudyDaoJdbc.java index d3e116ab8..27dcd73fb 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/ResearchStudyDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/ResearchStudyDaoJdbc.java @@ -13,7 +13,7 @@ public class ResearchStudyDaoJdbc extends AbstractResourceDaoJdbc public ResearchStudyDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { super(dataSource, fhirContext, ResearchStudy.class, "research_studies", "research_study", "research_study_id", - ResearchStudyIdentifier::new, ResearchStudyEnrollment::new); + with(ResearchStudyIdentifier::new, ResearchStudyEnrollment::new), with()); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/SubscriptionDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/SubscriptionDaoJdbc.java index 2891da95d..f4c1bf3a2 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/SubscriptionDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/SubscriptionDaoJdbc.java @@ -27,8 +27,9 @@ public class SubscriptionDaoJdbc extends AbstractResourceDaoJdbc i public SubscriptionDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { super(dataSource, fhirContext, Subscription.class, "subscriptions", "subscription", "subscription_id", - SubscriptionCriteria::new, SubscriptionStatus::new, SubscriptionChannelType::new, - SubscriptionChannelPayload::new); + with(SubscriptionCriteria::new, SubscriptionStatus::new, SubscriptionChannelType::new, + SubscriptionChannelPayload::new), + with()); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/TaskDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/TaskDaoJdbc.java index 33a09d591..71963c714 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/TaskDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/TaskDaoJdbc.java @@ -13,8 +13,8 @@ public class TaskDaoJdbc extends AbstractResourceDaoJdbc implements TaskDa { public TaskDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { - super(dataSource, fhirContext, Task.class, "tasks", "task", "task_id", TaskIdentifier::new, TaskRequester::new, - TaskStatus::new); + super(dataSource, fhirContext, Task.class, "tasks", "task", "task_id", + with(TaskIdentifier::new, TaskRequester::new, TaskStatus::new), with()); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/ValueSetDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/ValueSetDaoJdbc.java index 0e0080012..56f2bb3af 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/ValueSetDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/ValueSetDaoJdbc.java @@ -19,10 +19,10 @@ public class ValueSetDaoJdbc extends AbstractResourceDaoJdbc implement public ValueSetDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { super(dataSource, fhirContext, ValueSet.class, "value_sets", "value_set", "value_set_id", - ValueSetIdentifier::new, ValueSetUrl::new, ValueSetVersion::new); + with(ValueSetIdentifier::new, ValueSetUrl::new, ValueSetVersion::new), with()); - readByUrl = new ReadByUrlDaoJdbc<>(this::getDataSource, this::getResource, getResourceTable(), getResourceColumn(), - getResourceIdColumn()); + readByUrl = new ReadByUrlDaoJdbc<>(this::getDataSource, this::getResource, getResourceTable(), + getResourceColumn(), getResourceIdColumn()); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/function/BiConsumerWithSqlException.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/function/BiConsumerWithSqlException.java new file mode 100644 index 000000000..a008bb88b --- /dev/null +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/function/BiConsumerWithSqlException.java @@ -0,0 +1,40 @@ +package org.highmed.dsf.fhir.function; + +import java.sql.SQLException; +import java.util.Objects; + +@FunctionalInterface +public interface BiConsumerWithSqlException +{ + void accept(T t, U u) throws SQLException; + + default BiConsumerWithSqlException andThen(BiConsumerWithSqlException after) + { + Objects.requireNonNull(after); + return (T t, U u) -> + { + SQLException suppressed = null; + try + { + accept(t, u); + } + catch (SQLException e) + { + suppressed = e; + } + finally + { + try + { + after.accept(t, u); + } + catch (SQLException e) + { + if (suppressed != null) + e.addSuppressed(suppressed); + throw e; + } + } + }; + } +} \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/IncludeParts.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/IncludeParts.java new file mode 100644 index 000000000..abe4c7368 --- /dev/null +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/IncludeParts.java @@ -0,0 +1,74 @@ +package org.highmed.dsf.fhir.search; + +public class IncludeParts +{ + private final String sourceResourceTypeName; + private final String searchParameterName; + private final String targetResourceTypeName; + + public IncludeParts(String sourceResourceTypeName, String searchParameterName, String targetResourceTypeName) + { + this.sourceResourceTypeName = sourceResourceTypeName; + this.searchParameterName = searchParameterName; + this.targetResourceTypeName = targetResourceTypeName; + } + + public static IncludeParts fromString(String includeParameterValue) + { + if (includeParameterValue == null || includeParameterValue.isBlank()) + return new IncludeParts(null, null, null); + else + { + String[] parts = includeParameterValue.split(":"); + + String sourceResourceTypeName = null, searchParameterName = null, targetResourceTypeName = null; + if (parts.length > 0) + sourceResourceTypeName = parts[0]; + if (parts.length > 1) + searchParameterName = parts[1]; + if (parts.length > 2) + targetResourceTypeName = parts[2]; + + return new IncludeParts(sourceResourceTypeName, searchParameterName, targetResourceTypeName); + } + } + + public String toBundleUriQueryParameterValue() + { + return getSourceResourceTypeName() + ":" + getSearchParameterName() + + (getTargetResourceTypeName() != null ? (":" + getTargetResourceTypeName()) : ""); + } + + public String getSourceResourceTypeName() + { + return sourceResourceTypeName; + } + + public String getSearchParameterName() + { + return searchParameterName; + } + + public String getTargetResourceTypeName() + { + return targetResourceTypeName; + } + + public boolean matches(String resourceTypeName, String parameterName, String targetResourceTypeName) + { + return resourceTypeName.equals(getSourceResourceTypeName()) + && parameterName.equals(getSearchParameterName()) && (getTargetResourceTypeName() == null + || targetResourceTypeName.equals(getTargetResourceTypeName())); + } + + @Override + public String toString() + { + if (searchParameterName == null && targetResourceTypeName == null) + return sourceResourceTypeName; + else if (targetResourceTypeName == null) + return sourceResourceTypeName + ":" + searchParameterName; + else + return sourceResourceTypeName + ":" + searchParameterName + ":" + targetResourceTypeName; + } +} \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQuery.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQuery.java index ce33ea8df..9ddc3dd02 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQuery.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQuery.java @@ -27,13 +27,14 @@ public class SearchQuery implements DbSearchQuery, Matcher { public static final String PARAMETER_SORT = "_sort"; public static final String PARAMETER_INCLUDE = "_include"; + public static final String PARAMETER_REVINCLUDE = "_revinclude"; public static final String PARAMETER_PAGE = "_page"; public static final String PARAMETER_COUNT = "_count"; public static final String PARAMETER_FORMAT = "_format"; public static final String PARAMETER_PRETTY = "_pretty"; - public static final String[] STANDARD_PARAMETERS = { PARAMETER_SORT, PARAMETER_INCLUDE, PARAMETER_PAGE, - PARAMETER_COUNT, PARAMETER_FORMAT, PARAMETER_PRETTY }; + public static final String[] STANDARD_PARAMETERS = { PARAMETER_SORT, PARAMETER_INCLUDE, PARAMETER_REVINCLUDE, + PARAMETER_PAGE, PARAMETER_COUNT, PARAMETER_FORMAT, PARAMETER_PRETTY }; public static class SearchQueryBuilder { @@ -50,6 +51,7 @@ public static SearchQueryBuilder create(Class resourc private final int count; private final List> searchParameters = new ArrayList>(); + private final List revIncludeParameters = new ArrayList<>(); private SearchQueryBuilder(Class resourceType, String resourceTable, String resourceColumn, int page, int count) @@ -78,9 +80,27 @@ public SearchQueryBuilder with(List> searchParameters return this; } + public SearchQueryBuilder withRevInclude(SearchQueryRevIncludeParameterFactory searchParameters) + { + this.revIncludeParameters.add(searchParameters); + return this; + } + + public SearchQueryBuilder withRevInclude(SearchQueryRevIncludeParameterFactory... searchParameters) + { + return withRevInclude(Arrays.asList(searchParameters)); + } + + public SearchQueryBuilder withRevInclude(List searchParameters) + { + this.revIncludeParameters.addAll(searchParameters); + return this; + } + public SearchQuery build() { - return new SearchQuery(resourceType, resourceTable, resourceColumn, page, count, searchParameters); + return new SearchQuery(resourceType, resourceTable, resourceColumn, page, count, searchParameters, + revIncludeParameters); } } @@ -90,22 +110,27 @@ public SearchQuery build() private final String resourceColumn; private final String resourceTable; private final List> searchParameters = new ArrayList<>(); + private final List revIncludeParameterFactories = new ArrayList<>(); private final PageAndCount pageAndCount; - private String filterQuery = ""; - private String sortSql = ""; - private String includeSql = ""; + private String filterQuery; + private String sortSql; + private String includeSql; + private String revIncludeSql; private List> sortParameters = Collections.emptyList(); - private List> includeParameters = Collections.emptyList(); + private List includeParameters = Collections.emptyList(); + private List revIncludeParameters = Collections.emptyList(); SearchQuery(Class resourceType, String resourceTable, String resourceColumn, int page, int count, - List> searchParameters) + List> searchParameters, + List revIncludeParameters) { this.resourceType = resourceType; this.resourceTable = resourceTable; this.resourceColumn = resourceColumn; this.searchParameters.addAll(searchParameters); + this.revIncludeParameterFactories.addAll(revIncludeParameters); this.pageAndCount = new PageAndCount(page, count); } @@ -113,11 +138,16 @@ public void configureParameters(Map> queryParameters) { searchParameters.forEach(p -> p.configure(queryParameters)); + List revIncludeParameterValues = queryParameters.getOrDefault(PARAMETER_REVINCLUDE, + Collections.emptyList()); + revIncludeParameterFactories.forEach(p -> p.configure(revIncludeParameterValues)); + filterQuery = searchParameters.stream().filter(SearchQueryParameter::isDefined) .map(SearchQueryParameter::getFilterQuery).collect(Collectors.joining(" AND ")); - createSortSql(getFirst(queryParameters, PARAMETER_SORT)); - createIncludeSql(queryParameters.get(PARAMETER_INCLUDE)); + sortSql = createSortSql(getFirst(queryParameters, PARAMETER_SORT)); + includeSql = createIncludeSql(queryParameters.get(PARAMETER_INCLUDE)); + revIncludeSql = createRevIncludeSql(); } public List getUnsupportedQueryParameters(Map> queryParameters) @@ -133,10 +163,20 @@ public List getUnsupportedQueryParameters(Map p.getErrors().stream()).forEach(errors::add); + revIncludeParameterFactories.stream().flatMap(p -> p.getErrors().stream()).forEach(errors::add); + + List revIncludeParameterValues = new ArrayList<>( + queryParameters.getOrDefault(PARAMETER_REVINCLUDE, Collections.emptyList())); + + revIncludeParameters.stream().map(SearchQueryIncludeParameter::getBundleUriQueryParameterValues) + .forEach(v -> revIncludeParameterValues.remove(v)); + if (!revIncludeParameterValues.isEmpty()) + errors.add(new SearchQueryParameterError(SearchQueryParameterErrorType.UNSUPPORTED_PARAMETER, + PARAMETER_REVINCLUDE, revIncludeParameterValues)); if (!errors.isEmpty()) logger.warn("Query parameters with error: {}", errors); - + return errors; } @@ -148,7 +188,8 @@ private List getDuplicateStandardParameters(Map values = queryParameters.get(parameter); if (values != null && values.size() > 1) { - if (!PARAMETER_INCLUDE.equals(parameter) || hasDuplicates(values)) + if ((!PARAMETER_INCLUDE.equals(parameter) && !PARAMETER_REVINCLUDE.equals(parameter)) + || hasDuplicates(values)) errors.add(new SearchQueryParameterError(SearchQueryParameterErrorType.UNSUPPORTED_NUMBER_OF_VALUES, parameter, values)); } @@ -169,33 +210,48 @@ private String getFirst(Map> queryParameters, String key) return null; } - private void createSortSql(String sortParameterValue) + private String createSortSql(String sortParameterValue) { if (sortParameterValue == null) - return; + return ""; sortParameters = searchParameters.stream().filter(sp -> sp.getSortParameter().isPresent()) .collect(Collectors.toList()); if (sortParameters.isEmpty()) - return; + return ""; - sortSql = sortParameters.stream().map(sp -> sp.getSortParameter().get().getSql()) + return sortParameters.stream().map(sp -> sp.getSortParameter().get().getSql()) .collect(Collectors.joining(", ", " ORDER BY ", "")); } - private void createIncludeSql(List includeParameterValues) + private String createIncludeSql(List includeParameterValues) { if (includeParameterValues == null || includeParameterValues.isEmpty()) - return; + return ""; - includeParameters = searchParameters.stream().filter(sp -> sp.getIncludeParameter().isPresent()) + includeParameters = searchParameters.stream().flatMap(sp -> sp.getIncludeParameters().stream()) .collect(Collectors.toList()); if (includeParameters.isEmpty()) - return; + return ""; - includeSql = includeParameters.stream().map(sp -> sp.getIncludeParameter().get().getSql()) + return includeParameters.stream().map(SearchQueryIncludeParameter::getSql) + .collect(Collectors.joining(", ", ", ", "")); + } + + private String createRevIncludeSql() + { + if (revIncludeParameterFactories == null || revIncludeParameterFactories.isEmpty()) + return ""; + + revIncludeParameters = revIncludeParameterFactories.stream().flatMap(f -> f.getRevIncludeParameters().stream()) + .collect(Collectors.toList()); + + if (revIncludeParameters.isEmpty()) + return ""; + + return revIncludeParameters.stream().map(SearchQueryIncludeParameter::getSql) .collect(Collectors.joining(", ", ", ", "")); } @@ -210,7 +266,8 @@ public String getCountSql() @Override public String getSearchSql() { - String searchQueryMain = "SELECT " + resourceColumn + includeSql + " FROM current_" + resourceTable; + String searchQueryMain = "SELECT " + resourceColumn + includeSql + revIncludeSql + " FROM current_" + + resourceTable; return searchQueryMain + (!filterQuery.isEmpty() ? (" WHERE " + filterQuery) : "") + sortSql + pageAndCount.sql(); @@ -259,6 +316,8 @@ public UriBuilder configureBundleUri(UriBuilder bundleUri) bundleUri.replaceQueryParam(PARAMETER_SORT, sortParameter()); if (!includeParameters.isEmpty()) bundleUri.replaceQueryParam(PARAMETER_INCLUDE, includeParameters()); + if (!revIncludeParameterFactories.isEmpty()) + bundleUri.replaceQueryParam(PARAMETER_REVINCLUDE, revIncludeParameters()); return bundleUri; } @@ -271,7 +330,12 @@ private String sortParameter() private Object[] includeParameters() { - return includeParameters.stream().flatMap(p -> p.getIncludeParameter().get().getBundleUriQueryParameterValues()) + return includeParameters.stream().map(SearchQueryIncludeParameter::getBundleUriQueryParameterValues).toArray(); + } + + private Object[] revIncludeParameters() + { + return revIncludeParameters.stream().map(SearchQueryIncludeParameter::getBundleUriQueryParameterValues) .toArray(); } @@ -326,14 +390,28 @@ public boolean matches(Resource resource) @Override public void modifyIncludeResource(Resource resource, int columnIndex, Connection connection) throws SQLException { - if (columnIndex - 1 > includeParameters.size()) + int includeParameterCount = includeParameters.size(); + int revIncludeParameterCount = revIncludeParameters.size(); + + if (includeParameterCount > 0 && columnIndex - 1 <= includeParameterCount) { - logger.warn("Unexpected column-index {}, column-index - 1 larger than include parameter count {}", - columnIndex, includeParameters.size()); - throw new IllegalStateException("Unexpected column-index " + columnIndex - + ", column-index - 1 larger than include parameter count " + includeParameters.size()); + includeParameters.get(columnIndex - 2).modifyIncludeResource(resource, connection); + } + else if (revIncludeParameters.size() > 0 && columnIndex - 1 - includeParameterCount <= revIncludeParameterCount) + { + revIncludeParameters.get(columnIndex - 2 - includeParameterCount).modifyIncludeResource(resource, + connection); + } + else + { + logger.warn( + "Unexpected column-index {}, column-index - 1 larger than include ({}) + revinclude ({}) parameter count {}", + columnIndex, includeParameterCount, revIncludeParameterCount, + includeParameterCount + revIncludeParameterCount); + throw new IllegalStateException( + "Unexpected column-index " + columnIndex + ", column-index - 1 larger than include (" + + includeParameterCount + ") + revinclude (" + revIncludeParameterCount + + ") parameter count " + (includeParameterCount + revIncludeParameterCount)); } - - includeParameters.get(columnIndex - 2).modifyIncludeResource(resource, connection); } } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQueryIncludeParameter.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQueryIncludeParameter.java index e8c4c7d80..5321cf994 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQueryIncludeParameter.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQueryIncludeParameter.java @@ -1,100 +1,53 @@ package org.highmed.dsf.fhir.search; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.sql.Connection; +import java.sql.SQLException; + +import org.highmed.dsf.fhir.function.BiConsumerWithSqlException; +import org.hl7.fhir.r4.model.Resource; public class SearchQueryIncludeParameter { - public static class IncludeParts - { - private final String sourceResourceTypeName; - private final String searchParameterName; - private final String targetResourceTypeName; - - public IncludeParts(String sourceResourceTypeName, String searchParameterName, String targetResourceTypeName) - { - this.sourceResourceTypeName = sourceResourceTypeName; - this.searchParameterName = searchParameterName; - this.targetResourceTypeName = targetResourceTypeName; - } - - public static IncludeParts fromString(String includeParameterValue) - { - if (includeParameterValue == null || includeParameterValue.isBlank()) - return new IncludeParts(null, null, null); - else - { - String[] parts = includeParameterValue.split(":"); - - String sourceResourceTypeName = null, searchParameterName = null, targetResourceTypeName = null; - if (parts.length > 0) - sourceResourceTypeName = parts[0]; - if (parts.length > 1) - searchParameterName = parts[1]; - if (parts.length > 2) - targetResourceTypeName = parts[2]; - - return new IncludeParts(sourceResourceTypeName, searchParameterName, targetResourceTypeName); - } - } - - public String toBundleUriQueryParameterValue() - { - return getSourceResourceTypeName() + ":" + getSearchParameterName() - + (getTargetResourceTypeName() != null ? (":" + getTargetResourceTypeName()) : ""); - } - - public String getSourceResourceTypeName() - { - return sourceResourceTypeName; - } + private final String sql; + private final IncludeParts includeParts; - public String getSearchParameterName() - { - return searchParameterName; - } + private final BiConsumerWithSqlException includeResourceModifier; - public String getTargetResourceTypeName() - { - return targetResourceTypeName; - } - - public boolean matches(String resourceTypeName, String parameterName, String targetResourceTypeName) - { - return resourceTypeName.equals(getSourceResourceTypeName()) - && parameterName.equals(getSearchParameterName()) && (getTargetResourceTypeName() == null - || targetResourceTypeName.equals(getTargetResourceTypeName())); - } - - @Override - public String toString() - { - if (searchParameterName == null && targetResourceTypeName == null) - return sourceResourceTypeName; - else if (targetResourceTypeName == null) - return sourceResourceTypeName + ":" + searchParameterName; - else - return sourceResourceTypeName + ":" + searchParameterName + ":" + targetResourceTypeName; - } + public SearchQueryIncludeParameter(String sql, IncludeParts includeParts) + { + this(sql, includeParts, null); } - private final List sql; - private final List includeParts; - - public SearchQueryIncludeParameter(List sql, List includeParts) + /** + * @param sql + * @param includeParts + * @param includeResourceModifier + * Use this {@link BiConsumerWithSqlException} to modify the include resources. This consumer can be used + * if the resources returned by the include SQL are not complete and additional content needs to be + * retrieved from a not included column. For example the content of a {@link Binary} resource might not + * be stored in the json column. + */ + public SearchQueryIncludeParameter(String sql, IncludeParts includeParts, + BiConsumerWithSqlException includeResourceModifier) { this.sql = sql; this.includeParts = includeParts; + this.includeResourceModifier = includeResourceModifier; } - public Stream getBundleUriQueryParameterValues() + public String getBundleUriQueryParameterValues() { - return includeParts.stream().map(IncludeParts::toBundleUriQueryParameterValue); + return includeParts.toBundleUriQueryParameterValue(); } public String getSql() { - return sql.stream().collect(Collectors.joining(", ")); + return sql; + } + + public void modifyIncludeResource(Resource resource, Connection connection) throws SQLException + { + if (includeResourceModifier != null) + includeResourceModifier.accept(resource, connection); } } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQueryParameter.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQueryParameter.java index acf19d47e..a6c237e96 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQueryParameter.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQueryParameter.java @@ -6,7 +6,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.sql.Array; -import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List; @@ -59,11 +58,9 @@ void modifyStatement(int parameterIndex, int subqueryParameterIndex, PreparedSta Optional getSortParameter(); - Optional getIncludeParameter(); + List getIncludeParameters(); String getParameterName(); Stream getBaseAndModifiedParameterNames(); - - void modifyIncludeResource(Resource resource, Connection connection); } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQueryRevIncludeParameterFactory.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQueryRevIncludeParameterFactory.java new file mode 100644 index 000000000..ec64fd450 --- /dev/null +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQueryRevIncludeParameterFactory.java @@ -0,0 +1,12 @@ +package org.highmed.dsf.fhir.search; + +import java.util.List; + +public interface SearchQueryRevIncludeParameterFactory +{ + void configure(List revIncludeParameterValues); + + List getErrors(); + + List getRevIncludeParameters(); +} diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/EndpointOrganization.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/EndpointOrganization.java index 61233f2ac..3eb93c1ab 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/EndpointOrganization.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/EndpointOrganization.java @@ -10,7 +10,7 @@ import org.highmed.dsf.fhir.dao.exception.ResourceDeletedException; import org.highmed.dsf.fhir.dao.provider.DaoProvider; import org.highmed.dsf.fhir.function.BiFunctionWithSqlException; -import org.highmed.dsf.fhir.search.SearchQueryIncludeParameter.IncludeParts; +import org.highmed.dsf.fhir.search.IncludeParts; import org.highmed.dsf.fhir.search.SearchQueryParameter.SearchParameterDefinition; import org.highmed.dsf.fhir.search.parameters.basic.AbstractIdentifierParameter; import org.highmed.dsf.fhir.search.parameters.basic.AbstractReferenceParameter; @@ -182,17 +182,14 @@ protected String getSortSql(String sortDirectionWithSpacePrefix) @Override protected String getIncludeSql(IncludeParts includeParts) { - if (RESOURCE_TYPE_NAME.equals(includeParts.getSourceResourceTypeName()) - && PARAMETER_NAME.equals(includeParts.getSearchParameterName()) - && (includeParts.getTargetResourceTypeName() == null - || TARGET_RESOURCE_TYPE_NAME.equals(includeParts.getTargetResourceTypeName()))) + if (includeParts.matches(RESOURCE_TYPE_NAME, PARAMETER_NAME, TARGET_RESOURCE_TYPE_NAME)) return "(SELECT jsonb_build_array(organization) FROM current_organizations WHERE concat('Organization/', organization->>'id') = endpoint->'managingOrganization'->>'reference') AS organizations"; else return null; } @Override - protected void doModifyIncludeResource(Resource resource, Connection connection) + protected void modifyIncludeResource(IncludeParts includeParts, Resource resource, Connection connection) { // Nothing to do for organizations } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/OrganizationEndpoint.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/OrganizationEndpoint.java index f29d0060c..ebe95df27 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/OrganizationEndpoint.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/OrganizationEndpoint.java @@ -10,7 +10,7 @@ import org.highmed.dsf.fhir.dao.exception.ResourceDeletedException; import org.highmed.dsf.fhir.dao.provider.DaoProvider; import org.highmed.dsf.fhir.function.BiFunctionWithSqlException; -import org.highmed.dsf.fhir.search.SearchQueryIncludeParameter.IncludeParts; +import org.highmed.dsf.fhir.search.IncludeParts; import org.highmed.dsf.fhir.search.SearchQueryParameter.SearchParameterDefinition; import org.highmed.dsf.fhir.search.parameters.basic.AbstractIdentifierParameter; import org.highmed.dsf.fhir.search.parameters.basic.AbstractReferenceParameter; @@ -154,7 +154,7 @@ public boolean matches(Resource resource) } else { - return o.getEndpoint().stream().map(e -> e.getReference()).anyMatch(ref -> + return o.getEndpoint().stream().map(Reference::getReference).anyMatch(ref -> { switch (valueAndType.type) { @@ -189,7 +189,7 @@ protected String getIncludeSql(IncludeParts includeParts) } @Override - protected void doModifyIncludeResource(Resource resource, Connection connection) + protected void modifyIncludeResource(IncludeParts includeParts, Resource resource, Connection connection) { // Nothing to do for endpoints } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/ResearchStudyEnrollment.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/ResearchStudyEnrollment.java index 8f7226426..060e78526 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/ResearchStudyEnrollment.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/ResearchStudyEnrollment.java @@ -10,7 +10,7 @@ import org.highmed.dsf.fhir.dao.exception.ResourceDeletedException; import org.highmed.dsf.fhir.dao.provider.DaoProvider; import org.highmed.dsf.fhir.function.BiFunctionWithSqlException; -import org.highmed.dsf.fhir.search.SearchQueryIncludeParameter; +import org.highmed.dsf.fhir.search.IncludeParts; import org.highmed.dsf.fhir.search.SearchQueryParameter; import org.highmed.dsf.fhir.search.parameters.basic.AbstractIdentifierParameter; import org.highmed.dsf.fhir.search.parameters.basic.AbstractReferenceParameter; @@ -21,9 +21,7 @@ import org.hl7.fhir.r4.model.ResearchStudy; import org.hl7.fhir.r4.model.Resource; -@SearchQueryParameter.SearchParameterDefinition(name = ResearchStudyEnrollment.PARAMETER_NAME, - definition = "http://highmed.org/fhir/SearchParameter/research-study-enrollment", - type = Enumerations.SearchParamType.REFERENCE, documentation = "Search by research study enrollment") +@SearchQueryParameter.SearchParameterDefinition(name = ResearchStudyEnrollment.PARAMETER_NAME, definition = "http://highmed.org/fhir/SearchParameter/research-study-enrollment", type = Enumerations.SearchParamType.REFERENCE, documentation = "Search by research study enrollment") public class ResearchStudyEnrollment extends AbstractReferenceParameter { public static final String PARAMETER_NAME = "enrollment"; @@ -96,9 +94,8 @@ public void modifyStatement(int parameterIndex, int subqueryParameterIndex, Prep "[{\"value\": \"" + valueAndType.identifier.codeValue + "\"}]"); break; case CODE_AND_SYSTEM: - statement.setString(parameterIndex, - "[{\"value\": \"" + valueAndType.identifier.codeValue + "\", \"system\": \"" - + valueAndType.identifier.systemValue + "\"}]"); + statement.setString(parameterIndex, "[{\"value\": \"" + valueAndType.identifier.codeValue + + "\", \"system\": \"" + valueAndType.identifier.systemValue + "\"}]"); break; case CODE_AND_NO_SYSTEM_PROPERTY: statement.setString(parameterIndex, valueAndType.identifier.codeValue); @@ -158,7 +155,8 @@ public boolean matches(Resource resource) } else { - return rs.getEnrollment().stream().map(Reference::getResource).anyMatch(ref -> { + return rs.getEnrollment().stream().map(Reference::getReference).anyMatch(ref -> + { switch (valueAndType.type) { case ID: @@ -181,7 +179,7 @@ protected String getSortSql(String sortDirectionWithSpacePrefix) } @Override - protected String getIncludeSql(SearchQueryIncludeParameter.IncludeParts includeParts) + protected String getIncludeSql(IncludeParts includeParts) { if (includeParts.matches(RESOURCE_TYPE_NAME, PARAMETER_NAME, TARGET_RESOURCE_TYPE_NAME)) return "(SELECT jsonb_agg(group_json) FROM current_groups" @@ -192,7 +190,7 @@ protected String getIncludeSql(SearchQueryIncludeParameter.IncludeParts includeP } @Override - protected void doModifyIncludeResource(Resource resource, Connection connection) + protected void modifyIncludeResource(IncludeParts includeParts, Resource resource, Connection connection) { // Nothing to do for groups } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/TaskRequester.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/TaskRequester.java index 3f8de09db..efcb92bd0 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/TaskRequester.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/TaskRequester.java @@ -11,7 +11,7 @@ import org.highmed.dsf.fhir.dao.exception.ResourceDeletedException; import org.highmed.dsf.fhir.dao.provider.DaoProvider; import org.highmed.dsf.fhir.function.BiFunctionWithSqlException; -import org.highmed.dsf.fhir.search.SearchQueryIncludeParameter.IncludeParts; +import org.highmed.dsf.fhir.search.IncludeParts; import org.highmed.dsf.fhir.search.SearchQueryParameter.SearchParameterDefinition; import org.highmed.dsf.fhir.search.parameters.basic.AbstractIdentifierParameter; import org.highmed.dsf.fhir.search.parameters.basic.AbstractReferenceParameter; @@ -259,7 +259,7 @@ protected String getIncludeSql(IncludeParts includeParts) } @Override - protected void doModifyIncludeResource(Resource resource, Connection connection) + protected void modifyIncludeResource(IncludeParts includeParts, Resource resource, Connection connection) { // Nothing to do for practitioners, organizations, patients or practitioner-roles } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/basic/AbstractReferenceParameter.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/basic/AbstractReferenceParameter.java index f1911b28f..9e57f08bf 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/basic/AbstractReferenceParameter.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/basic/AbstractReferenceParameter.java @@ -15,9 +15,9 @@ import javax.ws.rs.core.UriBuilder; import org.highmed.dsf.fhir.dao.provider.DaoProvider; +import org.highmed.dsf.fhir.search.IncludeParts; import org.highmed.dsf.fhir.search.SearchQuery; import org.highmed.dsf.fhir.search.SearchQueryIncludeParameter; -import org.highmed.dsf.fhir.search.SearchQueryIncludeParameter.IncludeParts; import org.highmed.dsf.fhir.search.SearchQueryParameterError; import org.highmed.dsf.fhir.search.SearchQueryParameterError.SearchQueryParameterErrorType; import org.hl7.fhir.r4.model.Binary; @@ -124,7 +124,7 @@ else if (identifierParam != null && identifierParam.isBlank()) private final String resourceTypeName; private final List targetResourceTypeNames; - private SearchQueryIncludeParameter includeParameter; + private final List includeParameters = new ArrayList<>(); protected ReferenceValueAndSearchType valueAndType; @@ -156,11 +156,12 @@ protected void configureIncludeParameter(Map> queryParamete { List includeParts = getIncludeParts(queryParameters); - if (!includeParts.isEmpty()) + for (IncludeParts ip : includeParts) { - List includeSqls = includeParts.stream().map(this::getIncludeSql).filter(s -> s != null) - .collect(Collectors.toList()); - includeParameter = new SearchQueryIncludeParameter(includeSqls, includeParts); + String includeSql = getIncludeSql(ip); + if (includeSql != null) + includeParameters.add(new SearchQueryIncludeParameter(includeSql, ip, + (resource, connection) -> modifyIncludeResource(ip, resource, connection))); } } @@ -181,12 +182,14 @@ private List getIncludeParts(Map> queryParame SearchQuery.PARAMETER_INCLUDE, includeParameterValues, "Non matching include parameter" + (nonMatchingIncludeParameters.size() != 1 ? "s " : " ") + nonMatchingIncludeParameters)); - return includeParameterValues.stream().map(IncludeParts::fromString) + List includeParts = includeParameterValues.stream().map(IncludeParts::fromString) .filter(p -> resourceTypeName.equals(p.getSourceResourceTypeName()) && parameterName.equals(p.getSearchParameterName()) && ((targetResourceTypeNames.size() == 1 && p.getTargetResourceTypeName() == null) || targetResourceTypeNames.contains(p.getTargetResourceTypeName()))) .collect(Collectors.toList()); + + return includeParts; } @Override @@ -238,9 +241,9 @@ public void modifyBundleUri(UriBuilder bundleUri) } @Override - public Optional getIncludeParameter() + public List getIncludeParameters() { - return Optional.ofNullable(includeParameter); + return Collections.unmodifiableList(includeParameters); } protected abstract String getIncludeSql(IncludeParts includeParts); @@ -254,21 +257,18 @@ public void resolveReferencesForMatching(Resource resource, DaoProvider daoProvi protected abstract void doResolveReferencesForMatching(R resource, DaoProvider daoProvider) throws SQLException; - @Override - public void modifyIncludeResource(Resource resource, Connection connection) - { - doModifyIncludeResource(resource, connection); - } - /** * Use this method to modify the include resources. This method can be used if the resources returned by the include * SQL are not complete and additional content needs to be retrieved from a not included column. For example the * content of a {@link Binary} resource might not be stored in the json column. * + * @param includeParts + * not null * @param resource * not null * @param connection * not null */ - protected abstract void doModifyIncludeResource(Resource resource, Connection connection); + protected abstract void modifyIncludeResource(IncludeParts includeParts, Resource resource, + Connection connection); } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/basic/AbstractSearchParameter.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/basic/AbstractSearchParameter.java index d3b81905d..a358f1916 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/basic/AbstractSearchParameter.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/basic/AbstractSearchParameter.java @@ -1,8 +1,8 @@ package org.highmed.dsf.fhir.search.parameters.basic; -import java.sql.Connection; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; @@ -66,7 +66,7 @@ public final void configure(Map> queryParameters) @Override public List getErrors() { - return errors; + return Collections.unmodifiableList(errors); } protected final void addError(SearchQueryParameterError error) @@ -110,13 +110,8 @@ public Optional getSortParameter() protected abstract String getSortSql(String sortDirectionWithSpacePrefix); @Override - public Optional getIncludeParameter() - { - return Optional.empty(); - } - - @Override - public void modifyIncludeResource(Resource resource, Connection connection) + public List getIncludeParameters() { + return Collections.emptyList(); } } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/AbstractRevIncludeParameterFactory.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/AbstractRevIncludeParameterFactory.java new file mode 100644 index 000000000..709816672 --- /dev/null +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/AbstractRevIncludeParameterFactory.java @@ -0,0 +1,118 @@ +package org.highmed.dsf.fhir.search.parameters.rev.include; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.highmed.dsf.fhir.search.IncludeParts; +import org.highmed.dsf.fhir.search.SearchQuery; +import org.highmed.dsf.fhir.search.SearchQueryIncludeParameter; +import org.highmed.dsf.fhir.search.SearchQueryParameterError; +import org.highmed.dsf.fhir.search.SearchQueryParameterError.SearchQueryParameterErrorType; +import org.highmed.dsf.fhir.search.SearchQueryRevIncludeParameterFactory; +import org.hl7.fhir.r4.model.Binary; +import org.hl7.fhir.r4.model.Resource; + +public abstract class AbstractRevIncludeParameterFactory implements SearchQueryRevIncludeParameterFactory +{ + private final List errors = new ArrayList<>(); + + /** + * The name of the source resource from which the join comes + */ + private final String resourceTypeName; + /** + * The name of the search parameter which must be of type reference + */ + private final String parameterName; + /** + * (Optional) A specific of type of target resource (for when the search parameter refers to multiple possible + * target types) + */ + private final List targetResourceTypeNames; + + private List includeParts; + + /** + * @param resourceTypeName + * The name of the source resource from which the join comes + * @param parameterName + * The name of the search parameter which must be of type reference + * @param targetResourceTypeName + * target resource type + */ + public AbstractRevIncludeParameterFactory(String resourceTypeName, String parameterName, + String... targetResourceTypeName) + { + this.resourceTypeName = resourceTypeName; + this.parameterName = parameterName; + this.targetResourceTypeNames = Arrays.asList(targetResourceTypeName); + } + + @Override + public void configure(List revIncludeParameters) + { + includeParts = getRevIncludeParts(revIncludeParameters); + } + + private List getRevIncludeParts(List revIncludeParameterValues) + { + List nonMatchingRevIncludeParameters = revIncludeParameterValues.stream().map(IncludeParts::fromString) + .filter(p -> !resourceTypeName.equals(p.getSourceResourceTypeName()) + || !parameterName.equals(p.getSearchParameterName()) + || !((targetResourceTypeNames.size() == 1 && p.getTargetResourceTypeName() == null) + || targetResourceTypeNames.contains(p.getTargetResourceTypeName()))) + .map(IncludeParts::toString).collect(Collectors.toList()); + + if (!nonMatchingRevIncludeParameters.isEmpty()) + addError(new SearchQueryParameterError(SearchQueryParameterErrorType.UNPARSABLE_VALUE, + SearchQuery.PARAMETER_REVINCLUDE, revIncludeParameterValues, + "Non matching revinclude parameter" + (nonMatchingRevIncludeParameters.size() != 1 ? "s " : " ") + + nonMatchingRevIncludeParameters)); + + List includeParts = revIncludeParameterValues.stream().map(IncludeParts::fromString) + .filter(p -> resourceTypeName.equals(p.getSourceResourceTypeName()) + && parameterName.equals(p.getSearchParameterName()) + && ((targetResourceTypeNames.size() == 1 && p.getTargetResourceTypeName() == null) + || targetResourceTypeNames.contains(p.getTargetResourceTypeName()))) + .collect(Collectors.toList()); + + return includeParts; + } + + protected final void addError(SearchQueryParameterError error) + { + errors.add(error); + } + + @Override + public List getErrors() + { + return Collections.unmodifiableList(errors); + } + + protected abstract String getRevIncludeSql(IncludeParts includeParts); + + /** + * Use this method to modify the include resources. This method can be used if the resources returned by the include + * SQL are not complete and additional content needs to be retrieved from a not included column. For example the + * content of a {@link Binary} resource might not be stored in the json column. + * + * @param resource + * not null + * @param connection + * not null + */ + protected abstract void modifyIncludeResource(Resource resource, Connection connection); + + @Override + public List getRevIncludeParameters() + { + return includeParts.stream() + .map(ip -> new SearchQueryIncludeParameter(getRevIncludeSql(ip), ip, this::modifyIncludeResource)) + .collect(Collectors.toList()); + } +} diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/EndpointOrganizationRevInclude.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/EndpointOrganizationRevInclude.java new file mode 100644 index 000000000..596f1d910 --- /dev/null +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/EndpointOrganizationRevInclude.java @@ -0,0 +1,26 @@ +package org.highmed.dsf.fhir.search.parameters.rev.include; + +import java.sql.Connection; + +import org.highmed.dsf.fhir.search.IncludeParts; +import org.hl7.fhir.r4.model.Resource; + +public class EndpointOrganizationRevInclude extends AbstractRevIncludeParameterFactory +{ + public EndpointOrganizationRevInclude() + { + super("Endpoint", "organization", "Organization"); + } + + @Override + protected String getRevIncludeSql(IncludeParts includeParts) + { + return "(SELECT jsonb_build_array(organization) FROM current_organizations WHERE concat('Organization/', organization->>'id') = endpoint->'managingOrganization'->>'reference') AS organizations"; + } + + @Override + protected void modifyIncludeResource(Resource resource, Connection connection) + { + // Nothing to do for organizations + } +} diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/OrganizationEndpointRevInclude.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/OrganizationEndpointRevInclude.java new file mode 100644 index 000000000..515121ba8 --- /dev/null +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/OrganizationEndpointRevInclude.java @@ -0,0 +1,26 @@ +package org.highmed.dsf.fhir.search.parameters.rev.include; + +import java.sql.Connection; + +import org.highmed.dsf.fhir.search.IncludeParts; +import org.hl7.fhir.r4.model.Resource; + +public class OrganizationEndpointRevInclude extends AbstractRevIncludeParameterFactory +{ + public OrganizationEndpointRevInclude() + { + super("Organization", "endpoint", "Endpoint"); + } + + @Override + protected String getRevIncludeSql(IncludeParts includeParts) + { + return "(SELECT jsonb_build_array(organization) FROM current_organizations WHERE organization->'endpoint' @> concat('[{\"reference\": \"Endpoint/', endpoint->>'id', '\"}]')::jsonb) AS organizations"; + } + + @Override + protected void modifyIncludeResource(Resource resource, Connection connection) + { + // Nothing to do for organizations + } +} From a49f04f5392be2f362bd0e591b419ce507d1bcb1 Mon Sep 17 00:00:00 2001 From: Hauke Hund Date: Wed, 5 Feb 2020 01:53:59 +0100 Subject: [PATCH 2/4] more work on rev include and some code formating --- .../org/highmed/dsf/fhir/dao/jdbc/OrganizationDaoJdbc.java | 3 ++- .../dsf/fhir/search/parameters/EndpointOrganization.java | 3 +-- .../dsf/fhir/search/parameters/OrganizationEndpoint.java | 4 +--- .../rev/include/EndpointOrganizationRevInclude.java | 2 +- .../rev/include/OrganizationEndpointRevInclude.java | 2 +- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/OrganizationDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/OrganizationDaoJdbc.java index 694acc169..fcc465179 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/OrganizationDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/OrganizationDaoJdbc.java @@ -13,6 +13,7 @@ import org.highmed.dsf.fhir.search.parameters.OrganizationIdentifier; import org.highmed.dsf.fhir.search.parameters.OrganizationName; import org.highmed.dsf.fhir.search.parameters.OrganizationType; +import org.highmed.dsf.fhir.search.parameters.rev.include.EndpointOrganizationRevInclude; import org.hl7.fhir.r4.model.Organization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,7 +29,7 @@ public OrganizationDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) super(dataSource, fhirContext, Organization.class, "organizations", "organization", "organization_id", with(OrganizationName::new, OrganizationEndpoint::new, OrganizationIdentifier::new, OrganizationActive::new, OrganizationType::new), - with()); + with(EndpointOrganizationRevInclude::new)); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/EndpointOrganization.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/EndpointOrganization.java index 3eb93c1ab..2987bf942 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/EndpointOrganization.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/EndpointOrganization.java @@ -24,9 +24,8 @@ @SearchParameterDefinition(name = EndpointOrganization.PARAMETER_NAME, definition = "http://hl7.org/fhir/SearchParameter/Endpoint.managingOrganization", type = SearchParamType.REFERENCE, documentation = "The organization that is managing the endpoint, search by identifier is supported") public class EndpointOrganization extends AbstractReferenceParameter { - public static final String PARAMETER_NAME = "organization"; - private static final String RESOURCE_TYPE_NAME = "Endpoint"; + public static final String PARAMETER_NAME = "organization"; private static final String TARGET_RESOURCE_TYPE_NAME = "Organization"; private static final String ORGANIZATION_IDENTIFIERS_SUBQUERY = "(SELECT organization->'identifier' FROM current_organizations" diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/OrganizationEndpoint.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/OrganizationEndpoint.java index ebe95df27..61f63b722 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/OrganizationEndpoint.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/OrganizationEndpoint.java @@ -181,9 +181,7 @@ protected String getSortSql(String sortDirectionWithSpacePrefix) protected String getIncludeSql(IncludeParts includeParts) { if (includeParts.matches(RESOURCE_TYPE_NAME, PARAMETER_NAME, TARGET_RESOURCE_TYPE_NAME)) - return "(SELECT jsonb_agg(endpoint) FROM current_endpoints" - + " WHERE concat('Endpoint/', endpoint->>'id') IN (SELECT reference->>'reference' FROM jsonb_array_elements(organization->'endpoint') AS reference)" - + ") AS endpoints"; + return "(SELECT jsonb_agg(endpoint) FROM current_endpoints WHERE concat('Endpoint/', endpoint->>'id') IN (SELECT reference->>'reference' FROM jsonb_array_elements(organization->'endpoint') AS reference)) AS endpoints"; else return null; } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/EndpointOrganizationRevInclude.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/EndpointOrganizationRevInclude.java index 596f1d910..d2b85b73e 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/EndpointOrganizationRevInclude.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/EndpointOrganizationRevInclude.java @@ -15,7 +15,7 @@ public EndpointOrganizationRevInclude() @Override protected String getRevIncludeSql(IncludeParts includeParts) { - return "(SELECT jsonb_build_array(organization) FROM current_organizations WHERE concat('Organization/', organization->>'id') = endpoint->'managingOrganization'->>'reference') AS organizations"; + return "(SELECT jsonb_build_array(endpoint) FROM current_endpoints WHERE endpoint->'managingOrganization'->>'reference' = concat('Organization/', organization->>'id')) AS endpoints"; } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/OrganizationEndpointRevInclude.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/OrganizationEndpointRevInclude.java index 515121ba8..39057c6fc 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/OrganizationEndpointRevInclude.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/OrganizationEndpointRevInclude.java @@ -15,7 +15,7 @@ public OrganizationEndpointRevInclude() @Override protected String getRevIncludeSql(IncludeParts includeParts) { - return "(SELECT jsonb_build_array(organization) FROM current_organizations WHERE organization->'endpoint' @> concat('[{\"reference\": \"Endpoint/', endpoint->>'id', '\"}]')::jsonb) AS organizations"; + return "(SELECT jsonb_agg(organization) FROM current_organizations WHERE organization->'endpoint' @> concat('[{\"reference\": \"Endpoint/', endpoint->>'id', '\"}]')::jsonb) AS organizations"; } @Override From d723dc7ea8912f0a1028d46bf0e9df5f9ad5062b Mon Sep 17 00:00:00 2001 From: Hauke Hund Date: Thu, 6 Feb 2020 01:31:31 +0100 Subject: [PATCH 3/4] revincludes in CapabilityStatement, ResearchStudy:enrollment revinclude --- .../dsf/fhir/dao/jdbc/GroupDaoJdbc.java | 4 ++- ...SearchQueryRevIncludeParameterFactory.java | 19 ++++++++++++ .../parameters/ResearchStudyEnrollment.java | 3 +- .../fhir/search/parameters/TaskRequester.java | 3 +- .../EndpointOrganizationRevInclude.java | 5 +++ .../OrganizationEndpointRevInclude.java | 5 +++ .../ResearchStudyEnrollmentRevInclude.java | 31 +++++++++++++++++++ .../impl/ConformanceServiceImpl.java | 30 +++++++++++++++++- 8 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/ResearchStudyEnrollmentRevInclude.java diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/GroupDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/GroupDaoJdbc.java index 5904010d7..eba2ec585 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/GroupDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/dao/jdbc/GroupDaoJdbc.java @@ -2,6 +2,7 @@ import org.apache.commons.dbcp2.BasicDataSource; import org.highmed.dsf.fhir.dao.GroupDao; +import org.highmed.dsf.fhir.search.parameters.rev.include.ResearchStudyEnrollmentRevInclude; import org.hl7.fhir.r4.model.Group; import ca.uhn.fhir.context.FhirContext; @@ -10,7 +11,8 @@ public class GroupDaoJdbc extends AbstractResourceDaoJdbc implements Grou { public GroupDaoJdbc(BasicDataSource dataSource, FhirContext fhirContext) { - super(dataSource, fhirContext, Group.class, "groups", "group_json", "group_id", with(), with()); + super(dataSource, fhirContext, Group.class, "groups", "group_json", "group_id", with(), + with(ResearchStudyEnrollmentRevInclude::new)); } @Override diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQueryRevIncludeParameterFactory.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQueryRevIncludeParameterFactory.java index ec64fd450..0f85bcadb 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQueryRevIncludeParameterFactory.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/SearchQueryRevIncludeParameterFactory.java @@ -1,9 +1,28 @@ package org.highmed.dsf.fhir.search; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.List; +import org.hl7.fhir.r4.model.Resource; + public interface SearchQueryRevIncludeParameterFactory { + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + @Documented + public @interface RevIncludeDefinition + { + Class resourceType(); + + String parameterName(); + + Class[] targetResourceTypes(); + } + void configure(List revIncludeParameterValues); List getErrors(); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/ResearchStudyEnrollment.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/ResearchStudyEnrollment.java index 060e78526..28aff6c39 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/ResearchStudyEnrollment.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/ResearchStudyEnrollment.java @@ -24,9 +24,8 @@ @SearchQueryParameter.SearchParameterDefinition(name = ResearchStudyEnrollment.PARAMETER_NAME, definition = "http://highmed.org/fhir/SearchParameter/research-study-enrollment", type = Enumerations.SearchParamType.REFERENCE, documentation = "Search by research study enrollment") public class ResearchStudyEnrollment extends AbstractReferenceParameter { - public static final String PARAMETER_NAME = "enrollment"; - private static final String RESOURCE_TYPE_NAME = "ResearchStudy"; + public static final String PARAMETER_NAME = "enrollment"; private static final String TARGET_RESOURCE_TYPE_NAME = "Group"; public ResearchStudyEnrollment() diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/TaskRequester.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/TaskRequester.java index efcb92bd0..a9d9a616c 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/TaskRequester.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/TaskRequester.java @@ -29,9 +29,8 @@ @SearchParameterDefinition(name = TaskRequester.PARAMETER_NAME, definition = "http://hl7.org/fhir/SearchParameter/Task-requester", type = SearchParamType.REFERENCE, documentation = "Search by task requester") public class TaskRequester extends AbstractReferenceParameter { - public static final String PARAMETER_NAME = "requester"; - private static final String RESOURCE_TYPE_NAME = "Task"; + public static final String PARAMETER_NAME = "requester"; private static final String[] TARGET_RESOURCE_TYPE_NAMES = { "Practitioner", "Organization", "Patient", "PractitionerRole" }; // TODO add Device, RelatedPerson if supported, see also doResolveReferencesForMatching, matches, getIncludeSql diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/EndpointOrganizationRevInclude.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/EndpointOrganizationRevInclude.java index d2b85b73e..bb250af31 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/EndpointOrganizationRevInclude.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/EndpointOrganizationRevInclude.java @@ -3,8 +3,13 @@ import java.sql.Connection; import org.highmed.dsf.fhir.search.IncludeParts; +import org.highmed.dsf.fhir.search.SearchQueryRevIncludeParameterFactory.RevIncludeDefinition; +import org.hl7.fhir.r4.model.Endpoint; +import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Resource; +@RevIncludeDefinition(resourceType = Endpoint.class, parameterName = "organization", targetResourceTypes = { + Organization.class }) public class EndpointOrganizationRevInclude extends AbstractRevIncludeParameterFactory { public EndpointOrganizationRevInclude() diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/OrganizationEndpointRevInclude.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/OrganizationEndpointRevInclude.java index 39057c6fc..fb6221937 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/OrganizationEndpointRevInclude.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/OrganizationEndpointRevInclude.java @@ -3,8 +3,13 @@ import java.sql.Connection; import org.highmed.dsf.fhir.search.IncludeParts; +import org.highmed.dsf.fhir.search.SearchQueryRevIncludeParameterFactory.RevIncludeDefinition; +import org.hl7.fhir.r4.model.Endpoint; +import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Resource; +@RevIncludeDefinition(resourceType = Organization.class, parameterName = "endpoint", targetResourceTypes = { + Endpoint.class }) public class OrganizationEndpointRevInclude extends AbstractRevIncludeParameterFactory { public OrganizationEndpointRevInclude() diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/ResearchStudyEnrollmentRevInclude.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/ResearchStudyEnrollmentRevInclude.java new file mode 100644 index 000000000..607447458 --- /dev/null +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/rev/include/ResearchStudyEnrollmentRevInclude.java @@ -0,0 +1,31 @@ +package org.highmed.dsf.fhir.search.parameters.rev.include; + +import java.sql.Connection; + +import org.highmed.dsf.fhir.search.IncludeParts; +import org.highmed.dsf.fhir.search.SearchQueryRevIncludeParameterFactory.RevIncludeDefinition; +import org.hl7.fhir.r4.model.Group; +import org.hl7.fhir.r4.model.ResearchStudy; +import org.hl7.fhir.r4.model.Resource; + +@RevIncludeDefinition(resourceType = ResearchStudy.class, parameterName = "enrollment", targetResourceTypes = { + Group.class }) +public class ResearchStudyEnrollmentRevInclude extends AbstractRevIncludeParameterFactory +{ + public ResearchStudyEnrollmentRevInclude() + { + super("ResearchStudy", "enrollment", "Group"); + } + + @Override + protected String getRevIncludeSql(IncludeParts includeParts) + { + return "(SELECT jsonb_agg(research_study) FROM current_research_studies WHERE research_study->'enrollment' @> concat('[{\"reference\": \"Group/', group_json->>'id', '\"}]')::jsonb) AS research_studies"; + } + + @Override + protected void modifyIncludeResource(Resource resource, Connection connection) + { + // Nothing to do for organizations + } +} diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/webservice/impl/ConformanceServiceImpl.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/webservice/impl/ConformanceServiceImpl.java index ee8a20ab3..160369189 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/webservice/impl/ConformanceServiceImpl.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/webservice/impl/ConformanceServiceImpl.java @@ -16,6 +16,7 @@ import org.highmed.dsf.fhir.help.ParameterConverter; import org.highmed.dsf.fhir.search.SearchQueryParameter.SearchParameterDefinition; +import org.highmed.dsf.fhir.search.SearchQueryRevIncludeParameterFactory.RevIncludeDefinition; import org.highmed.dsf.fhir.search.parameters.BinaryContentType; import org.highmed.dsf.fhir.search.parameters.BundleIdentifier; import org.highmed.dsf.fhir.search.parameters.CodeSystemIdentifier; @@ -57,6 +58,10 @@ import org.highmed.dsf.fhir.search.parameters.ValueSetIdentifier; import org.highmed.dsf.fhir.search.parameters.ValueSetUrl; import org.highmed.dsf.fhir.search.parameters.ValueSetVersion; +import org.highmed.dsf.fhir.search.parameters.rev.include.AbstractRevIncludeParameterFactory; +import org.highmed.dsf.fhir.search.parameters.rev.include.EndpointOrganizationRevInclude; +import org.highmed.dsf.fhir.search.parameters.rev.include.OrganizationEndpointRevInclude; +import org.highmed.dsf.fhir.search.parameters.rev.include.ResearchStudyEnrollmentRevInclude; import org.highmed.dsf.fhir.webservice.specification.ConformanceService; import org.highmed.dsf.fhir.websocket.ServerEndpoint; import org.highmed.dsf.tools.build.BuildInfoReader; @@ -185,6 +190,7 @@ private CapabilityStatement createCapabilityStatement(String serverBase, int def StructureDefinition.class, Subscription.class, Task.class, ValueSet.class); var searchParameters = new HashMap, List>(); + var revIncludeParameters = new HashMap, List>>(); var binaryContentType = createSearchParameter(BinaryContentType.class); searchParameters.put(Binary.class, Arrays.asList(binaryContentType)); @@ -203,6 +209,10 @@ private CapabilityStatement createCapabilityStatement(String serverBase, int def var endpointStatus = createSearchParameter(EndpointStatus.class); searchParameters.put(Endpoint.class, Arrays.asList(endpointIdentifier, endpointName, endpointOrganization, endpointStatus)); + revIncludeParameters.put(Endpoint.class, Collections.singletonList(OrganizationEndpointRevInclude.class)); + + // Group + revIncludeParameters.put(Group.class, Collections.singletonList(ResearchStudyEnrollmentRevInclude.class)); var healthcareServiceActive = createSearchParameter(HealthcareServiceActive.class); var healthcareServiceIdentifier = createSearchParameter(HealthcareServiceIdentifier.class); @@ -222,6 +232,7 @@ private CapabilityStatement createCapabilityStatement(String serverBase, int def var organizationType = createSearchParameter(OrganizationType.class); searchParameters.put(Organization.class, Arrays.asList(organizationActive, organizationEndpoint, organizationIdentifier, organizationNameOrAlias, organizationType)); + revIncludeParameters.put(Organization.class, Collections.singletonList(EndpointOrganizationRevInclude.class)); var patientActive = createSearchParameter(PatientActive.class); var patientIdentifier = createSearchParameter(PatientIdentifier.class); @@ -283,7 +294,7 @@ private CapabilityStatement createCapabilityStatement(String serverBase, int def r.setConditionalRead(ConditionalReadStatus.FULLSUPPORT); r.setConditionalUpdate(true); r.setConditionalDelete(ConditionalDeleteStatus.SINGLE); - r.addReferencePolicy(ReferenceHandlingPolicy.LITERAL); // TODO ReferenceHandlingPolicy.ENFORCED + r.addReferencePolicy(ReferenceHandlingPolicy.ENFORCED); r.setType(resource.getAnnotation(ResourceDef.class).name()); r.setProfile(resource.getAnnotation(ResourceDef.class).profile()); @@ -309,6 +320,23 @@ private CapabilityStatement createCapabilityStatement(String serverBase, int def .map(s -> new StringType(resource.getAnnotation(ResourceDef.class).name() + ":" + s.getName())) .collect(Collectors.toList())); + r.setSearchRevInclude( + revIncludeParameters.getOrDefault(resource, Collections.emptyList()).stream().flatMap(c -> + { + RevIncludeDefinition def = c.getAnnotation(RevIncludeDefinition.class); + if (def == null) + return Stream.empty(); + else + { + String resourceTypeName = def.resourceType().getAnnotation(ResourceDef.class).name(); + String parameterName = def.parameterName(); + return Arrays.stream(def.targetResourceTypes()) + .map(t -> t.getAnnotation(ResourceDef.class).name()) + .map(targetResourceTypeName -> new StringType( + resourceTypeName + ":" + parameterName + ":" + targetResourceTypeName)); + } + }).collect(Collectors.toList())); + r.addSearchParam(createFormatParameter()); r.addSearchParam(createPrettyParameter()); r.addSearchParam(createCountParameter(defaultPageCount)); From 4fd7fc86da5be109353615fc78c8dea653f8da94 Mon Sep 17 00:00:00 2001 From: Hauke Hund Date: Mon, 10 Feb 2020 21:32:54 +0100 Subject: [PATCH 4/4] revinclude integration tests --- .../integration/EndpointIntegrationTest.java | 71 +++++++++++++++++++ .../OrganizationIntegrationTest.java | 23 ++++++ .../fhir/test/TestSuiteIntegrationTests.java | 4 +- 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/integration/EndpointIntegrationTest.java diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/integration/EndpointIntegrationTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/integration/EndpointIntegrationTest.java new file mode 100644 index 000000000..c78a963be --- /dev/null +++ b/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/integration/EndpointIntegrationTest.java @@ -0,0 +1,71 @@ +package org.highmed.dsf.fhir.integration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.Collections; +import java.util.Map; + +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4.model.Bundle.SearchEntryMode; +import org.hl7.fhir.r4.model.Endpoint; +import org.hl7.fhir.r4.model.Organization; +import org.junit.Test; + +public class EndpointIntegrationTest extends AbstractIntegrationTest +{ + @Test + public void testSearchAll() throws Exception + { + Bundle searchBundle = getWebserviceClient().search(Endpoint.class, Collections.emptyMap()); + assertNotNull(searchBundle); + assertEquals(1, searchBundle.getTotal()); + assertTrue(searchBundle.getEntryFirstRep().getResource() instanceof Endpoint); + } + + @Test + public void testSearchEndpointIncludeOrganization() throws Exception + { + Bundle searchBundle = getWebserviceClient().search(Endpoint.class, + Map.of("_include", Collections.singletonList("Endpoint:organization"))); + assertNotNull(searchBundle); + assertEquals(1, searchBundle.getTotal()); + assertEquals(2, searchBundle.getEntry().size()); + + BundleEntryComponent eptEntry = searchBundle.getEntry().get(0); + assertNotNull(eptEntry); + assertEquals(SearchEntryMode.MATCH, eptEntry.getSearch().getMode()); + assertNotNull(eptEntry.getResource()); + assertTrue(eptEntry.getResource() instanceof Endpoint); + + BundleEntryComponent orgEntry = searchBundle.getEntry().get(1); + assertNotNull(orgEntry); + assertEquals(SearchEntryMode.INCLUDE, orgEntry.getSearch().getMode()); + assertNotNull(orgEntry.getResource()); + assertTrue(orgEntry.getResource() instanceof Organization); + } + + @Test + public void testSearchEndpointRevIncludeOrganization() throws Exception + { + Bundle searchBundle = getWebserviceClient().search(Endpoint.class, + Map.of("_revinclude", Collections.singletonList("Organization:endpoint"))); + assertNotNull(searchBundle); + assertEquals(1, searchBundle.getTotal()); + assertEquals(2, searchBundle.getEntry().size()); + + BundleEntryComponent orgEntry = searchBundle.getEntry().get(0); + assertNotNull(orgEntry); + assertEquals(SearchEntryMode.MATCH, orgEntry.getSearch().getMode()); + assertNotNull(orgEntry.getResource()); + assertTrue(orgEntry.getResource() instanceof Endpoint); + + BundleEntryComponent eptEntry = searchBundle.getEntry().get(1); + assertNotNull(eptEntry); + assertEquals(SearchEntryMode.INCLUDE, eptEntry.getSearch().getMode()); + assertNotNull(eptEntry.getResource()); + assertTrue(eptEntry.getResource() instanceof Organization); + } +} diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/integration/OrganizationIntegrationTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/integration/OrganizationIntegrationTest.java index dad9fe009..19d9ca678 100755 --- a/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/integration/OrganizationIntegrationTest.java +++ b/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/integration/OrganizationIntegrationTest.java @@ -22,6 +22,7 @@ public void testSearchAll() throws Exception Bundle searchBundle = getWebserviceClient().search(Organization.class, Collections.emptyMap()); assertNotNull(searchBundle); assertEquals(1, searchBundle.getTotal()); + assertTrue(searchBundle.getEntryFirstRep().getResource() instanceof Organization); } @Test @@ -45,4 +46,26 @@ public void testSearchOrganizationIncludeEndpoint() throws Exception assertNotNull(eptEntry.getResource()); assertTrue(eptEntry.getResource() instanceof Endpoint); } + + @Test + public void testSearchOrganizationRevIncludeEndpoint() throws Exception + { + Bundle searchBundle = getWebserviceClient().search(Organization.class, + Map.of("_revinclude", Collections.singletonList("Endpoint:organization"))); + assertNotNull(searchBundle); + assertEquals(1, searchBundle.getTotal()); + assertEquals(2, searchBundle.getEntry().size()); + + BundleEntryComponent orgEntry = searchBundle.getEntry().get(0); + assertNotNull(orgEntry); + assertEquals(SearchEntryMode.MATCH, orgEntry.getSearch().getMode()); + assertNotNull(orgEntry.getResource()); + assertTrue(orgEntry.getResource() instanceof Organization); + + BundleEntryComponent eptEntry = searchBundle.getEntry().get(1); + assertNotNull(eptEntry); + assertEquals(SearchEntryMode.INCLUDE, eptEntry.getSearch().getMode()); + assertNotNull(eptEntry.getResource()); + assertTrue(eptEntry.getResource() instanceof Endpoint); + } } diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/test/TestSuiteIntegrationTests.java b/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/test/TestSuiteIntegrationTests.java index f0ad25f57..76f442007 100755 --- a/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/test/TestSuiteIntegrationTests.java +++ b/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/test/TestSuiteIntegrationTests.java @@ -1,6 +1,7 @@ package org.highmed.dsf.fhir.test; import org.highmed.dsf.fhir.integration.BinaryIntegrationTest; +import org.highmed.dsf.fhir.integration.EndpointIntegrationTest; import org.highmed.dsf.fhir.integration.OrganizationIntegrationTest; import org.highmed.dsf.fhir.integration.TaskIntegrationTest; import org.junit.ClassRule; @@ -9,7 +10,8 @@ import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) -@SuiteClasses({ BinaryIntegrationTest.class, OrganizationIntegrationTest.class, TaskIntegrationTest.class }) +@SuiteClasses({ BinaryIntegrationTest.class, EndpointIntegrationTest.class, OrganizationIntegrationTest.class, + TaskIntegrationTest.class }) public class TestSuiteIntegrationTests { @ClassRule