Skip to content

Commit 6eaee8a

Browse files
committed
fix(ts-profile): preserve fixed CodeableConcept codings
Add a generic fixed-value merge helper for generated TypeScript profile apply() methods so fixed CodeableConcept codings are applied without dropping compatible caller-provided codings. Skip placeholder-only UNK ValueSet expansions when generating enum constraints. Bead: codegen-vwd
1 parent 7dfdb09 commit 6eaee8a

22 files changed

Lines changed: 280 additions & 62 deletions

assets/api/writer-generator/typescript/profile-helpers.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,42 @@ export const applySliceMatch = <T extends object>(input: Partial<T>, match: Part
9292
return result as T;
9393
};
9494

95+
const mergeFixedArray = (current: unknown[], fixed: unknown[]): unknown[] => {
96+
const result = [...current];
97+
for (const fixedItem of fixed) {
98+
const existingIndex = result.findIndex((item) => matchesValue(item, fixedItem));
99+
if (existingIndex === -1) {
100+
result.push(fixedItem);
101+
} else {
102+
result[existingIndex] = mergeFixedValue(result[existingIndex], fixedItem);
103+
}
104+
}
105+
return result;
106+
};
107+
108+
const mergeFixedValue = (current: unknown, fixed: unknown): unknown => {
109+
if (Array.isArray(current) && Array.isArray(fixed)) {
110+
return mergeFixedArray(current, fixed);
111+
}
112+
if (isRecord(current) && isRecord(fixed)) {
113+
const result: Record<string, unknown> = { ...current };
114+
for (const [key, fixedValue] of Object.entries(fixed)) {
115+
if (key === "__proto__" || key === "constructor" || key === "prototype") {
116+
continue;
117+
}
118+
result[key] = mergeFixedValue(result[key], fixedValue);
119+
}
120+
return result;
121+
}
122+
return fixed;
123+
};
124+
125+
/** Apply a generated fixed/profile value while preserving compatible existing data. */
126+
export const applyFixedValue = <T extends object>(resource: T, field: string, fixed: unknown): void => {
127+
const rec = resource as Record<string, unknown>;
128+
rec[field] = mergeFixedValue(rec[field], fixed);
129+
};
130+
95131
/**
96132
* Recursively test whether `value` structurally contains everything in
97133
* `match`. Arrays are matched with "every match item has a corresponding

examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Extension_birthPlace.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { Address } from "../../hl7-fhir-r4-core/Address";
66
import type { Extension } from "../../hl7-fhir-r4-core/Extension";
77

88
import {
9+
applyFixedValue,
910
validateRequired,
1011
validateExcluded,
1112
validateFixedValue,
@@ -45,9 +46,7 @@ export class birthPlaceProfile {
4546

4647
static apply (resource: Extension) : birthPlaceProfile {
4748
resource.url = birthPlaceProfile.canonicalUrl;
48-
Object.assign(resource, {
49-
url: "http://hl7.org/fhir/StructureDefinition/patient-birthPlace",
50-
})
49+
applyFixedValue(resource, "url", "http://hl7.org/fhir/StructureDefinition/patient-birthPlace");
5150
return new birthPlaceProfile(resource);
5251
}
5352

examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Extension_birthTime.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import type { Extension } from "../../hl7-fhir-r4-core/Extension";
66

77
import {
8+
applyFixedValue,
89
validateRequired,
910
validateExcluded,
1011
validateFixedValue,
@@ -44,9 +45,7 @@ export class birthTimeProfile {
4445

4546
static apply (resource: Extension) : birthTimeProfile {
4647
resource.url = birthTimeProfile.canonicalUrl;
47-
Object.assign(resource, {
48-
url: "http://hl7.org/fhir/StructureDefinition/patient-birthTime",
49-
})
48+
applyFixedValue(resource, "url", "http://hl7.org/fhir/StructureDefinition/patient-birthTime");
5049
return new birthTimeProfile(resource);
5150
}
5251

examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Extension_nationality.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { Period } from "../../hl7-fhir-r4-core/Period";
88

99
import {
1010
isRawExtensionInput,
11+
applyFixedValue,
1112
isExtension,
1213
getExtensionValue,
1314
pushExtension,
@@ -56,9 +57,7 @@ export class nationalityProfile {
5657

5758
static apply (resource: Extension) : nationalityProfile {
5859
resource.url = nationalityProfile.canonicalUrl;
59-
Object.assign(resource, {
60-
url: "http://hl7.org/fhir/StructureDefinition/patient-nationality",
61-
})
60+
applyFixedValue(resource, "url", "http://hl7.org/fhir/StructureDefinition/patient-nationality");
6261
return new nationalityProfile(resource);
6362
}
6463

examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Extension_own_prefix.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import type { Extension } from "../../hl7-fhir-r4-core/Extension";
66

77
import {
8+
applyFixedValue,
89
validateRequired,
910
validateExcluded,
1011
validateFixedValue,
@@ -44,9 +45,7 @@ export class own_prefixProfile {
4445

4546
static apply (resource: Extension) : own_prefixProfile {
4647
resource.url = own_prefixProfile.canonicalUrl;
47-
Object.assign(resource, {
48-
url: "http://hl7.org/fhir/StructureDefinition/humanname-own-prefix",
49-
})
48+
applyFixedValue(resource, "url", "http://hl7.org/fhir/StructureDefinition/humanname-own-prefix");
5049
return new own_prefixProfile(resource);
5150
}
5251

examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bodyweight.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export type Observation_bodyweight_Category_VSCatSliceFlatAll = Observation_body
1515

1616
import {
1717
ensureProfile,
18+
applyFixedValue,
1819
applySliceMatch,
1920
matchesValue,
2021
setArraySlice,
@@ -70,9 +71,7 @@ export class observation_bodyweightProfile {
7071

7172
static apply (resource: Observation) : observation_bodyweightProfile {
7273
ensureProfile(resource, observation_bodyweightProfile.canonicalUrl);
73-
Object.assign(resource, {
74-
code: {"coding":[{"code":"29463-7","system":"http://loinc.org"}]},
75-
})
74+
applyFixedValue(resource, "code", {"coding":[{"code":"29463-7","system":"http://loinc.org"}]});
7675
resource.category = ensureSliceDefaults(
7776
[...(resource.category ?? [])],
7877
observation_bodyweightProfile.VSCatSliceMatch,

examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bp.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export type Observation_bp_Component_DiastolicBPSliceFlatAll = Observation_bp_Co
2121

2222
import {
2323
ensureProfile,
24+
applyFixedValue,
2425
applySliceMatch,
2526
matchesValue,
2627
setArraySlice,
@@ -85,9 +86,7 @@ export class observation_bpProfile {
8586

8687
static apply (resource: Observation) : observation_bpProfile {
8788
ensureProfile(resource, observation_bpProfile.canonicalUrl);
88-
Object.assign(resource, {
89-
code: {"coding":[{"code":"85354-9","system":"http://loinc.org"}]},
90-
})
89+
applyFixedValue(resource, "code", {"coding":[{"code":"85354-9","system":"http://loinc.org"}]});
9190
resource.category = ensureSliceDefaults(
9291
[...(resource.category ?? [])],
9392
observation_bpProfile.VSCatSliceMatch,

examples/typescript-r4/fhir-types/profile-helpers.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,42 @@ export const applySliceMatch = <T extends object>(input: Partial<T>, match: Part
9292
return result as T;
9393
};
9494

95+
const mergeFixedArray = (current: unknown[], fixed: unknown[]): unknown[] => {
96+
const result = [...current];
97+
for (const fixedItem of fixed) {
98+
const existingIndex = result.findIndex((item) => matchesValue(item, fixedItem));
99+
if (existingIndex === -1) {
100+
result.push(fixedItem);
101+
} else {
102+
result[existingIndex] = mergeFixedValue(result[existingIndex], fixedItem);
103+
}
104+
}
105+
return result;
106+
};
107+
108+
const mergeFixedValue = (current: unknown, fixed: unknown): unknown => {
109+
if (Array.isArray(current) && Array.isArray(fixed)) {
110+
return mergeFixedArray(current, fixed);
111+
}
112+
if (isRecord(current) && isRecord(fixed)) {
113+
const result: Record<string, unknown> = { ...current };
114+
for (const [key, fixedValue] of Object.entries(fixed)) {
115+
if (key === "__proto__" || key === "constructor" || key === "prototype") {
116+
continue;
117+
}
118+
result[key] = mergeFixedValue(result[key], fixedValue);
119+
}
120+
return result;
121+
}
122+
return fixed;
123+
};
124+
125+
/** Apply a generated fixed/profile value while preserving compatible existing data. */
126+
export const applyFixedValue = <T extends object>(resource: T, field: string, fixed: unknown): void => {
127+
const rec = resource as Record<string, unknown>;
128+
rec[field] = mergeFixedValue(rec[field], fixed);
129+
};
130+
95131
/**
96132
* Recursively test whether `value` structurally contains everything in
97133
* `match`. Arrays are matched with "every match item has a corresponding

examples/typescript-r4/profile-bodyweight.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ describe("factory method equivalence", () => {
182182
const fromCreate = bodyweightProfile.create(args).toResource();
183183
const fromCreateResource = bodyweightProfile.createResource(args);
184184
const fromApply = bodyweightProfile
185-
.apply({ resourceType: "Observation", status: "preliminary", code: { text: "Body weight" } })
185+
.apply({ resourceType: "Observation", status: "preliminary", code: {} })
186186
.setStatus("final")
187187
.setSubject({ reference: "Patient/pt-1" })
188188
.toResource();

examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreEthnicityExtension.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export type USCoreEthnicityExtension_Extension_TextSliceFlatAll = USCoreEthnicit
1818

1919
import {
2020
isRawExtensionInput,
21+
applyFixedValue,
2122
applySliceMatch,
2223
matchesValue,
2324
setArraySlice,
@@ -80,9 +81,7 @@ export class USCoreEthnicityExtensionProfile {
8081

8182
static apply (resource: Extension) : USCoreEthnicityExtensionProfile {
8283
resource.url = USCoreEthnicityExtensionProfile.canonicalUrl;
83-
Object.assign(resource, {
84-
url: "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity",
85-
})
84+
applyFixedValue(resource, "url", "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity");
8685
return new USCoreEthnicityExtensionProfile(resource);
8786
}
8887

0 commit comments

Comments
 (0)