Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for enhanced prefetch - and clarified prefetch expectat… #612

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 44 additions & 23 deletions docs/specification/current.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ Similarly, each CDS Client will decide what FHIR resources to authorize and to p

### Prefetch Template

A _prefetch template_ is a FHIR [`read`](http://hl7.org/fhir/http.html#read) or [`search`](http://hl7.org/fhir/http.html#search) request that describes relevant data needed by the CDS Service. For example, the following is a prefetch template for hemoglobin A1c observations:
A _prefetch template_ is a FHIR [`read`](http://hl7.org/fhir/http.html#read), collection of FHIR [`read`](http://hl7.org/fhir/http.html#read) calls, or a [`search`](http://hl7.org/fhir/http.html#search) request that describes relevant data needed by the CDS Service. For example, the following is a prefetch template for hemoglobin A1c observations:

```
Observation?patient={{context.patientId}}&code=4548-4&_count=1&sort:desc=date
Expand Down Expand Up @@ -220,7 +220,7 @@ A CDS Client MAY choose to honor some or all of the desired prefetch templates,

The CDS Client SHALL deny access to the requested resource if the user in context is not authorized to access the resource.

As part of preparing the request, a CDS Client processes each prefetch template it intends to satisfy by replacing the prefetch tokens in the prefetch template to construct a relative FHIR request URL. This specification is not prescriptive about how this request is actually processed. The relative URL may be appended to the base URL for the CDS Client's FHIR server and directly invoked, or the CDS Client may use internal infrastructure to satisfy the request in the same way that invoking against the FHIR server would.
As part of preparing the request, a CDS Client processes each prefetch template it intends to satisfy by replacing the prefetch tokens in the prefetch template and manipulating the results to construct RESTful call. This specification is not prescriptive about how this request is actually processed. The content may be executed as a direct GET or POST against the CDS Client's FHIR server and directly invoked, or the CDS Client may use internal infrastructure to satisfy the request in the same way that invoking against the FHIR server would.

Regardless of how the CDS Client satisfies the prefetch templates (if at all), the prefetched data given to the CDS Service MUST be equivalent to the data the CDS Service would receive if it were making its own call to the CDS Client's FHIR server using the parameterized prefetch template.

Expand All @@ -230,7 +230,7 @@ The resulting response is passed along to the CDS Service using the `prefetch`

> Note that the reason prefetch results are not allowed to include next page links is that if the prefetched data contains only the first page of a multi-page search result, the CDS Service has no means to retrieve the subsequent pages of data. Consider, for example, a CDS Hooks implementation that does not expose a FHIR server.

The CDS Client MUST NOT include any prefetch template key that it chooses not to satisfy. Similarly, if the CDS Client encounters an error while prefetching any data, the prefetch template key MUST NOT be included in the request to the CDS Service. If the CDS Client has no data to populate a template prefetch key, the prefetch template key MUST have a value of __null__. Note that the __null__ result is used rather than a bundle with zero entries to account for the possibility that the prefetch url is a single-resource request.
The CDS Client MUST NOT include any prefetch template key that it chooses not to satisfy. Similarly, if the CDS Client encounters an error while prefetching any data, the prefetch template key MUST NOT be included in the request to the CDS Service. If the CDS Client has no data to populate a template prefetch key, the prefetch template key MUST have a value of __null__. Note that the __null__ result is used rather than a bundle with zero entries to account for the possibility that the prefetch url is a single-resource request.

It is the CDS Service's responsibility to check prefetched data against its template to determine what requests were satisfied (if any) and to programmatically retrieve any additional necessary data. If the CDS Service is unable to obtain required data because it cannot access the FHIR server and the request did not contain the necessary prefetch keys, the service SHALL respond with an HTTP 412 Precondition Failed status code.

Expand All @@ -240,45 +240,66 @@ A prefetch token is a placeholder in a prefetch template that is *_replaced by i

Prefetch tokens MUST be delimited by `{{` and `}}`, and MUST contain only the qualified path to a hook context field *_or one of the following user identifiers: `userPractitionerId`, 'userPractitioneRoleId', `userPatientId`, or `userRelatedPersonId`_*.

Individual hooks specify which of their `context` fields can be used as prefetch tokens. Only root-level fields with a primitive value within the `context` object are eligible to be used as prefetch tokens. For example, `{{context.medication.id}}` is not a valid prefetch token because it attempts to access the `id` field of the `medication` field.
Individual hooks specify which of their `context` fields can be used as prefetch tokens. Prefetch templates must start with one of these context elements and can then navigate to information available within the context element using [simple FHIRPath](https://hl7.org/fhir/fhirpath.html#simple). For example,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dangling "For example,"


##### Prefetch tokens identifying the user
A prefetch template enables a CDS Service to learn more about the current user through a FHIR read, like so:
There are two styles of prefetch expressions - read and search. How the expressions are formatted and how the data is expected to come back varies for each.

##### read prefetch

A prefetch is determined to be a read if, after resolving the prefetch token, the result is a string with the form `[resource name]/{{[token]}}`. For example:
```
{
"prefetch": {
"user": "{{context.userId}}"
"patient": "Patient/{{context.patient}}",
"user": "{{context.user}}"
}
}
```
or though a FHIR search:

`{{context.patient}}` resolves to a simple id. `{{context.user}}` resolves to a resource type and simple id separated by a '/'. In both cases, the result of resolving the prefetch template is a single string that matches a relative URL to a single resource instance. That relative URL could be appended to the CDS Client's server base URL to form the URL for a valid read.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`{{context.patient}}` resolves to a simple id. `{{context.user}}` resolves to a resource type and simple id separated by a '/'. In both cases, the result of resolving the prefetch template is a single string that matches a relative URL to a single resource instance. That relative URL could be appended to the CDS Client's server base URL to form the URL for a valid read.
`{{context.patient}}` resolves to a simple id (e.g., `123` for the Patient with id 123). `{{context.user}}` resolves to a resource type and simple id separated by a '/' (e.g., `Patient/123` or `Practitioner/456`). In both cases, the result of resolving the prefetch template is a single string that matches a relative URL to a single resource instance. That relative URL could be appended to the CDS Client's server base URL to form the URL for a valid read.

Adding examples would help, but I'm still not clear what the assumptions here. Is this paragraph defining a new context.patient variable in context, and a new context.user variable?


The result of a 'read' prefetch (if data is returned and not __null__) will be the JSON form of the referenced resource.

For read, the prefetch token FHIRPath is limited to *only* the name of the context element - no further navigation within the context element is permitted. For example, `{{context.draftOrders.id}}` is not a valid prefetch token for a read because it attempts to access the id field of the medication field. If expanding the prefetch token does not result in a simple Resource/id string, the prefetch is in error and the prefetch cannot be returned.

##### search prefetch
The cue that a prefetch is a search is that it starts with a resource name followed by a '?'. Search prefetches can be more complex than reads - and not all CDS clients will support all potential forms of search. Additional considerations include:

1. Prefetch templates can drill deeper into the context, navigating to child elements. For example `{{context.draftOrders.id}}` or event `{{context.draftOrders.ofType(MedicationRequest).medicationReference.reference}}` would be valid tokens.
2. Prefetch templates can resolve to an empty collection. If the template resolves to no elements, the prefetch result will be __null__, regardless of where within the search expression the prefetch token falls.
3. Prefetch templates can resolve to a collection of more than one element. If this occurs, the collection is returned as single comma-delimited concatenated string. E.g. `{{context.draftOrders.ofType(MedicationRequest).medicationReference.reference}}` might resolve to `Medication/123,http%3a//someserver.com/Medication/456,Medication/789`
4. Prefetch templates can resolve to complex objects. This is handled as follows:
* If a collection element is a simple type, the value is returned and any extensions or 'id' elements are ignored. If no value is specified for the element, the element is ignored
* If a collection element is a Coding or Identifier, the element is converted to [token](http://hl7.org/fhir/search.html#token) syntax with code/value followed by '|' followed by system.
5. Content expanded from a prefetch token may require HTTP escaping to ensure a valid URL fragment
6. When dealing with prefetch tokens that resolve to relative references, in some cases, the search needs the resolved token to be an id or collection of ids rather than references. In this circumstance, the FHIRPath can be followed with ":" and the name of the resource whose references should be filtered. Only relative references for the specified resource type are returned. For example `{{context.draftOrders.ofType(MedicationRequest).requester.reference:Practitioner}}` would resolve to the a list of ids who are requesters that are also Practitioners. For legacy reasons, this can also be done with the userId element by inserting the resource name into the context name. E.g. `{{context.userPatientRoleId}}`, `{{context.userPractionerId}}`, `{{context.userPractitionerRoleId}}`, and `{{context.userRelatedPersonId}}`.
isaacvetter marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This "extra colon at the end" syntax makes it harder to see what is the "thing that needs to be parsed as FHIRPath". I'd rather see (in order of preference):

  • Define "simple FHIRpath + replaceMatches" so developers can build a list of IDs by taking final path segments
  • Define a distinct templating syntax {{expr}:Patient} or somesuch so the fhirpath is unambiguous
  • Let CDS Clients figure out how to filter based on context (i.e., "please do what I mean" -- if searching for )
  • Use existing fhirpath comment syntax like /*Patient*/ instead of tacking on :. Aim would be to leverage existing parsers. If you just split on : you hit complications in places where the gramamr (https://github.com/HL7/FHIRPath/blob/master/spec/fhirpath.g4) allows colons.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For legacy reasons, this can also be done

The "legacy" examples are about "context.user*" but the other equivalent examples are about "context.draftOrders...". I'm unclear on how to translate (say) context.userPatientRoleId in the new syntax.


Examples of search prefetches include:
```
{
"prefetch": {
"user": "PractitionerRole?_id={{userPractitionerRoleId}}&_include=PractitionerRole:practitioner"
"patient": "Patient?_id={{context.patient}}&_include=Patient.general-practitioner",
"bloodPressures": "Observation?patient={{context.patient}}&code=85354-9|http%xx/loinc.org&_sort=-date&_count=10",
"observationDef": "ObservationDefinition?code={{context.draftOrdersServiceRequest.code.coding}}",
"prescriberPr": "Practitioner?_id={{context.draftOrders.ofType(MedicationRequest).requester.reference:Practitioner}}",
"userPR": "PractitionerRole?_id={{userPractitionerRoleId}}&_include=PractitionerRole:practitioner"
}
}
```

A prefetch template may include any of the following prefetch tokens:


Token | Description
---|---
`{{userPractitionerId}}` | FHIR id of the Practitioner resource corresponding to the current user.
`{{userPractitionerRoleId}}`|FHIR id of the PractitionerRole resource corresponding to the current user.
`{{userPatientId}}`|FHIR id of the Patient resource corresponding to the current user.
`{{userRelatedPersonId}}`|FHIR id of the RelatedPerson resource corresponding to the current user.


No single FHIR resource represents a user, rather Practitioner and PractitionerRole may be jointly used to represent a provider, and Patient or RelatedPerson are used to represent a patient or their proxy. Hook definitions typically define a `context.userId` field and corresponding prefetch token.
* The 'patient' prefetch is similar to the Patient read example, but takes advantage of search to 'include' related resources.
* The 'bloodPressures' example shows retrieving resources associated with the context patient but not otherwise related to context
* The 'observationDef' prefetch shows the filtering based on a deep path, complex object, and by something other than id
* The 'presciberPR' prefetch shows converting relative references to a subset of ids.
* The 'userPR' prefetch shows the ability to filter to a subset of ids using the special legacy 'userId' naming mechanism

Note:
* Authors SHOULD NOT create prefetches that presume availability of context information not expected to be available in persisted form. For example `MedicationRequest?_id={{context.draftOrders.id}}` would be problematic because the the draft orders aren't expected to have been persisted yet (though they might be on some clients).

#### Prefetch query restrictions
###### Prefetch query restrictions

To reduce the implementation burden on CDS Clients that support CDS Services, this specification RECOMMENDS that prefetch queries only use a subset of the full functionality available in the FHIR specification. When using this subset, valid prefetch templates MUST only make use of:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the name of this subset (so servers can say whether they are limited to it)?


* _instance_ level [read](https://www.hl7.org/fhir/http.html#read) interactions (for resources with known ids such as `Patient`, `Practitioner`, or `Encounter`)
* _type_ level [search](https://www.hl7.org/fhir/http.html#search) interactions; e.g. `patient={{context.patientId}}`
* Resource references (e.g. `patient={{context.patientId}}`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prohibits chaining, yes? It'd be good to annotate the examples above to say whether they are "compatible with the restricted subset".

* _token_ search parameters using equality (e.g. `code=4548-4`) and optionally the `:in` modifier (no other modifiers for token parameters)
Expand Down