Skip to content

Commit

Permalink
Merge pull request #3411 from IBM/tbieste-issue-2300
Browse files Browse the repository at this point in the history
Issue #2300 - Add calls to beforeHistory and afterHistory
  • Loading branch information
tbieste authored Mar 4, 2022
2 parents 0bcb572 + bfd9c18 commit c8010dc
Show file tree
Hide file tree
Showing 9 changed files with 540 additions and 88 deletions.
4 changes: 3 additions & 1 deletion docs/src/pages/guides/FHIRServerUsersGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -2760,7 +2760,9 @@ This component uses the IBM FHIR Server's PersistenceInterceptor feature to auto

Additionally, before returning resources to the client, the `fhir-smart` component performs authorization policy enforcement based on the list of SMART scopes included in the token's `scope` claim and the list of patient compartments in the `patient_id` claim.

For an example of using the IBM FHIR Server together with a SMART-enabled Keycloak authorization server, please see the data-access pattern at https://github.com/Alvearie/health-patterns/tree/main/data-access.
When the HTTP header `Prefer: return=minimal` is specified on a search or history request, only minimal resource metadata is retrieved. In those cases, either `user` or `system` SMART scopes must be used, since the resource data necessary to enforce access via `patient` SMART scopes is not available.

For an example of using the IBM FHIR Server together with a SMART-enabled Keycloak authorization server, please see the data-access pattern at https://github.com/LinuxForHealth/health-patterns/tree/main/data-access.

## 5.4 Custom HTTP Headers
IBM FHIR Server Supports the following custom HTTP Headers:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* (C) Copyright IBM Corp. 2016, 2021
* (C) Copyright IBM Corp. 2016, 2022
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -42,6 +42,7 @@ public class FHIRPersistenceEvent {
* This property is of type String and contains the resource id associated with an
* update, read, vread, history, or delete operation.
* For update and delete it may be null (i.e. for conditional updates/deletes)
* For whole-system history, this property will be null.
* For other operations, this property will be null.
*/
public static final String PROPNAME_RESOURCE_ID = "RESOURCE_ID";
Expand All @@ -64,6 +65,14 @@ public class FHIRPersistenceEvent {
*/
public static final String PROPNAME_SEARCH_CONTEXT_IMPL = "SEARCH_CONTEXT_IMPL";

/**
* This property is of type FHIRSystemHistoryContext and is the system history context
* associated with a system history request, but it may be null.
* For other operations, this property will be null.
*/
public static final String PROPNAME_SYSTEM_HISTORY_CONTEXT_IMPL = "SYSTEM_HISTORY_CONTEXT_IMPL";


private Resource fhirResource;
private Resource prevFhirResource = null;
private boolean prevFhirResourceSet = false;
Expand Down Expand Up @@ -149,7 +158,7 @@ public String getFhirResourceType() {

/**
* Returns the resource id associated with the FHIR REST API request that triggered the
* interceptor invocation. This will be non-null for a read, vread, history, or non-conditional update/delete operation.
* interceptor invocation. This will be non-null for a read, vread, non-whole-system history, or non-conditional update/delete operation.
*/
public String getFhirResourceId() {
return (String) getProperty(PROPNAME_RESOURCE_ID);
Expand Down Expand Up @@ -206,4 +215,11 @@ public FHIRSearchContext getSearchContextImpl() {
return (FHIRSearchContext) getProperty(PROPNAME_SEARCH_CONTEXT_IMPL);
}

/**
* Returns the FHIRSystemHistoryContext instance currently being used by the FHIR REST API layer
* to process the current request.
*/
public FHIRSystemHistoryContext getSystemHistoryContextImpl() {
return (FHIRSystemHistoryContext) getProperty(PROPNAME_SYSTEM_HISTORY_CONTEXT_IMPL);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.ibm.fhir.persistence.ResourceEraseRecord;
import com.ibm.fhir.persistence.SingleResourceResult;
import com.ibm.fhir.persistence.context.FHIRPersistenceEvent;
import com.ibm.fhir.persistence.context.FHIRSystemHistoryContext;
import com.ibm.fhir.persistence.erase.EraseDTO;
import com.ibm.fhir.persistence.exception.FHIRPersistenceException;
import com.ibm.fhir.persistence.payload.PayloadPersistenceResponse;
Expand All @@ -36,10 +37,10 @@ public interface FHIRResourceHelpers {
public static final boolean DO_VALIDATION = true;
// Constant for indicating whether an update can be skipped when the requested update resource matches the existing one
public static final boolean SKIPPABLE_UPDATE = true;

// Constant for when we don't use the If-Not-Match header value
public static final Integer IF_NOT_MATCH_NULL = null;

public enum Interaction {
CREATE("create"),
DELETE("delete"),
Expand Down Expand Up @@ -186,11 +187,13 @@ public FHIRRestOperationResponse doPatchOrUpdatePersist(FHIRPersistenceEvent eve
* the resource version
* @param searchContext
* the request search context
* @param systemHistoryContext
* the request system history context
* @return a map of persistence event properties
* @throws FHIRPersistenceException
*/
Map<String, Object> buildPersistenceEventProperties(String type, String id,
String version, FHIRSearchContext searchContext) throws FHIRPersistenceException;
String version, FHIRSearchContext searchContext, FHIRSystemHistoryContext systemHistoryContext) throws FHIRPersistenceException;

/**
* Performs an update operation (a new version of the Resource will be stored). Validates the resource.
Expand All @@ -208,7 +211,7 @@ Map<String, Object> buildPersistenceEventProperties(String type, String id,
* @param skippableUpdate
* if true, and the resource content in the update matches the existing resource on the server, then skip the update;
* if false, then always attempt the update
* @param ifNoneMatch
* @param ifNoneMatch
* conditional create-on-update
* @return a FHIRRestOperationResponse that contains the results of the operation
* @throws Exception
Expand Down Expand Up @@ -236,7 +239,7 @@ default FHIRRestOperationResponse doUpdate(String type, String id, Resource newR
* if false, then always attempt the update
* @param doValidation
* if true, validate the resource; if false, assume the resource has already been validated
* @param ifNoneMatch
* @param ifNoneMatch
* conditional create-on-update
* @return a FHIRRestOperationResponse that contains the results of the operation
* @throws Exception
Expand Down Expand Up @@ -391,7 +394,7 @@ default Bundle doHistory(MultivaluedMap<String, String> queryParameters, String
* Implement the system level history operation to obtain a list of changes to resources
* with an optional resourceType which supports for example [base]/Patient/_history
* requests to return the complete history of changes filtered to a specific resource type.
* Because the resource type is included in the path, this variant allows only a single
* Because the resource type is included in the path, this variant allows only a single
* resource type to be specified. To obtain history for more than one resource type, the
* [base]/_history whole system history endpoint should be used instead with a list of
* resource types specified using the _type query parameter.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* (C) Copyright IBM Corp. 2021
* (C) Copyright IBM Corp. 2021, 2022
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -387,7 +387,7 @@ private FHIRRestInteraction processEntryForPatch(Entry requestEntry, FHIRUrlPars

// Build the event we'll use when executing the interaction command
// - the resource gets injected later when we have it
FHIRPersistenceEvent event = new FHIRPersistenceEvent(null, helpers.buildPersistenceEventProperties(resourceType, resourceId, null, null));
FHIRPersistenceEvent event = new FHIRPersistenceEvent(null, helpers.buildPersistenceEventProperties(resourceType, resourceId, null, null, null));

// We don't perform the actual operation here, just generate the command
// we want to execute later
Expand Down Expand Up @@ -575,7 +575,7 @@ private FHIRRestInteraction processEntryForPost(Entry requestEntry, Entry valida

// Create the event
FHIRPersistenceEvent event =
new FHIRPersistenceEvent(resource, helpers.buildPersistenceEventProperties(resource.getClass().getSimpleName(), null, null, null));
new FHIRPersistenceEvent(resource, helpers.buildPersistenceEventProperties(resource.getClass().getSimpleName(), null, null, null, null));

result = new FHIRRestInteractionCreate(entryIndex, event, validationResponseEntry, requestDescription, requestURL, pathTokens[0], resource, ifNoneExist, localIdentifier);
} else {
Expand Down Expand Up @@ -667,7 +667,7 @@ private FHIRRestInteraction processEntryForPut(Entry requestEntry, Entry validat
}

// Create the event we'll use for this resource interaction
FHIRPersistenceEvent event = new FHIRPersistenceEvent(resource, helpers.buildPersistenceEventProperties(type, id, null, null));
FHIRPersistenceEvent event = new FHIRPersistenceEvent(resource, helpers.buildPersistenceEventProperties(type, id, null, null, null));
result = new FHIRRestInteractionUpdate(entryIndex, event, validationResponseEntry, requestDescription, requestURL,
type, id, resource, ifMatchBundleValue, requestURL.getQuery(), skippableUpdate, localIdentifier, ifNoneMatch);

Expand Down
Loading

0 comments on commit c8010dc

Please sign in to comment.