Skip to content
Merged
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { applySliceMatch, matchesSlice, extractSliceSimplified, validateRequired

export type observation_bodyweightProfileParams = {
status: ("registered" | "preliminary" | "final" | "amended" | "corrected" | "cancelled" | "entered-in-error" | "unknown");
category: CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[];
subject: Reference<"Patient">;
}

Expand All @@ -44,9 +43,9 @@ export class observation_bodyweightProfile {
static createResource (args: observation_bodyweightProfileParams) : Observation {
const resource: Observation = {
resourceType: "Observation",
category: [{"coding":{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}}],
code: {"coding":[{"code":"29463-7","system":"http://loinc.org"}]},
status: args.status,
category: args.category,
subject: args.subject,
meta: { profile: ["http://hl7.org/fhir/StructureDefinition/bodyweight"] },
} as unknown as Observation
Expand All @@ -70,21 +69,21 @@ export class observation_bodyweightProfile {
return this
}

getCategory () : CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[] | undefined {
return this.resource.category as CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[] | undefined
getSubject () : Reference<"Patient"> | undefined {
return this.resource.subject as Reference<"Patient"> | undefined
}

setCategory (value: CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[]) : this {
Object.assign(this.resource, { category: value })
setSubject (value: Reference<"Patient">) : this {
Object.assign(this.resource, { subject: value })
return this
}

getSubject () : Reference<"Patient"> | undefined {
return this.resource.subject as Reference<"Patient"> | undefined
getCategory () : CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[] | undefined {
return this.resource.category as CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[] | undefined
}

setSubject (value: Reference<"Patient">) : this {
Object.assign(this.resource, { subject: value })
setCategory (value: CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[]) : this {
Object.assign(this.resource, { category: value })
return this
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import { applySliceMatch, matchesSlice, extractSliceSimplified, validateRequired

export type observation_bpProfileParams = {
status: ("registered" | "preliminary" | "final" | "amended" | "corrected" | "cancelled" | "entered-in-error" | "unknown");
category: CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[];
subject: Reference<"Patient">;
}

Expand All @@ -47,9 +46,10 @@ export class observation_bpProfile {
static createResource (args: observation_bpProfileParams) : Observation {
const resource: Observation = {
resourceType: "Observation",
category: [{"coding":{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}}],
code: {"coding":[{"code":"85354-9","system":"http://loinc.org"}]},
component: [{"code":{"coding":{"code":"8480-6","system":"http://loinc.org"}}},{"code":{"coding":{"code":"8462-4","system":"http://loinc.org"}}}],
status: args.status,
category: args.category,
subject: args.subject,
meta: { profile: ["http://hl7.org/fhir/StructureDefinition/bp"] },
} as unknown as Observation
Expand All @@ -73,21 +73,21 @@ export class observation_bpProfile {
return this
}

getCategory () : CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[] | undefined {
return this.resource.category as CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[] | undefined
getSubject () : Reference<"Patient"> | undefined {
return this.resource.subject as Reference<"Patient"> | undefined
}

setCategory (value: CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[]) : this {
Object.assign(this.resource, { category: value })
setSubject (value: Reference<"Patient">) : this {
Object.assign(this.resource, { subject: value })
return this
}

getSubject () : Reference<"Patient"> | undefined {
return this.resource.subject as Reference<"Patient"> | undefined
getCategory () : CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[] | undefined {
return this.resource.category as CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[] | undefined
}

setSubject (value: Reference<"Patient">) : this {
Object.assign(this.resource, { subject: value })
setCategory (value: CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[]) : this {
Object.assign(this.resource, { category: value })
return this
}

Expand All @@ -100,6 +100,15 @@ export class observation_bpProfile {
return this
}

getComponent () : ObservationComponent[] | undefined {
return this.resource.component as ObservationComponent[] | undefined
}

setComponent (value: ObservationComponent[]) : this {
Object.assign(this.resource, { component: value })
return this
}

getEffectiveDateTime () : string | undefined {
return this.resource.effectiveDateTime as string | undefined
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { applySliceMatch, matchesSlice, extractSliceSimplified, validateRequired

export type observation_vitalsignsProfileParams = {
status: ("registered" | "preliminary" | "final" | "amended" | "corrected" | "cancelled" | "entered-in-error" | "unknown");
category: CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[];
code: CodeableConcept<("85353-1" | "9279-1" | "8867-4" | "2708-6" | "8310-5" | "8302-2" | "9843-4" | "29463-7" | "39156-5" | "85354-9" | "8480-6" | "8462-4" | "8478-0" | string)>;
subject: Reference<"Patient">;
}
Expand All @@ -44,8 +43,8 @@ export class observation_vitalsignsProfile {
static createResource (args: observation_vitalsignsProfileParams) : Observation {
const resource: Observation = {
resourceType: "Observation",
category: [{"coding":{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}}],
status: args.status,
category: args.category,
code: args.code,
subject: args.subject,
meta: { profile: ["http://hl7.org/fhir/StructureDefinition/vitalsigns"] },
Expand All @@ -70,15 +69,6 @@ export class observation_vitalsignsProfile {
return this
}

getCategory () : CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[] | undefined {
return this.resource.category as CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[] | undefined
}

setCategory (value: CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[]) : this {
Object.assign(this.resource, { category: value })
return this
}

getCode () : CodeableConcept<("85353-1" | "9279-1" | "8867-4" | "2708-6" | "8310-5" | "8302-2" | "9843-4" | "29463-7" | "39156-5" | "85354-9" | "8480-6" | "8462-4" | "8478-0" | string)> | undefined {
return this.resource.code as CodeableConcept<("85353-1" | "9279-1" | "8867-4" | "2708-6" | "8310-5" | "8302-2" | "9843-4" | "29463-7" | "39156-5" | "85354-9" | "8480-6" | "8462-4" | "8478-0" | string)> | undefined
}
Expand All @@ -97,6 +87,15 @@ export class observation_vitalsignsProfile {
return this
}

getCategory () : CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[] | undefined {
return this.resource.category as CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[] | undefined
}

setCategory (value: CodeableConcept<("social-history" | "vital-signs" | "imaging" | "laboratory" | "procedure" | "survey" | "exam" | "therapy" | "activity" | string)>[]) : this {
Object.assign(this.resource, { category: value })
return this
}

getEffectiveDateTime () : string | undefined {
return this.resource.effectiveDateTime as string | undefined
}
Expand Down
32 changes: 15 additions & 17 deletions examples/typescript-r4/profile-bodyweight.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ describe("bodyweight profile creation", () => {
test("create() returns a profile wrapping the resource with auto-set code", () => {
const profile = bodyweightProfile.create({
status: "final",
category: [],
subject: { reference: "Patient/pt-1" },
});
fromCreate = profile.toResource();
Expand All @@ -29,7 +28,6 @@ describe("bodyweight profile creation", () => {
test("createResource() returns a plain Observation with auto-set code", () => {
fromCreateResource = bodyweightProfile.createResource({
status: "final",
category: [],
subject: { reference: "Patient/pt-1" },
});

Expand All @@ -46,7 +44,14 @@ describe("bodyweight profile creation", () => {
profile
.setStatus("final")
.setCode({ coding: [{ code: "29463-7", system: "http://loinc.org" }] })
.setCategory([])
.setCategory([
{
coding: {
code: "vital-signs",
system: "http://terminology.hl7.org/CodeSystem/observation-category",
},
} as any,
])
.setSubject({ reference: "Patient/pt-1" });

fromFrom = profile.toResource();
Expand All @@ -73,7 +78,6 @@ describe("bodyweight profile creation", () => {
describe("bodyweight profile getters and setters", () => {
const profile = bodyweightProfile.create({
status: "final",
category: [],
subject: { reference: "Patient/pt-1" },
});

Expand All @@ -93,7 +97,8 @@ describe("bodyweight profile getters and setters", () => {
});

test("getCategory / setCategory", () => {
expect(profile.getCategory()).toEqual([]);
// category is auto-populated with VSCat discriminator
expect(profile.getCategory()).toHaveLength(1);
const newCategory = [{ text: "Laboratory" }];
profile.setCategory(newCategory);
expect(profile.getCategory()).toEqual(newCategory);
Expand All @@ -109,23 +114,19 @@ describe("bodyweight profile getters and setters", () => {
describe("bodyweight profile slice accessors", () => {
const profile = bodyweightProfile.create({
status: "final",
category: [],
subject: { reference: "Patient/pt-1" },
});

test("getVscat returns undefined when no matching slice", () => {
expect(profile.getVscat()).toBeUndefined();
expect(profile.getVscatRaw()).toBeUndefined();
});

test("setVscat with no args inserts discriminator-only element", () => {
profile.setVscat();

test("getVscat returns empty simplified view from auto-populated stub", () => {
// category is auto-populated with VSCat discriminator match
expect(profile.getVscatRaw()).toBeDefined();
const raw = profile.getVscatRaw()!;
expect(raw.coding as unknown).toEqual({
code: "vital-signs",
system: "http://terminology.hl7.org/CodeSystem/observation-category",
});
// simplified view strips discriminator keys, leaving empty object
expect(profile.getVscat()).toEqual({});
});

test("setVscat adds category with discriminator values", () => {
Expand Down Expand Up @@ -162,7 +163,6 @@ describe("bodyweight profile slice accessors", () => {
describe("bodyweight profile choice type accessors", () => {
const profile = bodyweightProfile.create({
status: "final",
category: [],
subject: { reference: "Patient/pt-1" },
});

Expand Down Expand Up @@ -199,7 +199,6 @@ describe("bodyweight profile choice type accessors", () => {
test("choice accessors mutate the underlying resource", () => {
const obs = bodyweightProfile.createResource({
status: "final",
category: [],
subject: { reference: "Patient/pt-1" },
});
const p = bodyweightProfile.from(obs);
Expand All @@ -222,7 +221,6 @@ describe("bodyweight profile mutability", () => {
test("profile mutates the underlying resource", () => {
const obs = bodyweightProfile.createResource({
status: "final",
category: [],
subject: { reference: "Patient/pt-1" },
});
const profile = bodyweightProfile.from(obs);
Expand Down
27 changes: 13 additions & 14 deletions examples/typescript-r4/profile-bp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,17 @@
*/

import { describe, expect, test } from "bun:test";
import type { Observation } from "./fhir-types/hl7-fhir-r4-core/Observation";
import { observation_bpProfile as bpProfile } from "./fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bp";

const createBp = () =>
bpProfile.create({
status: "final",
category: [],
subject: { reference: "Patient/pt-1" },
});

describe("blood pressure profile", () => {
const profile = bpProfile.create({
status: "final",
category: [],
subject: { reference: "Patient/pt-1" },
});

Expand All @@ -32,20 +29,21 @@ describe("blood pressure profile", () => {
expect(obs.meta?.profile).toEqual(["http://hl7.org/fhir/StructureDefinition/bp"]);
});

test("freshly created profile is not yet valid (missing slices/effective)", () => {
test("freshly created profile is not yet valid (missing effective)", () => {
const errors = profile.validate();
expect(errors).toEqual([
"observation-bp.category: slice 'VSCat' requires at least 1 item(s), found 0",
"effective: at least one of effectiveDateTime, effectivePeriod is required",
"observation-bp.component: slice 'SystolicBP' requires at least 1 item(s), found 0",
"observation-bp.component: slice 'DiastolicBP' requires at least 1 item(s), found 0",
]);
expect(errors).toEqual(["effective: at least one of effectiveDateTime, effectivePeriod is required"]);
});

test("setSystolicBp / getSystolicBp / getSystolicBpRaw", () => {
expect(profile.getSystolicBp()).toBeUndefined();
expect(profile.getSystolicBpRaw()).toBeUndefined();
test("create() auto-populates component with systolic/diastolic stubs", () => {
const fresh = createBp();
const obs = fresh.toResource();
expect(obs.component).toHaveLength(2);
// stubs contain only discriminator match values
expect(fresh.getSystolicBpRaw()).toBeDefined();
expect(fresh.getDiastolicBpRaw()).toBeDefined();
});

test("setSystolicBp / getSystolicBp / getSystolicBpRaw", () => {
profile.setSystolicBp({
valueQuantity: { value: 120, unit: "mmHg", system: "http://unitsofmeasure.org", code: "mm[Hg]" },
});
Expand Down Expand Up @@ -75,8 +73,9 @@ describe("blood pressure profile", () => {
});
});

test("both systolic and diastolic populate the component array", () => {
test("both systolic and diastolic are in the component array", () => {
const obs = profile.toResource();
// auto-populated stubs + set values = still 2 items (set replaces stubs)
expect(obs.component).toHaveLength(2);

const systolicCode = profile.getSystolicBpRaw()!.code as Record<string, unknown>;
Expand Down
21 changes: 21 additions & 0 deletions src/api/writer-generator/typescript/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ const tryPromoteChoice = (
promotedChoices.add(choiceName);
};

const collectRequiredSliceMatches = (field: RegularField): Record<string, unknown>[] | undefined => {
if (!field.array || !field.slicing?.slices) return undefined;
const matches = Object.values(field.slicing.slices)
.filter((s) => s.min !== undefined && s.min >= 1 && s.match && Object.keys(s.match).length > 0)
.map((s) => s.match as Record<string, unknown>);
return matches.length > 0 ? matches : undefined;
};

const collectProfileFactoryInfo = (flatProfile: ProfileTypeSchema): ProfileFactoryInfo => {
const autoFields: ProfileFactoryInfo["autoFields"] = [];
const params: ProfileFactoryInfo["params"] = [];
Expand Down Expand Up @@ -105,6 +113,19 @@ const collectProfileFactoryInfo = (flatProfile: ProfileTypeSchema): ProfileFacto
continue;
}

if (isNotChoiceDeclarationField(field)) {
const requiredMatches = collectRequiredSliceMatches(field);
if (requiredMatches) {
const value = `[${requiredMatches.map((m) => JSON.stringify(m)).join(",")}]`;
autoFields.push({ name, value });
if (field.type) {
const tsType = resolveFieldTsType("", "", field) + (field.array ? "[]" : "");
autoAccessors.push({ name, tsType, typeId: field.type });
}
continue;
}
}

if (field.required) {
const tsType = resolveFieldTsType("", "", field) + (field.array ? "[]" : "");
params.push({ name, tsType, typeId: field.type });
Expand Down
Loading