From 43f1e4b2dd22c7db4ff104df6dc8e54b1aa25202 Mon Sep 17 00:00:00 2001 From: Martha Mitran Date: Thu, 8 Feb 2024 08:08:13 -0800 Subject: [PATCH] Handle invalid resourceType when performing FHIR search with non-chained reference (#5674) * Throw exception when resourceType of parameter value is invalid when performing FHIR search with non-chained reference * Fix changelog issue id and spotless warning * Cleanup unnecessary test * Validate resourceType only when using relative reference * Fix test for search using _type parameter --- ...-resource-type-should-throw-exception.yaml | 6 +++ .../ResourceLinkPredicateBuilder.java | 22 +++++++--- .../r4/FhirResourceDaoR4SearchNoFtTest.java | 40 +++++++++++++++++-- 3 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5672-search-non-chained-reference-invalid-resource-type-should-throw-exception.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5672-search-non-chained-reference-invalid-resource-type-should-throw-exception.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5672-search-non-chained-reference-invalid-resource-type-should-throw-exception.yaml new file mode 100644 index 00000000000..c039d9faf14 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5672-search-non-chained-reference-invalid-resource-type-should-throw-exception.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5672 +title: "Previously, when performing a FHIR search using a non-chained relative reference (returns entire resource) with +a server assigned id, it ignores the invalid resourceType in the parameter value and proceeds with the id based lookup. e.g. + GET `/MedicationAdministration?context=abc/1352` returns `Encounter/1352`. This has been fixed." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java index 9a1243a5aaf..0ee46dacf07 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java @@ -70,6 +70,7 @@ import com.healthmarketscience.sqlbuilder.UnaryCondition; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; +import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; @@ -204,6 +205,7 @@ public class ResourceLinkPredicateBuilder extends BaseJoiningPredicateBuilder im targetQualifiedUrls.add(dt.getValue()); } } else { + validateResourceTypeInReferenceParam(ref.getResourceType()); targetIds.add(dt); } @@ -256,6 +258,18 @@ public class ResourceLinkPredicateBuilder extends BaseJoiningPredicateBuilder im } } + private void validateResourceTypeInReferenceParam(final String theResourceType) { + if (StringUtils.isEmpty(theResourceType)) { + return; + } + + try { + getFhirContext().getResourceDefinition(theResourceType); + } catch (DataFormatException e) { + throw newInvalidResourceTypeException(theResourceType); + } + } + private Condition createPredicateReference( boolean theInverse, List thePathsToMatch, @@ -362,11 +376,7 @@ public class ResourceLinkPredicateBuilder extends BaseJoiningPredicateBuilder im String typeValue = theReferenceParam.getValue(); - try { - getFhirContext().getResourceDefinition(typeValue).getImplementingClass(); - } catch (DataFormatException e) { - throw newInvalidResourceTypeException(typeValue); - } + validateResourceTypeInReferenceParam(typeValue); if (!resourceTypes.contains(typeValue)) { throw newInvalidTargetTypeForChainException(theResourceName, theParamName, typeValue); } @@ -705,7 +715,7 @@ public class ResourceLinkPredicateBuilder extends BaseJoiningPredicateBuilder im .getLocalizer() .getMessage( ResourceLinkPredicateBuilder.class, "invalidTargetTypeForChain", theTypeValue, searchParamName); - return new InvalidRequestException(msg); + return new InvalidRequestException(Msg.code(2495) + msg); } @Nonnull diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index 2d668f24e5d..ee36b6b6f19 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -23,7 +23,6 @@ import ca.uhn.fhir.jpa.model.entity.ResourceLink; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage; import ca.uhn.fhir.jpa.model.util.UcumServiceUtil; -import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum; @@ -36,6 +35,7 @@ import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.parser.StrictErrorHandler; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.param.CompositeParam; import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateRangeParam; @@ -60,6 +60,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.util.HapiExtensions; import com.google.common.collect.Lists; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateUtils; @@ -151,7 +152,6 @@ import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; @@ -658,7 +658,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { myEncounterDao.search(map); fail(); } catch (InvalidRequestException e) { - assertEquals("Resource type \"Organization\" is not a valid target type for reference search parameter: Encounter:subject", e.getMessage()); + assertEquals(Msg.code(2495) + "Resource type \"Organization\" is not a valid target type for reference search parameter: Encounter:subject", e.getMessage()); } map = new SearchParameterMap(); @@ -3549,7 +3549,41 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { map.add(Task.SP_REQUESTER, new ReferenceParam(oid1.getValue())); ids = toUnqualifiedVersionlessIds(myTaskDao.search(map)); assertThat(ids, contains(tid1)); // NOT tid2 + } + @Test + public void testSearchWithValidTypedResourceReference_returnsCorrectly() { + // setup + IIdType encounterId = createEncounter(withIdentifier("http://example", "someValue")); + + MedicationAdministration ma = new MedicationAdministration() + .setContext(new Reference(encounterId)) + .setEffective(new DateTimeType()); + IIdType medicationAdministrationId = myMedicationAdministrationDao.create(ma, mySrd).getId(); + + // execute + ReferenceParam referenceParam = new ReferenceParam(encounterId.getResourceType(), null, encounterId.getIdPart()); + SearchParameterMap map = new SearchParameterMap().add(MedicationAdministration.SP_CONTEXT, referenceParam); + + // verify + List ids = toUnqualifiedVersionlessIds(myMedicationAdministrationDao.search(map, mySrd)); + assertEquals(1, ids.size()); + assertThat(ids, contains(medicationAdministrationId.toUnqualifiedVersionless())); + } + + @Test + public void testSearchWithInvalidTypedResourceReference_throwsUnsupportedResourceType() { + // execute + try { + ReferenceParam referenceParam = new ReferenceParam("abc", null, "123"); + SearchParameterMap map = new SearchParameterMap().setLoadSynchronous(true).add(MedicationAdministration.SP_CONTEXT, referenceParam); + + // verify + myMedicationAdministrationDao.search(map, mySrd); + fail(); + } catch (InvalidRequestException e) { + assertEquals(Msg.code(1250) + "Invalid/unsupported resource type: \"abc\"", e.getMessage()); + } } @Test