diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java index d5e9aa9a35b..837fc188567 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java @@ -1155,6 +1155,9 @@ public enum Pointcut implements IPointcut { * pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will * only be populated when operating in a RestfulServer implementation. It is provided as a convenience. * + *
  • + * ca.uhn.fhir.jpa.searchparam.SearchParameterMap - Contains the details of the search being checked. This can be modified. + *
  • * *

    * Hooks should return void. @@ -1163,7 +1166,8 @@ public enum Pointcut implements IPointcut { STORAGE_PRESEARCH_REGISTERED(void.class, "ca.uhn.fhir.rest.server.util.ICachedSearchDetails", "ca.uhn.fhir.rest.api.server.RequestDetails", - "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails" + "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails", + "ca.uhn.fhir.jpa.searchparam.SearchParameterMap" ), /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java index 93d957299d2..ef960acc0a2 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java @@ -200,6 +200,7 @@ public class Constants { public static final String PARAMQUALIFIER_STRING_CONTAINS = ":contains"; public static final String PARAMQUALIFIER_STRING_EXACT = ":exact"; public static final String PARAMQUALIFIER_TOKEN_TEXT = ":text"; + public static final String PARAMQUALIFIER_MDM = ":mdm"; public static final int STATUS_HTTP_200_OK = 200; public static final int STATUS_HTTP_201_CREATED = 201; public static final int STATUS_HTTP_204_NO_CONTENT = 204; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java index a8c43a708bd..583edaa6731 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.param; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.util.CoverageIgnore; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -41,6 +42,7 @@ public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/ private String myBaseUrl; private String myValue; private String myIdPart; + private Boolean myMdmExpand; /** * Constructor @@ -121,6 +123,11 @@ public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/ @Override void doSetValueAsQueryToken(FhirContext theContext, String theParamName, String theQualifier, String theValue) { + if (Constants.PARAMQUALIFIER_MDM.equals(theQualifier)) { + myMdmExpand = true; + theQualifier = ""; + //TODO GGG i probably have to deal with chaining here? like refusing the mdm qualifier if i can detect its chained? + } String q = theQualifier; if (isNotBlank(q)) { if (q.startsWith(":")) { @@ -166,6 +173,14 @@ public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/ return myBaseUrl; } + public boolean isMdmExpand() { + return myMdmExpand != null && myMdmExpand; + } + + public ReferenceParam setMdmExpand(boolean theMdmExpand) { + myMdmExpand = theMdmExpand; + return this; + } public String getChain() { return myChain; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java index 6ef6a72f468..55617d7b2a2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java @@ -40,6 +40,7 @@ import ca.uhn.fhir.jpa.dao.index.DaoResourceLinkResolver; import ca.uhn.fhir.jpa.dao.index.DaoSearchParamSynchronizer; import ca.uhn.fhir.jpa.dao.index.IdHelperService; import ca.uhn.fhir.jpa.dao.index.SearchParamWithInlineReferencesExtractor; +import ca.uhn.fhir.jpa.dao.mdm.MdmLinkExpandSvc; import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilder; import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderCoords; import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderDate; @@ -459,11 +460,15 @@ public abstract class BaseConfig { } @Bean - @Lazy public MdmSearchExpandingInterceptorInterceptor mdmSearchExpandingInterceptorInterceptor() { return new MdmSearchExpandingInterceptorInterceptor(); } + @Bean + public MdmLinkExpandSvc myMdmLinkExpandSvc() { + return new MdmLinkExpandSvc(); + } + @Bean @Lazy public TerminologyUploaderProvider terminologyUploaderProvider() { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/MdmSearchExpandingInterceptorInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/MdmSearchExpandingInterceptorInterceptor.java index 64762d566c2..04333b578a7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/MdmSearchExpandingInterceptorInterceptor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/MdmSearchExpandingInterceptorInterceptor.java @@ -35,6 +35,7 @@ import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.util.ClasspathUtil; import org.apache.commons.lang3.Validate; @@ -62,44 +63,21 @@ public class MdmSearchExpandingInterceptorInterceptor { @Autowired private MdmLinkExpandSvc myMdmLinkExpandSvc; @Autowired - private SearchParamHelper mySearchParamHelper; - @Autowired private FhirContext myFhirContext; @Autowired private IdHelperService myIdHelperService; - @Hook(Pointcut.STORAGE_PRECHECK_FOR_CACHED_SEARCH) - public boolean hook(RequestDetails theRequestDetails, SearchParameterMap theSearchParameterMap) { - Map parameters =theRequestDetails.getParameters(); - boolean shouldExpandMdm = false; - if (parameters.containsKey("_mdm")) { - shouldExpandMdm = parameters.get("_mdm").length == 1 && parameters.get("_mdm")[0].equalsIgnoreCase("true"); - } - if (shouldExpandMdm) { - ourLog.debug("Detected that incoming request has _mdm=true. The request was: {}", theRequestDetails.getRequestPath()); - String resourceName = theRequestDetails.getResourceName(); - Collection patientSearchParams = mySearchParamHelper.getPatientSearchParamsForResourceType(resourceName); - ourLog.debug("Resource type {} has patient search parameters [{}]", resourceName, patientSearchParams.stream().map(RuntimeSearchParam::getName).collect(Collectors.joining(", "))); - for (RuntimeSearchParam patientSearchParam: patientSearchParams) { - if (!theSearchParameterMap.containsKey(patientSearchParam.getName())) { - continue; - } - List> lists = theSearchParameterMap.get(patientSearchParam.getName()); - for (List list : lists) { - List toAdd = new ArrayList<>(); - for (IQueryParameterType paramVal : list) { - if (!paramVal.getMissing() && paramVal.getQueryParameterQualifier().equalsIgnoreCase("equals")){ - String valueAsQueryToken = paramVal.getValueAsQueryToken(myFhirContext); - Long pidOrThrowException = myIdHelperService.getPidOrThrowException(new IdDt(valueAsQueryToken)); - Set expandedIds= myMdmLinkExpandSvc.expandMdmBySourceResourcePid(pidOrThrowException); - ourLog.info("Expanded to resource ids: [{}]", String.join(",", expandedIds)); - toAdd.addAll(expandedIds.stream().map(StringParam::new).collect(Collectors.toList())); - } - } - list.addAll(toAdd); - } - } - } - return true; + @Hook(Pointcut.STORAGE_PRESEARCH_REGISTERED) + public void hook(RequestDetails theRequestDetails, SearchParameterMap theSearchParameterMap) { + System.out.println("zoop"); + theSearchParameterMap.values().stream() + .flatMap(Collection::stream) + .filter(queryParam -> queryParam instanceof ReferenceParam) + .filter(referenceParam -> ((ReferenceParam) referenceParam).isMdmExpand()) + .map(untypedParam -> (ReferenceParam)untypedParam) + .forEach(mdmReferenceParam -> { + System.out.println("zoop"); + System.out.println(mdmReferenceParam.toString()); + }); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java index 6786f9ff002..10a03f5c765 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java @@ -307,6 +307,13 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { ourLog.debug("Registering new search {}", searchUuid); + // Interceptor call: STORAGE_PRESEARCH_REGISTERED + HookParams params = new HookParams() + .add(ICachedSearchDetails.class, search) + .add(RequestDetails.class, theRequestDetails) + .addIfMatchesType(ServletRequestDetails.class, theRequestDetails) + .add(SearchParameterMap.class, theParams); + JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PRESEARCH_REGISTERED, params); Class resourceTypeClass = myContext.getResourceDefinition(theResourceType).getImplementingClass(); final ISearchBuilder sb = mySearchBuilderFactory.newSearchBuilder(theCallingDao, theResourceType, resourceTypeClass); sb.setFetchSize(mySyncSize); @@ -382,13 +389,15 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { private PersistedJpaSearchFirstPageBundleProvider submitSearch(IDao theCallingDao, SearchParameterMap theParams, String theResourceType, RequestDetails theRequestDetails, String theSearchUuid, ISearchBuilder theSb, String theQueryString, RequestPartitionId theRequestPartitionId) { StopWatch w = new StopWatch(); Search search = new Search(); + //TODO GGG MOVE THIS POPULATE AND ALSO THE HOOK CALL HIGHER UP IN THE STACK. populateSearchEntity(theParams, theResourceType, theSearchUuid, theQueryString, search, theRequestPartitionId); // Interceptor call: STORAGE_PRESEARCH_REGISTERED HookParams params = new HookParams() .add(ICachedSearchDetails.class, search) .add(RequestDetails.class, theRequestDetails) - .addIfMatchesType(ServletRequestDetails.class, theRequestDetails); + .addIfMatchesType(ServletRequestDetails.class, theRequestDetails) + .add(SearchParameterMap.class, theParams); JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PRESEARCH_REGISTERED, params); SearchTask task = new SearchTask(search, theCallingDao, theParams, theResourceType, theRequestDetails, theRequestPartitionId); diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java index 204b27e69f1..3ebb0dc6f4c 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.mdm.config; */ import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.interceptor.MdmSearchExpandingInterceptorInterceptor; import ca.uhn.fhir.jpa.mdm.svc.MdmSurvivorshipSvcImpl; import ca.uhn.fhir.mdm.api.IMdmControllerSvc; import ca.uhn.fhir.mdm.api.IMdmExpungeSvc; @@ -79,6 +80,11 @@ public class MdmConsumerConfig { return new MdmStorageInterceptor(); } + @Bean + MdmSearchExpandingInterceptorInterceptor myMdmSearchExpandingInterceptorInterceptor() { + return new MdmSearchExpandingInterceptorInterceptor(); + } + @Bean IMdmSurvivorshipService mdmSurvivorshipService() { return new MdmSurvivorshipSvcImpl(); } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSubmitterInterceptorLoader.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSubmitterInterceptorLoader.java index b3396377031..5d9360bb62d 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSubmitterInterceptorLoader.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSubmitterInterceptorLoader.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.mdm.interceptor; * #L% */ +import ca.uhn.fhir.jpa.interceptor.MdmSearchExpandingInterceptorInterceptor; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.interceptor.api.IInterceptorService; @@ -41,6 +42,8 @@ public class MdmSubmitterInterceptorLoader { @Autowired private IMdmStorageInterceptor myIMdmStorageInterceptor; @Autowired + private MdmSearchExpandingInterceptorInterceptor myMdmSearchExpandingInterceptorInterceptor; + @Autowired private IInterceptorService myInterceptorService; @Autowired private SubscriptionSubmitInterceptorLoader mySubscriptionSubmitInterceptorLoader; @@ -53,6 +56,7 @@ public class MdmSubmitterInterceptorLoader { myDaoConfig.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.MESSAGE); myInterceptorService.registerInterceptor(myIMdmStorageInterceptor); + myInterceptorService.registerInterceptor(myMdmSearchExpandingInterceptorInterceptor); ourLog.info("MDM interceptor registered"); // We need to call SubscriptionSubmitInterceptorLoader.start() again in case there were no subscription types the first time it was called. mySubscriptionSubmitInterceptorLoader.start(); diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java index 3c1b28764fa..dae0782cccb 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java @@ -44,6 +44,7 @@ import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.ContactPoint; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.Medication; +import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Practitioner; @@ -96,6 +97,8 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test { @Autowired protected IFhirResourceDao myPractitionerDao; @Autowired + protected IFhirResourceDao myObservationDao; + @Autowired protected MdmResourceMatcherSvc myMdmResourceMatcherSvc; @Autowired protected IMdmLinkDao myMdmLinkDao; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java index 5ad4b9ebe84..051bf27c546 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java @@ -6,10 +6,12 @@ import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperConfig; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.mdm.model.CanonicalEID; import ca.uhn.fhir.mdm.rules.config.MdmSettings; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; +import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.server.TransactionLogMessages; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; import org.hl7.fhir.instance.model.api.IAnyResource; @@ -65,6 +67,13 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test { assertLinkCount(1); } + @Test + public void testSearchExpandingInterceptorWorks() { + SearchParameterMap subject = new SearchParameterMap("subject", new ReferenceParam("Patient/123").setMdmExpand(true)).setLoadSynchronous(false); + myObservationDao.search(subject); + } + + @Test public void testDeleteGoldenResourceDeletesLinks() throws InterruptedException { myMdmHelper.createWithLatch(buildPaulPatient()); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/config/TestJpaR4Config.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/config/TestJpaR4Config.java index d5c08620370..1716e099cad 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/config/TestJpaR4Config.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/config/TestJpaR4Config.java @@ -68,7 +68,6 @@ public class TestJpaR4Config extends BaseJavaConfigR4 { retVal.setDriver(new org.h2.Driver()); retVal.setUrl("jdbc:h2:mem:testdb_r4"); -// retVal.setUrl("jdbc:h2:file:/home/tadgh/smile/hapi-fhir/testdb_r4.db"); retVal.setMaxWaitMillis(10000); retVal.setUsername(""); retVal.setPassword("");