From e2ca967fd987ceb506a75b85dd764895e0a98f14 Mon Sep 17 00:00:00 2001 From: michaelabuckley Date: Thu, 19 Oct 2023 09:18:01 -0400 Subject: [PATCH] Force _summary=false when fetching CodeSystems and ValueSets with remote terminology (#5372) Force _summary=false when fetching CodeSystems and ValueSets with remote terminology --- ...force-summary-false-remote-validation.yaml | 4 ++ ...teTerminologyServiceValidationSupport.java | 69 +++++++++++++++---- ...rminologyServiceValidationSupportTest.java | 34 ++++++++- 3 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5371-force-summary-false-remote-validation.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5371-force-summary-false-remote-validation.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5371-force-summary-false-remote-validation.yaml new file mode 100644 index 00000000000..d969e560b11 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5371-force-summary-false-remote-validation.yaml @@ -0,0 +1,4 @@ +--- +type: change +issue: 5371 +title: "Remote terminology operations that fetch ValueSets or CodeSystems now force _summary=false to allow local validation." diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java index 437ddb2358c..99a4b1b90c9 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java @@ -8,7 +8,9 @@ import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.TranslateConceptResults; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.gclient.IQuery; import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.ParametersUtil; import org.apache.commons.lang3.StringUtils; @@ -24,6 +26,7 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -141,16 +144,32 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup @Override public IBaseResource fetchCodeSystem(String theSystem) { + // callers of this want the whole resource. + return fetchCodeSystem(theSystem, SummaryEnum.FALSE); + } + + /** + * Fetch the code system, possibly a summary. + * @param theSystem the canonical url + * @param theSummaryParam to force a summary mode - or null to allow server default. + * @return the CodeSystem + */ + @Nullable + private IBaseResource fetchCodeSystem(String theSystem, @Nullable SummaryEnum theSummaryParam) { IGenericClient client = provideClient(); Class bundleType = myCtx.getResourceDefinition("Bundle").getImplementingClass(IBaseBundle.class); - IBaseBundle results = client.search() + IQuery codeSystemQuery = client.search() .forResource("CodeSystem") - .where(CodeSystem.URL.matches().value(theSystem)) - .returnBundle(bundleType) - .execute(); + .where(CodeSystem.URL.matches().value(theSystem)); + + if (theSummaryParam != null) { + codeSystemQuery.summaryMode(theSummaryParam); + } + + IBaseBundle results = codeSystemQuery.returnBundle(bundleType).execute(); List resultsList = BundleUtil.toListOfResources(myCtx, results); - if (resultsList.size() > 0) { + if (!resultsList.isEmpty()) { return resultsList.get(0); } @@ -388,16 +407,36 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup @Override public IBaseResource fetchValueSet(String theValueSetUrl) { + // force the remote server to send the whole resource. + SummaryEnum summaryParam = SummaryEnum.FALSE; + return fetchValueSet(theValueSetUrl, summaryParam); + } + + /** + * Search for a ValueSet by canonical url via IGenericClient. + * + * @param theValueSetUrl the canonical url of the ValueSet + * @param theSummaryParam force a summary mode - null allows server default + * @return the ValueSet or null if none match the url + */ + @Nullable + private IBaseResource fetchValueSet(String theValueSetUrl, SummaryEnum theSummaryParam) { IGenericClient client = provideClient(); Class bundleType = myCtx.getResourceDefinition("Bundle").getImplementingClass(IBaseBundle.class); - IBaseBundle results = client.search() + + IQuery valueSetQuery = client.search() .forResource("ValueSet") - .where(CodeSystem.URL.matches().value(theValueSetUrl)) - .returnBundle(bundleType) - .execute(); + .where(CodeSystem.URL.matches().value(theValueSetUrl)); + + if (theSummaryParam != null) { + valueSetQuery.summaryMode(theSummaryParam); + } + + IBaseBundle results = valueSetQuery.returnBundle(bundleType).execute(); + List resultsList = BundleUtil.toListOfResources(myCtx, results); - if (resultsList.size() > 0) { + if (!resultsList.isEmpty()) { return resultsList.get(0); } @@ -406,12 +445,18 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup @Override public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) { - return fetchCodeSystem(theSystem) != null; + // a summary is ok if we are just checking the presence. + SummaryEnum summaryParam = null; + + return fetchCodeSystem(theSystem, summaryParam) != null; } @Override public boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) { - return fetchValueSet(theValueSetUrl) != null; + // a summary is ok if we are just checking the presence. + SummaryEnum summaryParam = null; + + return fetchValueSet(theValueSetUrl, summaryParam) != null; } @Override diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java index 5f95cf86b97..99e8b754c3a 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java @@ -12,6 +12,7 @@ import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.client.api.IClientInterceptor; import ca.uhn.fhir.rest.client.api.IHttpRequest; @@ -175,6 +176,18 @@ public class RemoteTerminologyServiceValidationSupportTest { assertEquals(null, myValueSetProvider.myLastValueSet); } + @Test + void testFetchValueSet_forcesSummaryFalse() { + // given + myValueSetProvider.myNextReturnValueSets = new ArrayList<>(); + + // when + IBaseResource valueSet = mySvc.fetchValueSet(VALUE_SET_URL); + + // then + assertEquals(SummaryEnum.FALSE, myValueSetProvider.myLastSummaryParam); + } + @Test public void testValidateCodeWithAllParams_CodeSystem_Success() { createNextCodeSystemReturnParameters(true, DISPLAY, null); @@ -374,6 +387,19 @@ public class RemoteTerminologyServiceValidationSupportTest { assertNull(myConceptMapProvider.myLastReverse); } + @Test + void testFetchCodeSystem_forcesSummaryFalse() { + // given + myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>(); + + // when + IBaseResource codeSystem = mySvc.fetchCodeSystem("http://loinc.org"); + + // then + assertEquals(SummaryEnum.FALSE, myCodeSystemProvider.myLastSummaryParam); + } + + private void addMatchToTranslateRequest(Parameters params) { Parameters.ParametersParameterComponent matchParam = params.addParameter().setName("match"); matchParam.addPart().setName("equivalence").setValue(new CodeType(EQUIVALENCE_CODE)); @@ -626,6 +652,7 @@ public class RemoteTerminologyServiceValidationSupportTest { private static class MyCodeSystemProvider implements IResourceProvider { + private SummaryEnum myLastSummaryParam; private UriParam myLastUrlParam; private List myNextReturnCodeSystems; private int myInvocationCount; @@ -680,8 +707,9 @@ public class RemoteTerminologyServiceValidationSupportTest { } @Search - public List find(@RequiredParam(name = "url") UriParam theUrlParam) { + public List find(@RequiredParam(name = "url") UriParam theUrlParam, SummaryEnum theSummaryParam) { myLastUrlParam = theUrlParam; + myLastSummaryParam = theSummaryParam; assert myNextReturnCodeSystems != null; return myNextReturnCodeSystems; } @@ -703,6 +731,7 @@ public class RemoteTerminologyServiceValidationSupportTest { private StringType myLastDisplay; private ValueSet myLastValueSet; private UriParam myLastUrlParam; + private SummaryEnum myLastSummaryParam; @Operation(name = "validate-code", idempotent = true, returnParameters = { @OperationParam(name = "result", type = BooleanType.class, min = 1), @@ -728,8 +757,9 @@ public class RemoteTerminologyServiceValidationSupportTest { } @Search - public List find(@RequiredParam(name = "url") UriParam theUrlParam) { + public List find(@RequiredParam(name = "url") UriParam theUrlParam, SummaryEnum theSummaryParam) { myLastUrlParam = theUrlParam; + myLastSummaryParam = theSummaryParam; assert myNextReturnValueSets != null; return myNextReturnValueSets; }