FHIR Chat · extension helpers · javascript

Stream: javascript

Topic: extension helpers


view this post on Zulip Brian Postlethwaite (Jan 24 2022 at 23:01):

I'm almost ready to post a small helper library to assist with handling fhir extensions in typescript/javascript - that will end up in npm.
This is a sample from the unit tests showing how its used

test('boolean get/set extension value', () => {
    const sample: fhir4.Coding = { system: 'system', code: 'c', display: 'blah' };
    expect(JSON.stringify(sample)).toBe('{"system":"system","code":"c","display":"blah"}');
    exHelpers.setExtension(sample, { url: 'exturl', valueBoolean: true });
    let result = exHelpers.getExtensionBooleanValue(sample, 'exturl');
    expect(JSON.stringify(sample)).toBe('{"system":"system","code":"c","display":"blah","extension":[{"url":"exturl","valueBoolean":true}]}');
    expect(result).toBe(true);

    exHelpers.setExtension(sample, { url: 'exturl', valueBoolean: false });
    result = exHelpers.getExtensionBooleanValue(sample, 'exturl');
    expect(JSON.stringify(sample)).toBe('{"system":"system","code":"c","display":"blah","extension":[{"url":"exturl","valueBoolean":false}]}');
    expect(result).toBe(false);

    exHelpers.clearExtension(sample, 'exturl');
    expect(JSON.stringify(sample)).toBe('{"system":"system","code":"c","display":"blah"}');
    result = exHelpers.getExtensionBooleanValue(sample, 'exturl');
    expect(result).toBe(undefined);
})

this then makes it pretty easy to make helper methods for handling standard extensions from IGs too

    export function getHidden(item?: QuestionnaireItem): boolean | undefined {
        return extensionHelpers.getExtensionBooleanValue(exturl_questionnaire_hidden, item);
    }

    export function setHidden(item: QuestionnaireItem, value: boolean | undefined) {
        extensionHelpers.setExtension(item, { url: exturl_questionnaire_hidden, valueBoolean: value });
    }

Wondering if anyone else has done something similar already?

view this post on Zulip Brian Postlethwaite (Jan 24 2022 at 23:01):

(I'm hoping to have the code all on github in the next day or so)

view this post on Zulip Brian Postlethwaite (Jan 24 2022 at 23:43):

Ok, so first commit is up there now, still doing more cleanup and docco, but the core routines work fine.
https://github.com/brianpos/fhir-extension-helpers

view this post on Zulip Josh Mandel (Jan 25 2022 at 02:40):

Nice! For my usage patterns, I find it's also helpful to have non-mutating versions of these (e.g., an addExtension that returns a new object that's like the old one + an extension).

view this post on Zulip Brian Postlethwaite (Jan 25 2022 at 03:50):

I've just pushed some updates with more helper methods, and an optional parameter to create the object if it didn't exist already.
Any suggestions on naming for non mutating versions?
(or just have another namespace for that?)

view this post on Zulip Brian Postlethwaite (Jan 25 2022 at 03:53):

My goal was for manipulating large objects, rather than re-creating an entire object.
Or did you think to have something more like:

let patient = {resourceType:'Patient'}
patient._birthDate = setExtension(patient._birthDate, 'https://example.org', 'newValue');

or

let patient = {resourceType:'Patient'}
patient = setExtension(patient, 'https://example.org', 'newValue');

Where the first is for a property extension, and the other is for a resource level extension
Doing this then clones the entire object.

view this post on Zulip Brian Postlethwaite (Jan 25 2022 at 03:53):

So the mutation can be chosen by the caller?

view this post on Zulip Brian Postlethwaite (Jan 25 2022 at 05:58):

And now up on npm too!
https://www.npmjs.com/package/fhir-extension-helpers

view this post on Zulip Josh Mandel (Jan 25 2022 at 13:56):

Awesome! Re mutations, your latter example was the kind of pattern I tend to use when available. Separate namespaces is a good thought.

view this post on Zulip Brian Postlethwaite (Jan 29 2022 at 01:18):

I've moved onto the SDC extension package...
Looking good but considering what to do for extensions that support multiple types?
I've generated this so far (which is just grabbing the first type)

// ----------------------------------------------------------------------
// maxValue
// date(0..1) - date, dateTime, time, instant, decimal, integer
// The inclusive upper bound on the range of allowed values for the data element.
export const exturl_MaxValue = "http://hl7.org/fhir/StructureDefinition/maxValue";

export function getMaxValue(element: Element | undefined): string | undefined {
    return extensionHelpers.getExtensionDateValue(element, exturl_MaxValue);
}

export function setMaxValue(element: Element, value: string) {
    return extensionHelpers.setExtension(element, { url: exturl_MaxValue, valueDate: value });
}

I'm thinking that the sensible thing for these cases would be to generate:

getMaxValueAsDate,
getMaxValueAsDateTime,
getMaxValueAsTime,
getMaxValueAsInstant,
getMaxValueAsDecimal,
getMaxValueAsInteger

@Paul Lynch thoughts?
(there are only 3 of these cases in SDC - but I expect this is likely elsewhere too)

view this post on Zulip Brian Postlethwaite (Jan 29 2022 at 01:37):

This is the current generated code for the SDC IG if anyone wants to have a look at it and make comments before I push it into an npm package...
https://gist.github.com/brianpos/94500f42591e97d67bcf771689ade0b7

Note that I'm not happy with the preferred terminology server extraction, I'd like to have a way that grabs either of the 2 URLs... thoughts on that part?

view this post on Zulip Ilya Beda (Jan 31 2022 at 13:36):

It looks good @Brian Postlethwaite

I just want to mention that there is an alternative way of working with extensions.
Instead of working with extensions in the runtime by getter/setter pair, you can extract all known extensions to the top level.
This approach is used by google https://github.com/google/fhir/blob/master/proto/google/fhir/proto/r4/uscore.proto#L7137 and aidbox https://docs.aidbox.app/modules-1/first-class-extensions

The main advantage is that you will get a strongly typed representation, based on a definition from a profile.

view this post on Zulip Brian Postlethwaite (Jan 31 2022 at 21:12):

My approach retains the fhir structure so other tools/utilities can work with it and doesn't need to be mutated again to/from the fhir representation.

view this post on Zulip Brian Postlethwaite (Jan 31 2022 at 21:15):

I do like both those approaches, but need to be all in using those. And then do conversions where you need to.
And have care that your profiled extensions don't clash with names.

view this post on Zulip Brian Postlethwaite (Jan 31 2022 at 21:15):

Thanks very much for the feedback too.
I might link the code whem I eventually do a post about them.

view this post on Zulip Paul Lynch (Feb 04 2022 at 15:56):

Brian Postlethwaite said:

I'm thinking that the sensible thing for these cases would be to generate:

getMaxValueAsDate,
getMaxValueAsDateTime,
getMaxValueAsTime,
getMaxValueAsInstant,
getMaxValueAsDecimal,
getMaxValueAsInteger

Paul Lynch thoughts?
(there are only 3 of these cases in SDC - but I expect this is likely elsewhere too)

It probably will not surprise Brian that my thought about this is that some languages care too much about types.

view this post on Zulip Brian Postlethwaite (Feb 04 2022 at 20:36):

Lol, it's not just types here, it's the prop name that's being read.

view this post on Zulip Brian Postlethwaite (Feb 08 2022 at 02:46):

Have pushed a draft of the SDC helpers too!
https://www.npmjs.com/package/fhir-sdc-helpers


Last updated: Apr 12 2022 at 19:14 UTC