Correctly expose chains in DSTU2 server conformance statmeent

This commit is contained in:
James Agnew 2019-01-10 06:15:31 -07:00
parent ee52d6fb31
commit b8f200f897
4 changed files with 178 additions and 66 deletions

View File

@ -387,16 +387,24 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
}
}
RestResourceSearchParam param = resource.addSearchParam();
String finalNextParamUnchainedName = nextParamUnchainedName;
RestResourceSearchParam param =
resource
.getSearchParam()
.stream()
.filter(t -> t.getName().equals(finalNextParamUnchainedName))
.findFirst()
.orElseGet(() -> resource.addSearchParam());
param.setName(nextParamUnchainedName);
if (StringUtils.isNotBlank(chain)) {
param.addChain(chain);
}
if (nextParameter.getParamType() == RestSearchParameterTypeEnum.REFERENCE) {
for (String nextWhitelist : new TreeSet<String>(nextParameter.getQualifierWhitelist())) {
if (nextWhitelist.startsWith(".")) {
param.addChain(nextWhitelist.substring(1));
} else {
if (nextParameter.getParamType() == RestSearchParameterTypeEnum.REFERENCE) {
for (String nextWhitelist : new TreeSet<String>(nextParameter.getQualifierWhitelist())) {
if (nextWhitelist.startsWith(".")) {
param.addChain(nextWhitelist.substring(1));
}
}
}
}

View File

@ -579,6 +579,45 @@ public class ServerConformanceProviderDstu2Test {
assertEquals(2, param.getChain().size());
}
@Test
public void testSearchReferenceParameterWithExplicitChainsDocumentation() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new SearchProviderWithExplicitChains());
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
boolean found = false;
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().get(0);
assertEquals("The organization at which this person is a patient", param.getDescription());
found = true;
}
}
assertTrue(found);
Conformance conformance = sc.getServerConformance(createHttpServletRequest());
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
ourLog.info(conf);
RestResource resource = findRestResource(conformance, "Patient");
assertEquals(1, resource.getSearchParam().size());
RestResourceSearchParam param = resource.getSearchParam().get(0);
assertEquals("organization", param.getName());
assertEquals("bar", param.getChain().get(0).getValue());
assertEquals("baz.bob", param.getChain().get(1).getValue());
assertEquals("foo", param.getChain().get(2).getValue());
assertEquals(3, param.getChain().size());
}
@Test
public void testSystemHistorySupported() throws Exception {
@ -851,6 +890,19 @@ public class ServerConformanceProviderDstu2Test {
}
public static class SearchProviderWithExplicitChains {
@Search(type = Patient.class)
public Patient findPatient1(
@Description(shortDefinition = "The organization at which this person is a patient")
@RequiredParam(name = "organization.foo") ReferenceAndListParam theFoo,
@RequiredParam(name = "organization.bar") ReferenceAndListParam theBar,
@RequiredParam(name = "organization.baz.bob") ReferenceAndListParam theBazbob) {
return null;
}
}
public static class SystemHistoryProvider {
@History

View File

@ -1,56 +1,49 @@
package org.hl7.fhir.dstu3.hapi.rest.server;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.*;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.model.primitive.InstantDt;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.CapabilityStatement.*;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.AfterClass;
import org.junit.Test;
import com.google.common.collect.Lists;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.*;
import ca.uhn.fhir.rest.server.method.*;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.ResourceBinding;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.RestulfulServerConfiguration;
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.method.IParameter;
import ca.uhn.fhir.rest.server.method.SearchMethodBinding;
import ca.uhn.fhir.rest.server.method.SearchParameter;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import com.google.common.collect.Lists;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.CapabilityStatement.*;
import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus;
import org.hl7.fhir.dstu3.model.OperationDefinition.OperationDefinitionParameterComponent;
import org.hl7.fhir.dstu3.model.OperationDefinition.OperationKind;
import org.hl7.fhir.dstu3.model.OperationDefinition.OperationParameterUse;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.AfterClass;
import org.junit.Test;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class ServerCapabilityStatementProviderDstu3Test {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerCapabilityStatementProviderDstu3Test.class);
private static FhirContext ourCtx;
private static FhirValidator ourValidator;
static {
@ -88,6 +81,46 @@ public class ServerCapabilityStatementProviderDstu3Test {
return resource;
}
@Test
public void testSearchReferenceParameterWithExplicitChainsDocumentation() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new SearchProviderWithExplicitChains());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
boolean found = false;
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().get(0);
assertEquals("The organization at which this person is a patient", param.getDescription());
found = true;
}
}
assertTrue(found);
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest());
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
ourLog.info(conf);
CapabilityStatementRestResourceComponent resource = findRestResource(conformance, "Patient");
assertEquals(1, resource.getSearchParam().size());
CapabilityStatementRestResourceSearchParamComponent param = resource.getSearchParam().get(0);
assertEquals("organization", param.getName());
// assertEquals("bar", param.getChain().get(0).getValue());
// assertEquals("baz.bob", param.getChain().get(1).getValue());
// assertEquals("foo", param.getChain().get(2).getValue());
// assertEquals(3, param.getChain().size());
}
@Test
public void testConditionalOperations() throws Exception {
@ -235,7 +268,9 @@ public class ServerCapabilityStatementProviderDstu3Test {
assertNull(res.getConditionalUpdateElement().getValue());
}
/** See #379 */
/**
* See #379
*/
@Test
public void testOperationAcrossMultipleTypes() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
@ -544,7 +579,7 @@ public class ServerCapabilityStatementProviderDstu3Test {
@Test
public void testSearchReferenceParameterWithList() throws Exception {
RestfulServer rsNoType = new RestfulServer(ourCtx){
RestfulServer rsNoType = new RestfulServer(ourCtx) {
@Override
public RestulfulServerConfiguration createConfiguration() {
RestulfulServerConfiguration retVal = super.createConfiguration();
@ -561,7 +596,7 @@ public class ServerCapabilityStatementProviderDstu3Test {
String confNoType = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
ourLog.info(confNoType);
RestfulServer rsWithType = new RestfulServer(ourCtx){
RestfulServer rsWithType = new RestfulServer(ourCtx) {
@Override
public RestulfulServerConfiguration createConfiguration() {
RestulfulServerConfiguration retVal = super.createConfiguration();
@ -720,8 +755,8 @@ public class ServerCapabilityStatementProviderDstu3Test {
assertThat(param.getUse(), is(OperationParameterUse.IN));
CapabilityStatementRestResourceComponent patientResource = restComponent.getResource().stream()
.filter(r -> patientResourceName.equals(r.getType()))
.findAny().get();
.filter(r -> patientResourceName.equals(r.getType()))
.findAny().get();
assertThat("Named query parameters should not appear in the resource search params", patientResource.getSearchParam(), is(empty()));
}
@ -787,9 +822,17 @@ public class ServerCapabilityStatementProviderDstu3Test {
assertTrue(outcome, result.isSuccessful());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
public static class SearchProviderWithExplicitChains {
@Search(type = Patient.class)
public Patient findPatient1(
@Description(shortDefinition = "The organization at which this person is a patient")
@RequiredParam(name = "organization.foo") ReferenceAndListParam theFoo,
@RequiredParam(name = "organization.bar") ReferenceAndListParam theBar,
@RequiredParam(name = "organization.baz.bob") ReferenceAndListParam theBazbob) {
return null;
}
}
@SuppressWarnings("unused")
@ -836,7 +879,7 @@ public class ServerCapabilityStatementProviderDstu3Test {
@Search(type = Patient.class)
public Patient findPatient(@Description(shortDefinition = "The patient's identifier") @OptionalParam(name = Patient.SP_IDENTIFIER) TokenParam theIdentifier,
@Description(shortDefinition = "The patient's name") @OptionalParam(name = Patient.SP_NAME) StringParam theName) {
@Description(shortDefinition = "The patient's name") @OptionalParam(name = Patient.SP_NAME) StringParam theName) {
return null;
}
@ -847,7 +890,7 @@ public class ServerCapabilityStatementProviderDstu3Test {
@Operation(name = "someOp")
public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId,
@OperationParam(name = "someOpParam1") DateType theStart, @OperationParam(name = "someOpParam2") Encounter theEnd) {
@OperationParam(name = "someOpParam1") DateType theStart, @OperationParam(name = "someOpParam2") Encounter theEnd) {
return null;
}
@ -868,7 +911,7 @@ public class ServerCapabilityStatementProviderDstu3Test {
@Operation(name = "someOp")
public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId,
@OperationParam(name = "someOpParam1") DateType theStart, @OperationParam(name = "someOpParam2") Patient theEnd) {
@OperationParam(name = "someOpParam1") DateType theStart, @OperationParam(name = "someOpParam2") Patient theEnd) {
return null;
}
@ -912,9 +955,9 @@ public class ServerCapabilityStatementProviderDstu3Test {
@SuppressWarnings("unused")
public static class PlainProviderWithExtendedOperationOnNoType {
@Operation(name = "plain", idempotent = true, returnParameters = { @OperationParam(min = 1, max = 2, name = "out1", type = StringType.class) })
@Operation(name = "plain", idempotent = true, returnParameters = {@OperationParam(min = 1, max = 2, name = "out1", type = StringType.class)})
public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart,
@OperationParam(name = "end") DateType theEnd) {
@OperationParam(name = "end") DateType theEnd) {
return null;
}
@ -925,7 +968,7 @@ public class ServerCapabilityStatementProviderDstu3Test {
@Operation(name = "everything", idempotent = true)
public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart,
@OperationParam(name = "end") DateType theEnd) {
@OperationParam(name = "end") DateType theEnd) {
return null;
}
@ -942,8 +985,8 @@ public class ServerCapabilityStatementProviderDstu3Test {
@Description(shortDefinition = "This is a search for stuff!")
@Search
public List<DiagnosticReport> findDiagnosticReportsByPatient(@RequiredParam(name = DiagnosticReport.SP_SUBJECT + '.' + Patient.SP_IDENTIFIER) TokenParam thePatientId,
@OptionalParam(name = DiagnosticReport.SP_CODE) TokenOrListParam theNames, @OptionalParam(name = DiagnosticReport.SP_DATE) DateRangeParam theDateRange,
@IncludeParam(allow = { "DiagnosticReport.result" }) Set<Include> theIncludes) throws Exception {
@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;
}
@ -974,7 +1017,7 @@ public class ServerCapabilityStatementProviderDstu3Test {
@Search(type = Patient.class)
public Patient findPatient2(
@Description(shortDefinition = "All patients linked to the given patient") @OptionalParam(name = "link", targetTypes = { Patient.class }) ReferenceAndListParam theLink) {
@Description(shortDefinition = "All patients linked to the given patient") @OptionalParam(name = "link", targetTypes = {Patient.class}) ReferenceAndListParam theLink) {
return null;
}
@ -984,15 +1027,15 @@ public class ServerCapabilityStatementProviderDstu3Test {
public static class SearchProviderWithWhitelist {
@Search(type = Patient.class)
public Patient findPatient1(@Description(shortDefinition = "The organization at which this person is a patient") @RequiredParam(name = Patient.SP_ORGANIZATION, chainWhitelist = { "foo",
"bar" }) ReferenceAndListParam theIdentifier) {
public Patient findPatient1(@Description(shortDefinition = "The organization at which this person is a patient") @RequiredParam(name = Patient.SP_ORGANIZATION, chainWhitelist = {"foo",
"bar"}) ReferenceAndListParam theIdentifier) {
return null;
}
}
@SuppressWarnings("unused")
public static class SearchProviderWithListNoType implements IResourceProvider {
public static class SearchProviderWithListNoType implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
@ -1000,7 +1043,6 @@ public class ServerCapabilityStatementProviderDstu3Test {
}
@Search()
public List<Patient> findPatient1(@Description(shortDefinition = "The organization at which this person is a patient") @RequiredParam(name = Patient.SP_ORGANIZATION) ReferenceAndListParam theIdentifier) {
return null;
@ -1009,7 +1051,7 @@ public class ServerCapabilityStatementProviderDstu3Test {
}
@SuppressWarnings("unused")
public static class SearchProviderWithListWithType implements IResourceProvider {
public static class SearchProviderWithListWithType implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
@ -1017,15 +1059,13 @@ public class ServerCapabilityStatementProviderDstu3Test {
}
@Search(type=Patient.class)
@Search(type = Patient.class)
public List<Patient> findPatient1(@Description(shortDefinition = "The organization at which this person is a patient") @RequiredParam(name = Patient.SP_ORGANIZATION) ReferenceAndListParam theIdentifier) {
return null;
}
}
public static class SystemHistoryProvider {
@History
@ -1110,4 +1150,9 @@ public class ServerCapabilityStatementProviderDstu3Test {
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -270,6 +270,13 @@
performed by the user to limit them to specific resources or compartments
that the user should have access to.
</action>
<action type="add">
In a DSTU2 server, if search parameters are expressed with chains directly in the
parameter name (e.g.
<![CDATA[<code>@RequiredParam(name="subject.name.family")</code>]]>) the second
part of the chain was lost when the chain was described in the server
CapabilityStatement. This has been corrected.
</action>
</release>
<release version="3.6.0" date="2018-11-12" description="Food">
<action type="add">