Skip to content

Commit f9c36c8

Browse files
authored
Merge pull request #130 from atomic-ehr/fix-slice-field-validation
TS: Validate required fields inside slice elements
2 parents 5d6449d + 07e565e commit f9c36c8

26 files changed

Lines changed: 156 additions & 10 deletions

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,31 @@ export const validateSliceCardinality = (
378378
return errors;
379379
};
380380

381+
/**
382+
* Validate required fields within matched slice elements.
383+
* For each array item matching the discriminator, checks that the listed fields are present.
384+
*/
385+
export const validateSliceFields = (
386+
res: object,
387+
profileName: string,
388+
field: string,
389+
match: Record<string, unknown>,
390+
sliceName: string,
391+
requiredFields: string[],
392+
): string[] => {
393+
const items = (res as Record<string, unknown>)[field] as unknown[] | undefined;
394+
const errors: string[] = [];
395+
for (const item of (items ?? []).filter((item) => matchesValue(item, match))) {
396+
const obj = item as Record<string, unknown>;
397+
for (const rf of requiredFields) {
398+
if (obj[rf] === undefined || obj[rf] === null) {
399+
errors.push(`${profileName}.${field}[${sliceName}].${rf} is required`);
400+
}
401+
}
402+
}
403+
return errors;
404+
};
405+
381406
/**
382407
* Checks that at least one of the listed choice-type variants is present.
383408
* E.g. `["effectiveDateTime", "effectivePeriod"]`.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
validateExcluded,
1111
validateFixedValue,
1212
validateSliceCardinality,
13+
validateSliceFields,
1314
validateEnum,
1415
validateReference,
1516
validateChoiceRequired,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
validateExcluded,
1010
validateFixedValue,
1111
validateSliceCardinality,
12+
validateSliceFields,
1213
validateEnum,
1314
validateReference,
1415
validateChoiceRequired,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
validateExcluded,
1717
validateFixedValue,
1818
validateSliceCardinality,
19+
validateSliceFields,
1920
validateEnum,
2021
validateReference,
2122
validateChoiceRequired,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
validateExcluded,
1010
validateFixedValue,
1111
validateSliceCardinality,
12+
validateSliceFields,
1213
validateEnum,
1314
validateReference,
1415
validateChoiceRequired,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
validateExcluded,
2525
validateFixedValue,
2626
validateSliceCardinality,
27+
validateSliceFields,
2728
validateEnum,
2829
validateReference,
2930
validateChoiceRequired,

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
validateExcluded,
3333
validateFixedValue,
3434
validateSliceCardinality,
35+
validateSliceFields,
3536
validateEnum,
3637
validateReference,
3738
validateChoiceRequired,
@@ -280,7 +281,9 @@ export class observation_bpProfile {
280281
...validateReference(res, profileName, "hasMember", ["MolecularSequence","QuestionnaireResponse","Observation"]),
281282
...validateReference(res, profileName, "derivedFrom", ["DocumentReference","ImagingStudy","Media","MolecularSequence","QuestionnaireResponse","Observation"]),
282283
...validateSliceCardinality(res, profileName, "component", {"code":{"coding":[{"code":"8480-6","system":"http://loinc.org"}]}}, "SystolicBP", 1, 1),
284+
...validateSliceFields(res, profileName, "component", {"code":{"coding":[{"code":"8480-6","system":"http://loinc.org"}]}}, "SystolicBP", ["valueQuantity"]),
283285
...validateSliceCardinality(res, profileName, "component", {"code":{"coding":[{"code":"8462-4","system":"http://loinc.org"}]}}, "DiastolicBP", 1, 1),
286+
...validateSliceFields(res, profileName, "component", {"code":{"coding":[{"code":"8462-4","system":"http://loinc.org"}]}}, "DiastolicBP", ["valueQuantity"]),
284287
],
285288
warnings: [
286289
...validateEnum(res, profileName, "category", ["social-history","vital-signs","imaging","laboratory","procedure","survey","exam","therapy","activity"]),

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
validateExcluded,
2424
validateFixedValue,
2525
validateSliceCardinality,
26+
validateSliceFields,
2627
validateEnum,
2728
validateReference,
2829
validateChoiceRequired,

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,31 @@ export const validateSliceCardinality = (
378378
return errors;
379379
};
380380

381+
/**
382+
* Validate required fields within matched slice elements.
383+
* For each array item matching the discriminator, checks that the listed fields are present.
384+
*/
385+
export const validateSliceFields = (
386+
res: object,
387+
profileName: string,
388+
field: string,
389+
match: Record<string, unknown>,
390+
sliceName: string,
391+
requiredFields: string[],
392+
): string[] => {
393+
const items = (res as Record<string, unknown>)[field] as unknown[] | undefined;
394+
const errors: string[] = [];
395+
for (const item of (items ?? []).filter((item) => matchesValue(item, match))) {
396+
const obj = item as Record<string, unknown>;
397+
for (const rf of requiredFields) {
398+
if (obj[rf] === undefined || obj[rf] === null) {
399+
errors.push(`${profileName}.${field}[${sliceName}].${rf} is required`);
400+
}
401+
}
402+
}
403+
return errors;
404+
};
405+
381406
/**
382407
* Checks that at least one of the listed choice-type variants is present.
383408
* E.g. `["effectiveDateTime", "effectivePeriod"]`.

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ describe("demo: create a blood pressure observation", () => {
1414
subject: { reference: "Patient/pt-1" },
1515
});
1616

17-
// Not yet valid: effective[x] is required by the vitalsigns base profile
17+
// Not yet valid: effective[x] and component valueQuantity are required
1818
expect(profile.validate().errors).toEqual([
1919
"observation-bp: at least one of effectiveDateTime, effectivePeriod is required",
20+
"observation-bp.component[SystolicBP].valueQuantity is required",
21+
"observation-bp.component[DiastolicBP].valueQuantity is required",
2022
]);
2123

2224
// Fill in the remaining required fields
@@ -44,9 +46,11 @@ describe("demo: apply BP profile to an existing Observation", () => {
4446
// apply() adds meta.profile, sets fixed values (code), and auto-populates required slices
4547
const profile = bpProfile.apply(obs);
4648

47-
// Not yet valid: effective[x] is required
49+
// Not yet valid: effective[x] and component valueQuantity are required
4850
expect(profile.validate().errors).toEqual([
4951
"observation-bp: at least one of effectiveDateTime, effectivePeriod is required",
52+
"observation-bp.component[SystolicBP].valueQuantity is required",
53+
"observation-bp.component[DiastolicBP].valueQuantity is required",
5054
]);
5155

5256
// The profile mutates the original resource — no copy is made

0 commit comments

Comments
 (0)