WIP interceptor using reference param modifier

This commit is contained in:
Tadgh 2021-03-26 17:57:38 -04:00
parent e6cfb77c79
commit 9f13225aa5
11 changed files with 72 additions and 39 deletions

View File

@ -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 * 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. * only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
* </li> * </li>
* <li>
* ca.uhn.fhir.jpa.searchparam.SearchParameterMap - Contains the details of the search being checked. This can be modified.
* </li>
* </ul> * </ul>
* <p> * <p>
* Hooks should return <code>void</code>. * Hooks should return <code>void</code>.
@ -1163,7 +1166,8 @@ public enum Pointcut implements IPointcut {
STORAGE_PRESEARCH_REGISTERED(void.class, STORAGE_PRESEARCH_REGISTERED(void.class,
"ca.uhn.fhir.rest.server.util.ICachedSearchDetails", "ca.uhn.fhir.rest.server.util.ICachedSearchDetails",
"ca.uhn.fhir.rest.api.server.RequestDetails", "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"
), ),
/** /**

View File

@ -200,6 +200,7 @@ public class Constants {
public static final String PARAMQUALIFIER_STRING_CONTAINS = ":contains"; public static final String PARAMQUALIFIER_STRING_CONTAINS = ":contains";
public static final String PARAMQUALIFIER_STRING_EXACT = ":exact"; public static final String PARAMQUALIFIER_STRING_EXACT = ":exact";
public static final String PARAMQUALIFIER_TOKEN_TEXT = ":text"; 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_200_OK = 200;
public static final int STATUS_HTTP_201_CREATED = 201; public static final int STATUS_HTTP_201_CREATED = 201;
public static final int STATUS_HTTP_204_NO_CONTENT = 204; public static final int STATUS_HTTP_204_NO_CONTENT = 204;

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.CoverageIgnore;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
@ -41,6 +42,7 @@ public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/
private String myBaseUrl; private String myBaseUrl;
private String myValue; private String myValue;
private String myIdPart; private String myIdPart;
private Boolean myMdmExpand;
/** /**
* Constructor * Constructor
@ -121,6 +123,11 @@ public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/
@Override @Override
void doSetValueAsQueryToken(FhirContext theContext, String theParamName, String theQualifier, String theValue) { 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; String q = theQualifier;
if (isNotBlank(q)) { if (isNotBlank(q)) {
if (q.startsWith(":")) { if (q.startsWith(":")) {
@ -166,6 +173,14 @@ public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/
return myBaseUrl; return myBaseUrl;
} }
public boolean isMdmExpand() {
return myMdmExpand != null && myMdmExpand;
}
public ReferenceParam setMdmExpand(boolean theMdmExpand) {
myMdmExpand = theMdmExpand;
return this;
}
public String getChain() { public String getChain() {
return myChain; return myChain;

View File

@ -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.DaoSearchParamSynchronizer;
import ca.uhn.fhir.jpa.dao.index.IdHelperService; import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.dao.index.SearchParamWithInlineReferencesExtractor; 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.PredicateBuilder;
import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderCoords; import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderCoords;
import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderDate; import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderDate;
@ -459,11 +460,15 @@ public abstract class BaseConfig {
} }
@Bean @Bean
@Lazy
public MdmSearchExpandingInterceptorInterceptor mdmSearchExpandingInterceptorInterceptor() { public MdmSearchExpandingInterceptorInterceptor mdmSearchExpandingInterceptorInterceptor() {
return new MdmSearchExpandingInterceptorInterceptor(); return new MdmSearchExpandingInterceptorInterceptor();
} }
@Bean
public MdmLinkExpandSvc myMdmLinkExpandSvc() {
return new MdmLinkExpandSvc();
}
@Bean @Bean
@Lazy @Lazy
public TerminologyUploaderProvider terminologyUploaderProvider() { public TerminologyUploaderProvider terminologyUploaderProvider() {

View File

@ -35,6 +35,7 @@ import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails; 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.rest.param.StringParam;
import ca.uhn.fhir.util.ClasspathUtil; import ca.uhn.fhir.util.ClasspathUtil;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
@ -62,44 +63,21 @@ public class MdmSearchExpandingInterceptorInterceptor {
@Autowired @Autowired
private MdmLinkExpandSvc myMdmLinkExpandSvc; private MdmLinkExpandSvc myMdmLinkExpandSvc;
@Autowired @Autowired
private SearchParamHelper mySearchParamHelper;
@Autowired
private FhirContext myFhirContext; private FhirContext myFhirContext;
@Autowired @Autowired
private IdHelperService myIdHelperService; private IdHelperService myIdHelperService;
@Hook(Pointcut.STORAGE_PRECHECK_FOR_CACHED_SEARCH) @Hook(Pointcut.STORAGE_PRESEARCH_REGISTERED)
public boolean hook(RequestDetails theRequestDetails, SearchParameterMap theSearchParameterMap) { public void hook(RequestDetails theRequestDetails, SearchParameterMap theSearchParameterMap) {
Map<String, String[]> parameters =theRequestDetails.getParameters(); System.out.println("zoop");
boolean shouldExpandMdm = false; theSearchParameterMap.values().stream()
if (parameters.containsKey("_mdm")) { .flatMap(Collection::stream)
shouldExpandMdm = parameters.get("_mdm").length == 1 && parameters.get("_mdm")[0].equalsIgnoreCase("true"); .filter(queryParam -> queryParam instanceof ReferenceParam)
} .filter(referenceParam -> ((ReferenceParam) referenceParam).isMdmExpand())
if (shouldExpandMdm) { .map(untypedParam -> (ReferenceParam)untypedParam)
ourLog.debug("Detected that incoming request has _mdm=true. The request was: {}", theRequestDetails.getRequestPath()); .forEach(mdmReferenceParam -> {
String resourceName = theRequestDetails.getResourceName(); System.out.println("zoop");
Collection<RuntimeSearchParam> patientSearchParams = mySearchParamHelper.getPatientSearchParamsForResourceType(resourceName); System.out.println(mdmReferenceParam.toString());
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<List<IQueryParameterType>> lists = theSearchParameterMap.get(patientSearchParam.getName());
for (List<IQueryParameterType> list : lists) {
List<IQueryParameterType> 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<String> 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;
} }
} }

View File

@ -307,6 +307,13 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
ourLog.debug("Registering new search {}", searchUuid); 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<? extends IBaseResource> resourceTypeClass = myContext.getResourceDefinition(theResourceType).getImplementingClass(); Class<? extends IBaseResource> resourceTypeClass = myContext.getResourceDefinition(theResourceType).getImplementingClass();
final ISearchBuilder sb = mySearchBuilderFactory.newSearchBuilder(theCallingDao, theResourceType, resourceTypeClass); final ISearchBuilder sb = mySearchBuilderFactory.newSearchBuilder(theCallingDao, theResourceType, resourceTypeClass);
sb.setFetchSize(mySyncSize); 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) { private PersistedJpaSearchFirstPageBundleProvider submitSearch(IDao theCallingDao, SearchParameterMap theParams, String theResourceType, RequestDetails theRequestDetails, String theSearchUuid, ISearchBuilder theSb, String theQueryString, RequestPartitionId theRequestPartitionId) {
StopWatch w = new StopWatch(); StopWatch w = new StopWatch();
Search search = new Search(); 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); populateSearchEntity(theParams, theResourceType, theSearchUuid, theQueryString, search, theRequestPartitionId);
// Interceptor call: STORAGE_PRESEARCH_REGISTERED // Interceptor call: STORAGE_PRESEARCH_REGISTERED
HookParams params = new HookParams() HookParams params = new HookParams()
.add(ICachedSearchDetails.class, search) .add(ICachedSearchDetails.class, search)
.add(RequestDetails.class, theRequestDetails) .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); JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PRESEARCH_REGISTERED, params);
SearchTask task = new SearchTask(search, theCallingDao, theParams, theResourceType, theRequestDetails, theRequestPartitionId); SearchTask task = new SearchTask(search, theCallingDao, theParams, theResourceType, theRequestDetails, theRequestPartitionId);

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.mdm.config;
*/ */
import ca.uhn.fhir.context.FhirContext; 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.jpa.mdm.svc.MdmSurvivorshipSvcImpl;
import ca.uhn.fhir.mdm.api.IMdmControllerSvc; import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
import ca.uhn.fhir.mdm.api.IMdmExpungeSvc; import ca.uhn.fhir.mdm.api.IMdmExpungeSvc;
@ -79,6 +80,11 @@ public class MdmConsumerConfig {
return new MdmStorageInterceptor(); return new MdmStorageInterceptor();
} }
@Bean
MdmSearchExpandingInterceptorInterceptor myMdmSearchExpandingInterceptorInterceptor() {
return new MdmSearchExpandingInterceptorInterceptor();
}
@Bean @Bean
IMdmSurvivorshipService mdmSurvivorshipService() { return new MdmSurvivorshipSvcImpl(); } IMdmSurvivorshipService mdmSurvivorshipService() { return new MdmSurvivorshipSvcImpl(); }

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.mdm.interceptor;
* #L% * #L%
*/ */
import ca.uhn.fhir.jpa.interceptor.MdmSearchExpandingInterceptorInterceptor;
import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.interceptor.api.IInterceptorService;
@ -41,6 +42,8 @@ public class MdmSubmitterInterceptorLoader {
@Autowired @Autowired
private IMdmStorageInterceptor myIMdmStorageInterceptor; private IMdmStorageInterceptor myIMdmStorageInterceptor;
@Autowired @Autowired
private MdmSearchExpandingInterceptorInterceptor myMdmSearchExpandingInterceptorInterceptor;
@Autowired
private IInterceptorService myInterceptorService; private IInterceptorService myInterceptorService;
@Autowired @Autowired
private SubscriptionSubmitInterceptorLoader mySubscriptionSubmitInterceptorLoader; private SubscriptionSubmitInterceptorLoader mySubscriptionSubmitInterceptorLoader;
@ -53,6 +56,7 @@ public class MdmSubmitterInterceptorLoader {
myDaoConfig.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.MESSAGE); myDaoConfig.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.MESSAGE);
myInterceptorService.registerInterceptor(myIMdmStorageInterceptor); myInterceptorService.registerInterceptor(myIMdmStorageInterceptor);
myInterceptorService.registerInterceptor(myMdmSearchExpandingInterceptorInterceptor);
ourLog.info("MDM interceptor registered"); 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. // We need to call SubscriptionSubmitInterceptorLoader.start() again in case there were no subscription types the first time it was called.
mySubscriptionSubmitInterceptorLoader.start(); mySubscriptionSubmitInterceptorLoader.start();

View File

@ -44,6 +44,7 @@ import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.ContactPoint; import org.hl7.fhir.r4.model.ContactPoint;
import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.DateType;
import org.hl7.fhir.r4.model.Medication; 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.Organization;
import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Practitioner; import org.hl7.fhir.r4.model.Practitioner;
@ -96,6 +97,8 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
@Autowired @Autowired
protected IFhirResourceDao<Practitioner> myPractitionerDao; protected IFhirResourceDao<Practitioner> myPractitionerDao;
@Autowired @Autowired
protected IFhirResourceDao<Observation> myObservationDao;
@Autowired
protected MdmResourceMatcherSvc myMdmResourceMatcherSvc; protected MdmResourceMatcherSvc myMdmResourceMatcherSvc;
@Autowired @Autowired
protected IMdmLinkDao myMdmLinkDao; protected IMdmLinkDao myMdmLinkDao;

View File

@ -6,10 +6,12 @@ import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.mdm.helper.MdmHelperConfig; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperConfig;
import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4; 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.model.CanonicalEID;
import ca.uhn.fhir.mdm.rules.config.MdmSettings; import ca.uhn.fhir.mdm.rules.config.MdmSettings;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; 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.TransactionLogMessages;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IAnyResource;
@ -65,6 +67,13 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test {
assertLinkCount(1); assertLinkCount(1);
} }
@Test
public void testSearchExpandingInterceptorWorks() {
SearchParameterMap subject = new SearchParameterMap("subject", new ReferenceParam("Patient/123").setMdmExpand(true)).setLoadSynchronous(false);
myObservationDao.search(subject);
}
@Test @Test
public void testDeleteGoldenResourceDeletesLinks() throws InterruptedException { public void testDeleteGoldenResourceDeletesLinks() throws InterruptedException {
myMdmHelper.createWithLatch(buildPaulPatient()); myMdmHelper.createWithLatch(buildPaulPatient());

View File

@ -68,7 +68,6 @@ public class TestJpaR4Config extends BaseJavaConfigR4 {
retVal.setDriver(new org.h2.Driver()); retVal.setDriver(new org.h2.Driver());
retVal.setUrl("jdbc:h2:mem:testdb_r4"); retVal.setUrl("jdbc:h2:mem:testdb_r4");
// retVal.setUrl("jdbc:h2:file:/home/tadgh/smile/hapi-fhir/testdb_r4.db");
retVal.setMaxWaitMillis(10000); retVal.setMaxWaitMillis(10000);
retVal.setUsername(""); retVal.setUsername("");
retVal.setPassword(""); retVal.setPassword("");