On this page:

9.4FHIR Gateway Examples

 

This page contains example interceptors that can be registered with the FHIR Gateway module.

9.4.1Example: Direct Search based on Parameter Value

 

The following example shows an interceptor that looks for a search parameter for gateway searches and directs them to the appropriate target based on the parameter value. This could be used in cases where the same type of resource is served by multiple targets but additional logic is needed in order to determine which target to actually query.

/**
 * This interceptor expects Observation searches to contain a mandatory
 * parameter called <code>Category</code>. This parameter may have the
 * values <code>lab</code> or <code>vitals</code> and will direct the
 * search to one target or the other.
 */
@Interceptor
public class ChooseGatewaySearchTargetBasedOnParameterInterceptor {

	@CdrHook(CdrPointcut.FHIRGW_SEARCH_TARGET_PREINVOKE)
	public void hook(SearchRequest theRequest, GatewayTargetJson theTarget) {

		// This interceptor only applies to Observation searches
		if (!theRequest.getResourceType().equals("Observation")) {
			return;
		}

		// Look for a parameter on the search request called: category
		String category = theRequest.getParameter("category");

		// Remove the parameter from the request so that we don't pass
		// it to the target servers
		theRequest.removeParameters("category");

		// Depending on the value, only query specific targets
		if ("lab".equals(category)) {
			if (!theTarget.getId().equals("lab_target")) {
				theRequest.setSkip(true);
			}
			return;
		}
		if ("vitals".equals(category)) {
			if (!theTarget.getId().equals("vital_target")) {
				theRequest.setSkip(true);
			}
			return;
		}

		// If the category parameter value was missing or invalid, default to
		// an HTTP 400 Invalid Request response from the gateway
		throw new InvalidRequestException("Missing or invalid required parameter: category");
	}

}

Using this interceptor, the following search URL would be directed only to the Gateway target server with the ID lab_target.

http://fhir.example.com/Observation?category=lab

9.4.2Example: Modify Target Search based on Previous Target Search

 

This example shows how to modify a search being performed on multiple targets so that the actual query used for the later targets is changed based on the results from the search on the earlier targets.

/**
 * This interceptor is intended to be used in a FHIR Gateway that is configured with multiple
 * search targets. It uses the results of the first target's search results to inform what the
 * query should look like for the second target.
 *
 * While the exact pattern being used here is obviously a bit contrived, it is a simple
 * demonstration of a pattern that can be useful when you have complicated gateway setups.
 *
 * This interceptor would only be effective if the Gateway Route is configured in non-parallel
 * mode, since the results from the first target will not always be available when the second
 * target is being invoked if the searches are performed in parallel.
 */
@Interceptor
public class GatewayEnrichSearchUsingPreviousTargetResults {

	@CdrHook(CdrPointcut.FHIRGW_SEARCH_TARGET_PREINVOKE)
	public void hook(SearchRequest theRequest, GatewayTargetJson theTarget, SearchResultsAccumulator theSearchResultsAccumulator) {

		// We assume there are two targets, one called "target1" and one called "target2". This
		// logic only applies to the second target.
		if (!theTarget.getId().equals("target2")) {
			return;
		}

		/*
		 * For this example, we look for the first patient in the search results, and
		 * then modify the query for the second target based on those results. This
		 * is just an example of the type of logic you might use.
		 */
		List<IBaseResource> target1Results = theSearchResultsAccumulator.getResults("target1");
		Optional<Patient> patient = target1Results
			.stream()
			.filter(t <i class="fas fa-angle-double-right"></i> t instanceof Patient)
			.map(t <i class="fas fa-angle-double-right"></i> (Patient) t)
			.findFirst();

		if (!patient.isPresent()) {
			return;
		}

		// Add a search parameter to the second target search request
		String searchForId = patient.get().getId().replaceAll("^1", "2");
		theRequest.addParameter("_id", searchForId);
	}

}

9.4.3Example: Modify Target Search Results

 

This example shows an interceptor that modifies search results returned by a FHIR Gateway target. Any changes made by this interceptor will be reflected in the eventual client Gateway response.

/**
 * This interceptor is intended to be used in a FHIR Gateway that is configured with multiple
 * search targets. It modifies the results of searches from a specific target, changing the
 * way that they will be presented to Gateway clients.
 *
 * In this example, we are simply making a basic change to the ID of the
 * resources being returned. You could make other changes however, such
 * as adding or removing elements, or even removing result resources
 * entirely.
 *
 * If you need to see reults from previous searches in order to inform changes being
 * made here, you can retrieve these from {@literal theSearchResultsAccumulator}.
 */
@Interceptor
public class GatewayEnrichGatewayTargetResponses {

	@CdrHook(CdrPointcut.FHIRGW_SEARCH_TARGET_POSTINVOKE)
	public void hook(GatewayTargetJson theTarget, SearchResultsAccumulator theSearchResultsAccumulator, SearchResponse theResponse) {

		// We assume there are two targets, one called "target1" and one called "target2". This
		// logic only applies to the second target.
		if (!theTarget.getId().equals("target1")) {
			return;
		}

		// Loop through the results of the search and modify them
		for (IBaseResource next : theResponse.getSearchResults()) {
			IIdType existingId = next.getIdElement();
			IdType newId = new IdType("1000" + existingId.getIdPart());
			next.setId(newId);
		}
	}

}

9.4.4Example: Modify Target Search Uri

 

This example shows an interceptor that modifies search uri before calling a FHIR Gateway target, using http POST method. Useful when a targeted FHIR server doesn't strictly follow endpoint definitions, for some reason.

/**
 * This interceptor is intended to be used in a FHIR Gateway that is configured with multiple
 * search targets.
 *
 * It modifies the uri used for searches for a specific target.
 * For example, the first target doesn't support usual FHIR endpoint '/_search' when using http POST method,
 * but instead offer the same functionality under '/myCustomSearch', in its FHIR API, for some reason.
 * Note: yep, some FHIR servers use creative endpoints!
 *
 * So in this example, we are simply removing '/_search' from the uri of the target when searching using POST method,
 * and replace it with the expected server endpoint '/myCustomSearch'.
 */

@Interceptor
public class GatewayUpdateTargetUriUsingClientInterceptor {

	@CdrHook(CdrPointcut.FHIRGW_SEARCH_TARGET_PREINVOKE)
	public void hook(SearchRequest theRequest, GatewayTargetJson theTarget, SearchResultsAccumulator theSearchResultsAccumulator) {

		// We assume there are multiple targets, and the first one is called "target1".
		// This logic only applies to the first target, that also uses http POST method for searching,
		// as configured in Gateway target json configuration
		if (theTarget.getId().equals("target1")
			&& theTarget.getUseHttpPostForAllSearches()) {

			// in that case, we want to modify the uri used to call the target server
			// using a client interceptor
			theRequest.setClientInterceptor(new IClientInterceptor() {

				@Override
				public void interceptRequest(IHttpRequest theHttpRequest) {

					if (theHttpRequest.getUri().endsWith("/_search")) {
						// assuming there is no other '/_search' string in the uri,
						// this will make the Gateway FHIR client do this call:
						// POST [baseUrl]/myCustomSearch
						String correctedUri = theHttpRequest.getUri().replace("/_search", "/myCustomSearch");
						theHttpRequest.setUri(correctedUri);
					}
				}

				@Override
				public void interceptResponse(IHttpResponse theIHttpResponse) throws IOException {
					// nothing to do here!
				}
			});
 		}

	}

}