FHIRPatch

Commure's JavaScript implementation of the FHIRPatch specification

What is FHIRPatch?

FHIRPatch is a set of fhirpath based operations that alter a given value.

Variations

We provide two variations of fhirpatch

NameDescriptionDefault
fhirpatchMutates a given object in place.Yes
fhirpatchImmutableReturns a new object with patches applied (this new object will be an immer object)No

Which one should I use?

Generally use fhirpatchImmutable if your in a browser environment this plays nicely with React and other frontend frameworks. Use the default fhirpatch function if you do not want immer objects being returned or want to eke out a bit more performance.

Environments

Supports both Browser and Node environments.

FHIRPatch operations

The commure implementation supports all of the operations at https://www.hl7.org/fhir/fhirpatch.html.

Add

1// [Description]: If element at path + name is a collection appends the operations value to the end of the collection. If element is a single item replaces it's value with value specified in operation.
2// [Operation]:
3// Add takes an operation with the following keys:
4// type: "add"
5// path: [Path to add the value]
6// name: [Name of the property adding]
7// value: [Value to add]
8test("add", () => {
9 const patient = {
10 resourceType: "Patient",
11 active: false,
12 name: [
13 {
14 use: "official",
15 family: "Chalmers",
16 given: ["Peter", "James"]
17 },
18 {
19 use: "usual",
20 given: ["Jim"]
21 },
22 {
23 use: "maiden",
24 family: "Windsor",
25 given: ["Peter", "James"],
26 period: {
27 end: "2002"
28 }
29 }
30 ]
31 };
32 const operation = {
33 type: "add",
34 path: "Patient.name[0]",
35 name: "value",
36 value: "added value"
37 };
38 fhirpatch(operation, patient);
39 expect(patient).toEqual({
40 active: false,
41 name: [
42 {
43 family: "Chalmers",
44 given: ["Peter", "James"],
45 value: "added value",
46 use: "official"
47 },
48 { given: ["Jim"], use: "usual" },
49 {
50 family: "Windsor",
51 given: ["Peter", "James"],
52 period: { end: "2002" },
53 use: "maiden"
54 }
55 ],
56 resourceType: "Patient"
57 });
58
59 const patient2 = {
60 resourceType: "Patient",
61 active: false,
62 name: [
63 {
64 use: "official",
65 family: "Chalmers",
66 given: ["Peter", "James"]
67 },
68 {
69 use: "usual",
70 given: ["Jim"]
71 },
72 {
73 use: "maiden",
74 family: "Windsor",
75 given: ["Peter", "James"],
76 period: {
77 end: "2002"
78 }
79 }
80 ]
81 };
82
83 fhirpatch({ ...operation, path: "Patient.name[2]" }, patient2);
84 expect(patient2).toEqual({
85 active: false,
86 name: [
87 {
88 family: "Chalmers",
89 given: ["Peter", "James"],
90 use: "official"
91 },
92 { given: ["Jim"], use: "usual" },
93 {
94 family: "Windsor",
95 given: ["Peter", "James"],
96 period: { end: "2002" },
97 value: "added value",
98 use: "maiden"
99 }
100 ],
101 resourceType: "Patient"
102 });
103});

Insert

1// [Description]: Inserts a given value into the given collection [based on path].
2// [Operation]:
3// Insert takes an operation with the following keys:
4// type: "insert"
5// path: [Path of the collection to insert the value]
6// index: [Index where value will be inserted in]
7// value: [Value to insert]
8
9test("insert", () => {
10 const patient = {
11 resourceType: "Patient",
12 active: false,
13 name: [
14 {
15 use: "official",
16 family: "Chalmers",
17 given: ["Peter", "James"]
18 },
19 {
20 use: "usual",
21 given: ["Jim"]
22 },
23 {
24 use: "maiden",
25 family: "Windsor",
26 given: ["Peter", "James"],
27 period: {
28 end: "2002"
29 }
30 }
31 ]
32 };
33
34 const operation = {
35 type: "insert",
36 path: "Patient.name",
37 index: 3,
38 value: { family: "Blaire", use: "unknown", given: ["Jake", "Lamotta"] }
39 };
40 fhirpatch(operation, patient);
41 expect(patient).toEqual({
42 active: false,
43 name: [
44 { family: "Chalmers", given: ["Peter", "James"], use: "official" },
45 { given: ["Jim"], use: "usual" },
46 {
47 family: "Windsor",
48 given: ["Peter", "James"],
49 period: { end: "2002" },
50 use: "maiden"
51 },
52 { family: "Blaire", given: ["Jake", "Lamotta"], use: "unknown" }
53 ],
54 resourceType: "Patient"
55 });
56});

Delete

1// [Description]: Deletes an element from the object. Only one element can be deleted at a time.
2// [Operation]:
3// Delete takes an operation with the following keys:
4// type: "delete"
5// path: [Path of an element to delete]
6test("delete", () => {
7 let patient = {
8 resourceType: "Patient",
9 active: false,
10 name: [
11 {
12 use: "official",
13 family: "Chalmers",
14 given: ["Peter", "James"]
15 },
16 {
17 use: "usual",
18 given: ["Jim"]
19 },
20 {
21 use: "maiden",
22 family: "Windsor",
23 given: ["Peter", "James"],
24 period: {
25 end: "2002"
26 }
27 }
28 ]
29 };
30
31 fhirpatch(
32 {
33 type: "delete",
34 path: "Patient.name[0].use"
35 },
36 patient
37 );
38
39 expect(patient).toEqual({
40 resourceType: "Patient",
41 active: false,
42 name: [
43 {
44 family: "Chalmers",
45 given: ["Peter", "James"]
46 },
47 {
48 use: "usual",
49 given: ["Jim"]
50 },
51 {
52 use: "maiden",
53 family: "Windsor",
54 given: ["Peter", "James"],
55 period: {
56 end: "2002"
57 }
58 }
59 ]
60 });
61});

Replace

1// [Description]: Replaces an element in the object. Only one element can be replaced at a time.
2// [Operation]:
3// Replace takes an operation with the following keys:
4// type: "replace"
5// path: [Path of an element to replace]
6// value: [Replacement value for given element]
7test("replace", () => {
8 const patient = testPatient();
9 fhirpatch(
10 [
11 {
12 type: "replace",
13 path: "Patient.name[2].period",
14 value: {
15 end: null
16 }
17 },
18 {
19 type: "replace",
20 path: "Patient.name[0].use",
21 value: null
22 }
23 ],
24 patient
25 );
26
27 expect(patient).toEqual({
28 resourceType: "Patient",
29 active: false,
30 name: [
31 {
32 use: null,
33 family: "Chalmers",
34 given: ["Peter", "James"]
35 },
36 {
37 use: "usual",
38 given: ["Jim"]
39 },
40 {
41 use: "maiden",
42 family: "Windsor",
43 given: ["Peter", "James"],
44 period: {
45 end: null
46 }
47 }
48 ]
49 });
50});

Move

1// [Description]: Moves element from source index to destination for a given collection specified by path.
2// [Operation]:
3// Replace takes an operation with the following keys:
4// path: [Path of an element to replace]
5// value: [Replacement value for given element]
6test("move", () => {
7 let patient = {
8 resourceType: "Patient",
9 name: [{ given: ["James", "Michael", "Miller", "Mik"] }]
10 };
11 let operation = [
12 {
13 type: "move",
14 path: "Patient.name[0].given",
15 source: 2,
16 destination: 0
17 }
18 ];
19
20 fhirpatch(operation, patient);
21 expect(patient).toEqual({
22 resourceType: "Patient",
23 name: [{ given: ["Miller", "James", "Michael", "Mik"] }]
24 });
25
26 fhirpatch(
27 [
28 {
29 type: "move",
30 path: "Patient.name[0].given",
31 source: 1,
32 destination: 3
33 }
34 ],
35 patient
36 );
37 expect(patient).toEqual({
38 resourceType: "Patient",
39 name: [{ given: ["Miller", "Michael", "Mik", "James"] }]
40 });
41});