Skip to content

Commit

Permalink
Initial Commit (#2)
Browse files Browse the repository at this point in the history
Most relevant are the tests. We want them to be already running when the
code is first merged in.

---------

Co-authored-by: Copybara <[email protected]>
  • Loading branch information
alexlaurinmath and Copybara authored May 6, 2024
1 parent a72a839 commit 2853898
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Note: For syntax, see <https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-syntax>

* @verily-src/fhirpathgo-eng-reviewers
24 changes: 24 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Build and Test
on:
pull_request:
branches:
- main

jobs:
build-test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
# Use the central Go version defined in go.mod to make it easier
# to perform upgrades.
go-version-file: go.mod
- name: Vet
run: go vet -v -unreachable=false ./...
- name: Build
run: go build -v ./...
- name: Test
run: go test ./...
101 changes: 99 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,99 @@
# fhirpath-go
Go implementation of the FHIRPath specification, implemented directly with the google/fhir proto definitions.
# FHIRPath

This package contains a Go implementation of the [FHIRPath][fhirpath] specification, implemented directly with
the [google/fhir][google-fhir] proto definitions.

This package aims to be compliant with both:

- the [N1 Normative Release](http://hl7.org/fhirpath/N1/) specification, and
- the [R4 specifications](http://hl7.org/fhir/R4/fhirpath.html).

## Import

```go
import "github.com/verily-src/fhirpath-go/fhirpath"
```

## Usage

A FHIRPath must be compiled before running it against a resource using the `Compile` method like so:

```go
expression, err := fhirpath.Compile("Patient.name.given")
if err != nil {
panic("error while compiling FHIRPath")
}
```

The compilation result can then be run against a resource:

```go
inputResources := []fhir.Resource{somePatient, someMedication}

result, err := expression.Evaluate(inputResources)
if err != nil {
panic("error while running FHIRPath against resource")
}
```

As defined in the FHIRPath specification, the output of evaluation is a **Collection**. So, the
result of Evaluate is of type `[]any`. As such, the result must be unpacked and cast to the desired
type for further processing.

### CompileOptions and EvaluateOptions

Options are provided for optional modification of compilation and evaluation. There is currently
support for:

- adding custom functions during Compile time
- adding custom external constant variables

#### To add a custom function

The constraints on the custom function are as follows:

- First argument must be `system.Collection`
- Arguments that follow must be either a fhir proto type or primitive system type

```go
customFn := func (input system.Collection, args ...any) (system.Collection error) {
fmt.Print("called custom fn")
return input, nil
}
expression, err := fhirpath.Compile("print()", WithFunction("print", customFn))
```

#### To add external constants

The constraints on external constants are as follows:

- Must be a fhir proto type, primitive system type, or `system.Collection`
- If you pass in a collection, contained elements must be fhir proto or system type.

```go
customVar := system.String("custom variable")
result, err := expression.Evaluate([]fhir.Resource{someResource}, WithConstant("var", customVar))
```

### System Types

The FHIRPath [spec](http://hl7.org/fhirpath/N1/#literals) defines the following custom System types:

- Boolean
- String
- Integer
- Decimal
- Quantity
- Date
- Time
- DateTime

FHIR Protos get implicitly converted to the above types according to this
[chart](http://hl7.org/fhir/R4/fhirpath.html#types), when used in some FHIRPath expressions.

### Things to be aware of

FHIRPath is not the most intuitive language, and there are some quirks. See [gotchas](gotchas.md).

[fhirpath]: http://hl7.org/fhirpath/
[google-fhir]: https://github.com/google/fhir
25 changes: 25 additions & 0 deletions gotchas.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# FHIRPath Gotcha’s

## Empty collections are propagated

* In FHIRPath, whenever an empty collection is encountered, rather than raising an error it gets propagated throughout the rest of the expression. This may make some issues difficult to catch.
* Eg. given `Patient.name` -> `{}`, `Patient.name.family + ' MD'` -> `{}`

## Equality sometimes returns an empty collection { }, rather than false

* If either collection is empty
* If the **precision_ _**of Date, Time, or DateTime objects are mismatched
* If the **dimension** of a Quantity unit is mismatched

## FHIR type specifiers are case-sensitive

* **Primitive** types are denoted with lower case specifiers.
* **Primitive** types that are written as upper case will be resolved as **System** types, not **FHIR** types.
* Eg. `Patient.birthDate is date = **true**` but `Patient.birthDate is Date = **false**`
* Case should match what’s listed [here](https://www.hl7.org/fhir/r4/datatypes.html)
* System types always begin with an uppercase letter

## `As` Expression is _not_ a filter, expects singleton input

* The as expression (`Observation.value as integer`) expects a singleton as input. For example, if you pass in a resource with multiple value fields, it will raise an error.
* It doesn’t filter out things that don’t match the type. For this purpose, the `where()` function should be used -> `where(value is integer)`

0 comments on commit 2853898

Please sign in to comment.