From b8f200f89755d9c300dd4f4c3cf4e961521ae241 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Thu, 10 Jan 2019 06:15:31 -0700 Subject: [PATCH] Correctly expose chains in DSTU2 server conformance statmeent --- .../dstu2/ServerConformanceProvider.java | 22 ++- .../ServerConformanceProviderDstu2Test.java | 52 ++++++ ...rCapabilityStatementProviderDstu3Test.java | 163 +++++++++++------- src/changes/changes.xml | 7 + 4 files changed, 178 insertions(+), 66 deletions(-) diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java index 9fdd410d46c..99d863f578b 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java @@ -387,16 +387,24 @@ public class ServerConformanceProvider implements IServerConformanceProvider 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(nextParameter.getQualifierWhitelist())) { - if (nextWhitelist.startsWith(".")) { - param.addChain(nextWhitelist.substring(1)); + } else { + if (nextParameter.getParamType() == RestSearchParameterTypeEnum.REFERENCE) { + for (String nextWhitelist : new TreeSet(nextParameter.getQualifierWhitelist())) { + if (nextWhitelist.startsWith(".")) { + param.addChain(nextWhitelist.substring(1)); + } } } } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java index 7b688291353..1ff78efc688 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java @@ -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 resourceBindings = rs.getResourceBindings(); + for (ResourceBinding resourceBinding : resourceBindings) { + if (resourceBinding.getResourceName().equals("Patient")) { + List> 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 diff --git a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java index 345b41dab0f..e35c1c7fa53 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java @@ -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 resourceBindings = rs.getResourceBindings(); + for (ResourceBinding resourceBinding : resourceBindings) { + if (resourceBinding.getResourceName().equals("Patient")) { + List> 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); @@ -302,7 +337,7 @@ public class ServerCapabilityStatementProviderDstu3Test { assertEquals("Patient", opDef.getParameter().get(0).getType()); } } - + @Test public void testOperationDocumentation() throws Exception { @@ -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())); } @@ -783,13 +818,21 @@ public class ServerCapabilityStatementProviderDstu3Test { ValidationResult result = ourValidator.validateWithResult(theOpDef); String outcome = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome()); ourLog.info("Outcome: {}", outcome); - + 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 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 theIncludes) throws Exception { + @OptionalParam(name = DiagnosticReport.SP_CODE) TokenOrListParam theNames, @OptionalParam(name = DiagnosticReport.SP_DATE) DateRangeParam theDateRange, + @IncludeParam(allow = {"DiagnosticReport.result"}) Set 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 getResourceType() { @@ -1000,7 +1043,6 @@ public class ServerCapabilityStatementProviderDstu3Test { } - @Search() public List 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 getResourceType() { @@ -1017,15 +1059,13 @@ public class ServerCapabilityStatementProviderDstu3Test { } - - @Search(type=Patient.class) + @Search(type = Patient.class) public List 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 @@ -1063,7 +1103,7 @@ public class ServerCapabilityStatementProviderDstu3Test { } } - + public static class TypeLevelOperationProvider implements IResourceProvider { public static final String OPERATION_NAME = "op"; @@ -1110,4 +1150,9 @@ public class ServerCapabilityStatementProviderDstu3Test { } + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + } diff --git a/src/changes/changes.xml b/src/changes/changes.xml index d68cb4989ec..a62b977bde1 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -270,6 +270,13 @@ performed by the user to limit them to specific resources or compartments that the user should have access to. + + In a DSTU2 server, if search parameters are expressed with chains directly in the + parameter name (e.g. + @RequiredParam(name="subject.name.family")]]>) the second + part of the chain was lost when the chain was described in the server + CapabilityStatement. This has been corrected. +