ConformanceStatement generation for DSTU3 is out of date (#3737)
* add system SPs to resource * populate include list * wip * wip * Tighten test * Chaneglog * Use old-style collectors Co-authored-by: Tadgh <garygrantgraham@gmail.com>
This commit is contained in:
parent
4ec4d36ed4
commit
c073c0eeb4
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 3736
|
||||||
|
jira: SMILE-4066
|
||||||
|
title: "Previously, the DSTU3 Conformance provider was not including `searchInclude` or `searchRevInclude` results in the conformance response. This has been corrected."
|
|
@ -24,8 +24,10 @@ import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServerConfiguration;
|
||||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.rest.server.util.ResourceSearchParams;
|
import ca.uhn.fhir.rest.server.util.ResourceSearchParams;
|
||||||
import ca.uhn.fhir.util.CoverageIgnore;
|
import ca.uhn.fhir.util.CoverageIgnore;
|
||||||
|
@ -45,9 +47,15 @@ import org.hl7.fhir.dstu3.model.UriType;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.NavigableSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
public class JpaConformanceProviderDstu3 extends org.hl7.fhir.dstu3.hapi.rest.server.ServerCapabilityStatementProvider {
|
public class JpaConformanceProviderDstu3 extends org.hl7.fhir.dstu3.hapi.rest.server.ServerCapabilityStatementProvider {
|
||||||
|
@ -60,6 +68,8 @@ public class JpaConformanceProviderDstu3 extends org.hl7.fhir.dstu3.hapi.rest.se
|
||||||
private RestfulServer myRestfulServer;
|
private RestfulServer myRestfulServer;
|
||||||
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
||||||
|
|
||||||
|
private RestfulServerConfiguration myServerConfiguration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
|
@ -78,6 +88,7 @@ public class JpaConformanceProviderDstu3 extends org.hl7.fhir.dstu3.hapi.rest.se
|
||||||
myRestfulServer = theRestfulServer;
|
myRestfulServer = theRestfulServer;
|
||||||
mySystemDao = theSystemDao;
|
mySystemDao = theSystemDao;
|
||||||
myDaoConfig = theDaoConfig;
|
myDaoConfig = theDaoConfig;
|
||||||
|
myServerConfiguration = theRestfulServer.createConfiguration();
|
||||||
super.setCache(false);
|
super.setCache(false);
|
||||||
setSearchParamRegistry(theSearchParamRegistry);
|
setSearchParamRegistry(theSearchParamRegistry);
|
||||||
setIncludeResourceCounts(true);
|
setIncludeResourceCounts(true);
|
||||||
|
@ -117,7 +128,7 @@ public class JpaConformanceProviderDstu3 extends org.hl7.fhir.dstu3.hapi.rest.se
|
||||||
|
|
||||||
nextResource.getSearchParam().clear();
|
nextResource.getSearchParam().clear();
|
||||||
String resourceName = nextResource.getType();
|
String resourceName = nextResource.getType();
|
||||||
ResourceSearchParams searchParams = mySearchParamRegistry.getActiveSearchParams(resourceName);
|
ResourceSearchParams searchParams = constructCompleteSearchParamList(resourceName);
|
||||||
for (RuntimeSearchParam runtimeSp : searchParams.values()) {
|
for (RuntimeSearchParam runtimeSp : searchParams.values()) {
|
||||||
CapabilityStatementRestResourceSearchParamComponent confSp = nextResource.addSearchParam();
|
CapabilityStatementRestResourceSearchParamComponent confSp = nextResource.addSearchParam();
|
||||||
|
|
||||||
|
@ -156,6 +167,8 @@ public class JpaConformanceProviderDstu3 extends org.hl7.fhir.dstu3.hapi.rest.se
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateIncludesList(nextResource, searchParams);
|
||||||
|
updateRevIncludesList(nextResource, searchParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,6 +188,77 @@ public class JpaConformanceProviderDstu3 extends org.hl7.fhir.dstu3.hapi.rest.se
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResourceSearchParams constructCompleteSearchParamList(String theResourceName) {
|
||||||
|
// Borrowed from hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have an explicit registry (which will be the case in the JPA server) we use it as priority,
|
||||||
|
* but also fill in any gaps using params from the server itself. This makes sure we include
|
||||||
|
* global params like _lastUpdated
|
||||||
|
*/
|
||||||
|
ResourceSearchParams searchParams;
|
||||||
|
ResourceSearchParams serverConfigurationActiveSearchParams = myServerConfiguration.getActiveSearchParams(theResourceName);
|
||||||
|
if (mySearchParamRegistry != null) {
|
||||||
|
searchParams = mySearchParamRegistry.getActiveSearchParams(theResourceName).makeCopy();
|
||||||
|
for (String nextBuiltInSpName : serverConfigurationActiveSearchParams.getSearchParamNames()) {
|
||||||
|
if (nextBuiltInSpName.startsWith("_") &&
|
||||||
|
!searchParams.containsParamName(nextBuiltInSpName) &&
|
||||||
|
searchParamEnabled(nextBuiltInSpName)) {
|
||||||
|
searchParams.put(nextBuiltInSpName, serverConfigurationActiveSearchParams.get(nextBuiltInSpName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
searchParams = serverConfigurationActiveSearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
return searchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean searchParamEnabled(String theSearchParam) {
|
||||||
|
// Borrowed from hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java
|
||||||
|
return !Constants.PARAM_FILTER.equals(theSearchParam) || myDaoConfig.isFilterParameterEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void updateRevIncludesList(CapabilityStatementRestResourceComponent theNextResource, ResourceSearchParams theSearchParams) {
|
||||||
|
// Add RevInclude to CapabilityStatement.rest.resource
|
||||||
|
if (theNextResource.getSearchRevInclude().isEmpty()) {
|
||||||
|
String resourcename = theNextResource.getType();
|
||||||
|
Set<String> allResourceTypes = myServerConfiguration.collectMethodBindings().keySet();
|
||||||
|
for (String otherResourceType : allResourceTypes) {
|
||||||
|
if (isBlank(otherResourceType)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ResourceSearchParams activeSearchParams = mySearchParamRegistry.getActiveSearchParams(otherResourceType);
|
||||||
|
activeSearchParams.values()
|
||||||
|
.stream()
|
||||||
|
.filter(t -> isNotBlank(t.getName()))
|
||||||
|
.filter(t -> t.getTargets().contains(resourcename))
|
||||||
|
.forEach(t -> theNextResource.addSearchRevInclude(otherResourceType + ":" + t.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateIncludesList(CapabilityStatementRestResourceComponent theResource, ResourceSearchParams theSearchParams) {
|
||||||
|
// Borrowed from hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java
|
||||||
|
String resourceName = theResource.getType();
|
||||||
|
if (theResource.getSearchInclude().isEmpty()) {
|
||||||
|
List<String> includes = theSearchParams
|
||||||
|
.values()
|
||||||
|
.stream()
|
||||||
|
.filter(t -> t.getParamType() == RestSearchParameterTypeEnum.REFERENCE)
|
||||||
|
.map(t -> resourceName + ":" + t.getName())
|
||||||
|
.sorted().collect(Collectors.toList());
|
||||||
|
theResource.addSearchInclude("*");
|
||||||
|
for (String nextInclude : includes) {
|
||||||
|
theResource.addSearchInclude(nextInclude);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isIncludeResourceCounts() {
|
public boolean isIncludeResourceCounts() {
|
||||||
return myIncludeResourceCounts;
|
return myIncludeResourceCounts;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,23 @@ import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import org.hl7.fhir.dstu3.model.Bundle;
|
import org.hl7.fhir.dstu3.model.Bundle;
|
||||||
|
import org.hl7.fhir.dstu3.model.CapabilityStatement;
|
||||||
import org.hl7.fhir.dstu3.model.Parameters;
|
import org.hl7.fhir.dstu3.model.Parameters;
|
||||||
|
import org.hl7.fhir.dstu3.model.PrimitiveType;
|
||||||
|
import org.hl7.fhir.dstu3.model.StringType;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.containsString;
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
|
import static org.hamcrest.CoreMatchers.hasItem;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
public class ResourceProviderDstu3BundleTest extends BaseResourceProviderDstu3Test {
|
public class ResourceProviderDstu3BundleTest extends BaseResourceProviderDstu3Test {
|
||||||
|
@ -32,8 +43,25 @@ public class ResourceProviderDstu3BundleTest extends BaseResourceProviderDstu3Te
|
||||||
} catch (NotImplementedOperationException e) {
|
} catch (NotImplementedOperationException e) {
|
||||||
assertThat(e.getMessage(), containsString("This operation is not yet implemented on this server"));
|
assertThat(e.getMessage(), containsString("This operation is not yet implemented on this server"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConformanceContainsIncludesAndRevIncludes() {
|
||||||
|
CapabilityStatement execute = ourClient.capabilities().ofType(CapabilityStatement.class).execute();
|
||||||
|
Optional<CapabilityStatement.CapabilityStatementRestResourceComponent> patient = execute.getRestFirstRep().getResource().stream().filter(resource -> resource.getType().equalsIgnoreCase("Patient")).findFirst();
|
||||||
|
if (patient.isEmpty()) {
|
||||||
|
fail("No Patient resource found in conformance statement");
|
||||||
|
} else {
|
||||||
|
List<StringType> searchInclude = patient.get().getSearchInclude();
|
||||||
|
List<StringType> searchRevInclude = patient.get().getSearchRevInclude();
|
||||||
|
|
||||||
|
assertTrue(searchRevInclude.stream().map(PrimitiveType::getValue).anyMatch(stringRevIncludes -> stringRevIncludes.equals("Observation:subject")));
|
||||||
|
assertEquals(searchRevInclude.size(), 152);
|
||||||
|
|
||||||
|
assertTrue(searchInclude.stream().map(PrimitiveType::getValue).anyMatch(stringRevIncludes -> stringRevIncludes.equals("Patient:general-practitioner")));
|
||||||
|
assertEquals(searchInclude.size(), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||||
import ca.uhn.fhir.rest.param.QuantityParam;
|
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||||
import ca.uhn.fhir.rest.param.ReferenceAndListParam;
|
import ca.uhn.fhir.rest.param.ReferenceAndListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
@ -73,6 +74,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import javax.servlet.ServletConfig;
|
import javax.servlet.ServletConfig;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.tools.Diagnostic;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -434,6 +436,36 @@ public class ServerCapabilityStatementProviderDstu3Test {
|
||||||
assertEquals("DiagnosticReport.result", res.getSearchInclude().get(0).getValue());
|
assertEquals("DiagnosticReport.result", res.getSearchInclude().get(0).getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIncludesAndRevIncludes() throws Exception {
|
||||||
|
|
||||||
|
RestfulServer rs = new RestfulServer(ourCtx);
|
||||||
|
rs.setProviders(new ProviderWithObservationSubjectSearch(), new ReadProvider());
|
||||||
|
|
||||||
|
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||||
|
rs.setServerConformanceProvider(sc);
|
||||||
|
|
||||||
|
rs.init(createServletConfig());
|
||||||
|
|
||||||
|
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||||
|
String conf = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||||
|
ourLog.info(conf);
|
||||||
|
|
||||||
|
CapabilityStatementRestComponent rest = conformance.getRest().get(0);
|
||||||
|
CapabilityStatementRestResourceComponent res = rest.getResource().get(0);
|
||||||
|
assertEquals("DiagnosticReport", res.getType());
|
||||||
|
|
||||||
|
assertEquals(DiagnosticReport.SP_SUBJECT, res.getSearchParam().get(0).getName());
|
||||||
|
// assertEquals("identifier", res.getSearchParam().get(0).getChain().get(0).getValue());
|
||||||
|
|
||||||
|
assertEquals(DiagnosticReport.SP_CODE, res.getSearchParam().get(1).getName());
|
||||||
|
|
||||||
|
assertEquals(DiagnosticReport.SP_DATE, res.getSearchParam().get(2).getName());
|
||||||
|
|
||||||
|
assertEquals(1, res.getSearchInclude().size());
|
||||||
|
assertEquals("DiagnosticReport.result", res.getSearchInclude().get(0).getValue());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadAndVReadSupported() throws Exception {
|
public void testReadAndVReadSupported() throws Exception {
|
||||||
|
|
||||||
|
@ -1017,7 +1049,16 @@ public class ServerCapabilityStatementProviderDstu3Test {
|
||||||
@IncludeParam(allow = { "DiagnosticReport.result" }) Set<Include> theIncludes) throws Exception {
|
@IncludeParam(allow = { "DiagnosticReport.result" }) Set<Include> theIncludes) throws Exception {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ProviderWithObservationSubjectSearch {
|
||||||
|
|
||||||
|
@Search
|
||||||
|
public List<DiagnosticReport> findDiAgnosticReportsByPatient(@RequiredParam(name = DiagnosticReport.SP_SUBJECT) ReferenceParam thePatientId,
|
||||||
|
@OptionalParam(name = DiagnosticReport.SP_CODE) TokenOrListParam theNames, @OptionalParam(name = DiagnosticReport.SP_DATE) DateRangeParam theDateRange,
|
||||||
|
@IncludeParam(allow = { "DiagnosticReport.result" }) Set<Include> theIncludes) throws Exception {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
|
|
Loading…
Reference in New Issue