Skip to content

Commit 605a816

Browse files
fhir: map primitive elements (#1678)
* feat: implement primitive mappings Signed-off-by: Hunter Achieng <achienghunter@gmail.com> * feat: update comments Signed-off-by: Hunter Achieng <achienghunter@gmail.com> * fix tests Signed-off-by: Hunter Achieng <achienghunter@gmail.com> * feat: update fhir-eswatini Signed-off-by: Hunter Achieng <achienghunter@gmail.com> * fix: remove skipped tests Signed-off-by: Hunter Achieng <achienghunter@gmail.com> * feat: fix primp value renaming Signed-off-by: Hunter Achieng <achienghunter@gmail.com> * fix: package.json Signed-off-by: Hunter Achieng <achienghunter@gmail.com> * feat: rebuild fhir-eswatini Signed-off-by: Hunter Achieng <achienghunter@gmail.com> * fix: remove nested `_birthTime` Signed-off-by: Hunter Achieng <achienghunter@gmail.com> * fix: add tests Signed-off-by: Hunter Achieng <achienghunter@gmail.com> * clean up tests * remove comment * fix: add tests and update docs Signed-off-by: Hunter Achieng <achienghunter@gmail.com> * bump fhir-gen tool * fhir-eswatini: update build meta * changeset --------- Signed-off-by: Hunter Achieng <achienghunter@gmail.com> Co-authored-by: Joe Clark <joe@openfn.org>
1 parent 92e0b72 commit 605a816

13 files changed

Lines changed: 409 additions & 56 deletions

File tree

.changeset/thick-carrots-design.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@openfn/language-fhir-eswatini': minor
3+
---
4+
5+
Add support for Patient.\_birthDate and Patient.\_birthTime

packages/fhir-eswatini/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
"type": "module",
1616
"fhir": {
1717
"specUrl": "http://172.209.216.154/definitions.json.zip",
18-
"adaptorGeneratedDate": "2026-06-02T15:23:39.432Z",
19-
"generatorVersion": "0.7.9",
18+
"adaptorGeneratedDate": "2026-06-30T11:22:16.070Z",
19+
"generatorVersion": "0.8.0",
2020
"options": {
2121
"base": "fhir-4"
2222
}
@@ -55,4 +55,4 @@
5555
"ast.json",
5656
"configuration-schema.json"
5757
]
58-
}
58+
}

packages/fhir-eswatini/src/builders.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,7 @@ export function organization(type: any, props?: any) {
573573
* @public
574574
* @function
575575
* @param {object} props - Properties to apply to the resource (includes common and custom properties).
576+
* @param {} [props._birthDate] - undefined
576577
* @param {boolean} [props.active] - Whether this patient's record is in active use
577578
* @param {Address} [props.address] - An address for the individual
578579
* @param {date} [props.birthDate] - Date of birth: YYYY-MM-DD

packages/fhir-eswatini/src/datatypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const {
77
coding,
88
composite,
99
concept,
10+
ensureConceptText,
1011
ext,
1112
extendSystemMap,
1213
extendValues,

packages/fhir-eswatini/src/profiles/SzPatient.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import type { builders as FHIR } from "@openfn/language-fhir-4";
99
type MaybeArray<T> = T | T[];
1010

1111
export type Patient_SzPatient_Props = {
12+
_birthDate?: any;
13+
_birthTime?: any;
1214
active?: boolean;
1315
address?: FHIR.Address[];
1416
birthDate?: string;
@@ -135,6 +137,37 @@ export default function(props: Partial<Patient_SzPatient_Props>) {
135137
}
136138
}
137139

140+
{
141+
if (!_.isNil(props._birthDate)) {
142+
if (_.isPlainObject(props._birthDate)) {
143+
resource._birthDate = Object.assign({}, props._birthDate);
144+
} else {
145+
delete resource._birthDate;
146+
resource._birthDate = {};
147+
148+
dt.addExtension(
149+
resource._birthDate,
150+
"http://hl7.org/fhir/StructureDefinition/patient-birthTime",
151+
props._birthDate
152+
);
153+
}
154+
}
155+
156+
if (!_.isNil(props._birthTime)) {
157+
delete resource._birthTime;
158+
159+
if (!resource._birthDate) {
160+
resource._birthDate = {};
161+
}
162+
163+
dt.addExtension(
164+
resource._birthDate,
165+
"http://hl7.org/fhir/StructureDefinition/patient-birthTime",
166+
props._birthTime
167+
);
168+
}
169+
}
170+
138171
if (!_.isNil(props.deceased)) {
139172
delete resource.deceased;
140173
dt.composite(resource, "deceased", props.deceased);

packages/fhir-eswatini/test/resources/Patient.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,4 +439,58 @@ describe('SzPatient', () => {
439439
valueDateTime: '2025-06-01T10:00:00Z',
440440
});
441441
});
442+
443+
it('should map _birthDate primitive extension shorthand', () => {
444+
const resource = b.patient('SzPatient', {
445+
birthDate: '10/07/1990',
446+
_birthDate: '2000-01-01T14:35:45-05:00',
447+
});
448+
449+
assert.deepEqual(resource.birthDate, '10/07/1990');
450+
assert.deepEqual(resource._birthDate, {
451+
extension: [
452+
{
453+
url: 'http://hl7.org/fhir/StructureDefinition/patient-birthTime',
454+
valueDateTime: '2000-01-01T14:35:45-05:00',
455+
},
456+
],
457+
});
458+
});
459+
460+
it('should map _birthTime shorthand into _birthDate extension', () => {
461+
const resource = b.patient('SzPatient', {
462+
birthDate: '10/07/1990',
463+
_birthTime: '2000-01-01T14:35:45-05:00',
464+
});
465+
466+
assert.deepEqual(resource.birthDate, '10/07/1990');
467+
assert.deepEqual(resource._birthDate, {
468+
extension: [
469+
{
470+
url: 'http://hl7.org/fhir/StructureDefinition/patient-birthTime',
471+
valueDateTime: '2000-01-01T14:35:45-05:00',
472+
},
473+
],
474+
});
475+
assert.equal(resource._birthTime, undefined);
476+
});
477+
478+
it('should pass through a full _birthDate extension object as-is', () => {
479+
const fullExtension = {
480+
extension: [
481+
{
482+
url: 'http://hl7.org/fhir/StructureDefinition/patient-birthTime',
483+
valueDateTime: '2000-01-01T14:35:45-05:00',
484+
},
485+
],
486+
};
487+
488+
const resource = b.patient('SzPatient', {
489+
birthDate: '10/07/1990',
490+
_birthDate: fullExtension,
491+
});
492+
493+
assert.deepEqual(resource.birthDate, '10/07/1990');
494+
assert.deepEqual(resource._birthDate, fullExtension);
495+
});
442496
});

packages/fhir-eswatini/types/builders.d.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,8 @@ declare type Organization_SzOrganization_Props = {
461461
};
462462

463463
declare type Patient_SzPatient_Props = {
464+
_birthDate?: any;
465+
_birthTime?: any;
464466
active?: boolean;
465467
address?: builders.Address[];
466468
birthDate?: string;
@@ -676,6 +678,7 @@ declare const cc: (codings: (builders.Coding | [string, string, Omit<builders.Co
676678
declare const coding: typeof builders.coding;
677679
declare const composite: (object: any, key: any, value: any) => void;
678680
declare const concept: (codings: (builders.Coding | [string, string, Omit<builders.Coding, "system" | "code">?]) | (builders.Coding | [string, string, Omit<builders.Coding, "system" | "code">?])[], extra?: Omit<builders.CodeableConcept, "coding">) => builders.CodeableConcept;
681+
declare const ensureConceptText: (concept: any) => void;
679682
declare const ext: (url: string, value: any, props?: Omit<builders.Extension, "url">) => {
680683
extension: ({
681684
url: string;
@@ -1058,6 +1061,7 @@ declare function organization(props: Organization_SzOrganization_Props): any;
10581061
* @public
10591062
* @function
10601063
* @param {object} props - Properties to apply to the resource (includes common and custom properties).
1064+
* @param {} [props._birthDate] - undefined
10611065
* @param {boolean} [props.active] - Whether this patient's record is in active use
10621066
* @param {Address} [props.address] - An address for the individual
10631067
* @param {date} [props.birthDate] - Date of birth: YYYY-MM-DD
@@ -1239,5 +1243,5 @@ declare function serviceRequest(type: "SzReferral", props: ServiceRequest_SzRefe
12391243
declare function specimen(type: "SzLabSpecimen", props: Specimen_SzLabSpecimen_Props): any;
12401244
declare function specimen(props: Specimen_SzLabSpecimen_Props): any;
12411245

1242-
export { addExtension, appointment, c, cc, coding, composite, concept, condition, encounter, episodeOfCare, ext, extendSystemMap, extendValues, extension, findExtension, id, identifier, location, lookupValue, mapSystems, mapValues, medication, medicationDispense, medicationRequest, observation, organization, patient, practitioner, procedure, ref, reference, serviceRequest, setSystemMap, setValues, specimen, value };
1246+
export { addExtension, appointment, c, cc, coding, composite, concept, condition, encounter, ensureConceptText, episodeOfCare, ext, extendSystemMap, extendValues, extension, findExtension, id, identifier, location, lookupValue, mapSystems, mapValues, medication, medicationDispense, medicationRequest, observation, organization, patient, practitioner, procedure, ref, reference, serviceRequest, setSystemMap, setValues, specimen, value };
12431247

tools/generate-fhir/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
## 0.7.8
44

5+
### Minor Changes
6+
7+
Add support for primitive extension properties (ie. `_birthTime`)
8+
9+
## 0.7.8
10+
511
### Patch Changes
612

713
- Generate typedefs for core data types

tools/generate-fhir/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@openfn/generate-fhir",
33
"private": true,
44
"type": "module",
5-
"version": "0.7.9",
5+
"version": "0.8.0",
66
"description": "Helper utility for fhir adaptor generator",
77
"main": "src/index.ts",
88
"license": "ISC",

tools/generate-fhir/src/codegen/generate-types.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,30 @@ export const generateType = (
5353
continue;
5454
}
5555

56+
// Primitive extension props (_birthDate etc) are typed as `any` --
57+
// their content depends on which extensions are present
58+
if ((s as any).isPrimitiveExtension) {
59+
// emit the container prop, eg _birthDate
60+
props.push(
61+
b.tsPropertySignature(
62+
b.identifier(key),
63+
b.tsTypeAnnotation(b.tsAnyKeyword()),
64+
true,
65+
),
66+
);
67+
// emit shorthand child props, eg _birthTime
68+
for (const childKey of Object.keys((s as any).typeDef || {})) {
69+
props.push(
70+
b.tsPropertySignature(
71+
b.identifier(`_${childKey}`),
72+
b.tsTypeAnnotation(b.tsAnyKeyword()),
73+
true,
74+
),
75+
);
76+
}
77+
continue;
78+
}
79+
5680
// TODO need to handle this stuff!
5781
// let type;
5882
// if (s.typeDef) {

0 commit comments

Comments
 (0)