diff --git a/assets/api/writer-generator/typescript/profile-helpers.ts b/assets/api/writer-generator/typescript/profile-helpers.ts index 72164416..31da3e89 100644 --- a/assets/api/writer-generator/typescript/profile-helpers.ts +++ b/assets/api/writer-generator/typescript/profile-helpers.ts @@ -437,6 +437,17 @@ export const validateChoiceRequired = (res: object, profileName: string, choices : [`${profileName}: at least one of ${choices.join(", ")} is required`]; }; +/** + * Checks that none of the listed prohibited choice-type variants is present. + * E.g. a profile narrowing `value[x]` to `valueAddress` prohibits every other variant. + */ +export const validateChoiceProhibited = (res: object, profileName: string, prohibited: string[]): string[] => { + const rec = res as Record; + return prohibited + .filter((c) => rec[c] !== undefined) + .map((c) => `${profileName}: field '${c}' must not be present`); +}; + /** * Checks that the value of `field` has a code within `allowed`. * Handles plain strings, Coding objects, and CodeableConcept objects. diff --git a/bun.lock b/bun.lock index 427f77c7..6a3c8c69 100644 --- a/bun.lock +++ b/bun.lock @@ -6,7 +6,7 @@ "name": "@atomic-ehr/codegen", "dependencies": { "@atomic-ehr/fhir-canonical-manager": "0.0.24", - "@atomic-ehr/fhirschema": "0.0.11", + "@atomic-ehr/fhirschema": "0.0.13", "mustache": "^4.2.0", "picocolors": "^1.1.1", "yaml": "^2.9.0", @@ -35,7 +35,7 @@ "packages": { "@atomic-ehr/fhir-canonical-manager": ["@atomic-ehr/fhir-canonical-manager@0.0.24", "", { "peerDependencies": { "typescript": "^5" }, "bin": { "fcm": "dist/cli/index.js" } }, "sha512-3h+uGf3qqxNX2oClVx+Fz1NZ2Jm2h2dXKrbhA77gURmX5TUYYQKxdTh58a7N/tteLLsig5fW8q41wfeUqy7GdQ=="], - "@atomic-ehr/fhirschema": ["@atomic-ehr/fhirschema@0.0.11", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-oMNxhncEGspGI+QlK/FPjc7akLbfwMYw/hDfW6SbO8xF1KvSSH7NWqc3CJg/k5/309ZuJ6lKsHkgmgVDxo80sQ=="], + "@atomic-ehr/fhirschema": ["@atomic-ehr/fhirschema@0.0.13", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-4v5yLdvfeeXuXM3Y2AvNbukC5qiKa/Kts7kwe+8Mf0GqhvJwxjl2iHX4a/AZChFD7nkL02R5iEIpKKE3c7BFOg=="], "@biomejs/biome": ["@biomejs/biome@2.5.0", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.5.0", "@biomejs/cli-darwin-x64": "2.5.0", "@biomejs/cli-linux-arm64": "2.5.0", "@biomejs/cli-linux-arm64-musl": "2.5.0", "@biomejs/cli-linux-x64": "2.5.0", "@biomejs/cli-linux-x64-musl": "2.5.0", "@biomejs/cli-win32-arm64": "2.5.0", "@biomejs/cli-win32-x64": "2.5.0" }, "bin": { "biome": "bin/biome" } }, "sha512-4kURkd9hAPrdDM3C9n82ycYgx8hvQcW6MjKTEejruj8rK0N8P3OPpdy8BvI8kt3KWY4ycF5XtDOrktetEfhfuw=="], diff --git a/examples/python-r4-us-core/fhir_types/README.md b/examples/python-r4-us-core/fhir_types/README.md index eff69779..59fce1f6 100644 --- a/examples/python-r4-us-core/fhir_types/README.md +++ b/examples/python-r4-us-core/fhir_types/README.md @@ -2089,6 +2089,8 @@ - `http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus` - `http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus#code_binding` - `http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus#status_binding` +- `http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus#valueCodeableConcept_binding` +- `http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus#valueQuantity_binding` - `http://hl7.org/fhir/us/core/StructureDefinition/us-core-specimen` - `http://hl7.org/fhir/us/core/StructureDefinition/us-core-specimen#collection.bodySite_binding` - `http://hl7.org/fhir/us/core/StructureDefinition/us-core-specimen#condition_binding` @@ -3028,6 +3030,7 @@ - `urn:fhir:binding:specialty` - `urn:fhir:binding:status` - `urn:fhir:binding:strandType` +- `urn:fhir:binding:v3-Confidentiality` - `urn:fhir:binding:v3Act` - `urn:fhir:binding:validation-process` - `urn:fhir:binding:validation-status` @@ -5547,6 +5550,9 @@ and check `/collisions//1.json, 2.json, ...` files. - Version 1 (auto): Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1) - Version 2: Claim (hl7.fhir.r5.core#5.0.0), Claim (hl7.fhir.r5.core#5.0.0), Claim (hl7.fhir.r5.core#5.0.0), ClaimResponse (hl7.fhir.r5.core#5.0.0), ClaimResponse (hl7.fhir.r5.core#5.0.0), ClaimResponse (hl7.fhir.r5.core#5.0.0), ExplanationOfBenefit (hl7.fhir.r5.core#5.0.0), ExplanationOfBenefit (hl7.fhir.r5.core#5.0.0), ExplanationOfBenefit (hl7.fhir.r5.core#5.0.0) - Version 3: Claim (hl7.fhir.r4.examples#4.0.1), ClaimResponse (hl7.fhir.r4.examples#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.examples#4.0.1) +- `urn:fhir:binding:v3-Confidentiality` (2 versions) + - Version 1 (auto): confidential (hl7.fhir.uv.extensions.r4#5.2.0), confidential (hl7.fhir.uv.extensions.r4#5.2.0), confidential (hl7.fhir.uv.extensions.r4#5.2.0) + - Version 2: confidential (hl7.fhir.uv.extensions#5.1.0-snapshot1), confidential (hl7.fhir.uv.extensions#5.1.0-snapshot1) - `urn:fhir:binding:v3Act` (2 versions) - Version 1 (auto): DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1) - Version 2: DocumentManifest (hl7.fhir.r4.examples#4.0.1) @@ -8176,6 +8182,10 @@ Add to `.typeSchema({ resolveCollisions: { ... } })` to resolve remaining collis package: "hl7.fhir.r4.core#4.0.1", canonical: "http://hl7.org/fhir/StructureDefinition/Claim", }, + "urn:fhir:binding:v3-Confidentiality": { + package: "hl7.fhir.uv.extensions.r4#5.2.0", + canonical: "http://hl7.org/fhir/StructureDefinition/confidential", + }, "urn:fhir:binding:v3Act": { package: "hl7.fhir.r4.core#4.0.1", canonical: "http://hl7.org/fhir/StructureDefinition/DocumentManifest", diff --git a/examples/typescript-r4-us-core/fhir-types/README.md b/examples/typescript-r4-us-core/fhir-types/README.md index aac0db13..89632507 100644 --- a/examples/typescript-r4-us-core/fhir-types/README.md +++ b/examples/typescript-r4-us-core/fhir-types/README.md @@ -2081,6 +2081,8 @@ - `http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus` - `http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus#code_binding` - `http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus#status_binding` +- `http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus#valueCodeableConcept_binding` +- `http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus#valueQuantity_binding` - `http://hl7.org/fhir/us/core/StructureDefinition/us-core-specimen` - `http://hl7.org/fhir/us/core/StructureDefinition/us-core-specimen#collection.bodySite_binding` - `http://hl7.org/fhir/us/core/StructureDefinition/us-core-specimen#condition_binding` @@ -3022,6 +3024,7 @@ - `urn:fhir:binding:specialty` - `urn:fhir:binding:status` - `urn:fhir:binding:strandType` +- `urn:fhir:binding:v3-Confidentiality` - `urn:fhir:binding:v3Act` - `urn:fhir:binding:validation-process` - `urn:fhir:binding:validation-status` @@ -5541,6 +5544,9 @@ and check `/collisions//1.json, 2.json, ...` files. - Version 1 (auto): Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), Claim (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ClaimResponse (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.core#4.0.1) - Version 2: Claim (hl7.fhir.r5.core#5.0.0), Claim (hl7.fhir.r5.core#5.0.0), Claim (hl7.fhir.r5.core#5.0.0), ClaimResponse (hl7.fhir.r5.core#5.0.0), ClaimResponse (hl7.fhir.r5.core#5.0.0), ClaimResponse (hl7.fhir.r5.core#5.0.0), ExplanationOfBenefit (hl7.fhir.r5.core#5.0.0), ExplanationOfBenefit (hl7.fhir.r5.core#5.0.0), ExplanationOfBenefit (hl7.fhir.r5.core#5.0.0) - Version 3: Claim (hl7.fhir.r4.examples#4.0.1), ClaimResponse (hl7.fhir.r4.examples#4.0.1), ExplanationOfBenefit (hl7.fhir.r4.examples#4.0.1) +- `urn:fhir:binding:v3-Confidentiality` (2 versions) + - Version 1 (auto): confidential (hl7.fhir.uv.extensions.r4#5.2.0), confidential (hl7.fhir.uv.extensions.r4#5.2.0), confidential (hl7.fhir.uv.extensions.r4#5.2.0) + - Version 2: confidential (hl7.fhir.uv.extensions#5.1.0-snapshot1), confidential (hl7.fhir.uv.extensions#5.1.0-snapshot1) - `urn:fhir:binding:v3Act` (2 versions) - Version 1 (auto): DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1), DocumentManifest (hl7.fhir.r4.core#4.0.1) - Version 2: DocumentManifest (hl7.fhir.r4.examples#4.0.1) @@ -8154,6 +8160,10 @@ Add to `.typeSchema({ resolveCollisions: { ... } })` to resolve remaining collis package: "hl7.fhir.r4.core#4.0.1", canonical: "http://hl7.org/fhir/StructureDefinition/Claim", }, + "urn:fhir:binding:v3-Confidentiality": { + package: "hl7.fhir.uv.extensions.r4#5.2.0", + canonical: "http://hl7.org/fhir/StructureDefinition/confidential", + }, "urn:fhir:binding:v3Act": { package: "hl7.fhir.r4.core#4.0.1", canonical: "http://hl7.org/fhir/StructureDefinition/DocumentManifest", diff --git a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Extension_birthPlace.ts b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Extension_birthPlace.ts index 665eae66..320b0cfd 100644 --- a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Extension_birthPlace.ts +++ b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Extension_birthPlace.ts @@ -15,6 +15,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; @@ -92,6 +93,7 @@ export class birthPlaceProfile { ...validateRequired(res, profileName, "url"), ...validateFixedValue(res, profileName, "url", birthPlaceProfile.canonicalUrl), ...validateChoiceRequired(res, profileName, ["valueAddress"]), + ...validateChoiceProhibited(res, profileName, ["valueBase64Binary","valueBoolean","valueCanonical","valueCode","valueDate","valueDateTime","valueDecimal","valueId","valueInstant","valueInteger","valueMarkdown","valueOid","valuePositiveInt","valueString","valueTime","valueUnsignedInt","valueUri","valueUrl","valueUuid","valueAge","valueAnnotation","valueAttachment","valueCodeableConcept","valueCoding","valueContactPoint","valueCount","valueDistance","valueDuration","valueHumanName","valueIdentifier","valueMoney","valuePeriod","valueQuantity","valueRange","valueRatio","valueReference","valueSampledData","valueSignature","valueTiming","valueContactDetail","valueContributor","valueDataRequirement","valueExpression","valueParameterDefinition","valueRelatedArtifact","valueTriggerDefinition","valueUsageContext","valueDosage","valueMeta"]), ], warnings: [], } diff --git a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Extension_birthTime.ts b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Extension_birthTime.ts index 66177168..3a906f65 100644 --- a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Extension_birthTime.ts +++ b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Extension_birthTime.ts @@ -14,6 +14,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; @@ -91,6 +92,7 @@ export class birthTimeProfile { ...validateRequired(res, profileName, "url"), ...validateFixedValue(res, profileName, "url", birthTimeProfile.canonicalUrl), ...validateChoiceRequired(res, profileName, ["valueDateTime"]), + ...validateChoiceProhibited(res, profileName, ["valueBase64Binary","valueBoolean","valueCanonical","valueCode","valueDate","valueDecimal","valueId","valueInstant","valueInteger","valueMarkdown","valueOid","valuePositiveInt","valueString","valueTime","valueUnsignedInt","valueUri","valueUrl","valueUuid","valueAddress","valueAge","valueAnnotation","valueAttachment","valueCodeableConcept","valueCoding","valueContactPoint","valueCount","valueDistance","valueDuration","valueHumanName","valueIdentifier","valueMoney","valuePeriod","valueQuantity","valueRange","valueRatio","valueReference","valueSampledData","valueSignature","valueTiming","valueContactDetail","valueContributor","valueDataRequirement","valueExpression","valueParameterDefinition","valueRelatedArtifact","valueTriggerDefinition","valueUsageContext","valueDosage","valueMeta"]), ], warnings: [], } diff --git a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Extension_nationality.ts b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Extension_nationality.ts index 04a3210e..a53ef274 100644 --- a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Extension_nationality.ts +++ b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Extension_nationality.ts @@ -21,6 +21,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; diff --git a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Extension_own_prefix.ts b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Extension_own_prefix.ts index 41d42769..73122af9 100644 --- a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Extension_own_prefix.ts +++ b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Extension_own_prefix.ts @@ -14,6 +14,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; @@ -91,6 +92,7 @@ export class own_prefixProfile { ...validateRequired(res, profileName, "url"), ...validateFixedValue(res, profileName, "url", own_prefixProfile.canonicalUrl), ...validateChoiceRequired(res, profileName, ["valueString"]), + ...validateChoiceProhibited(res, profileName, ["valueBase64Binary","valueBoolean","valueCanonical","valueCode","valueDate","valueDateTime","valueDecimal","valueId","valueInstant","valueInteger","valueMarkdown","valueOid","valuePositiveInt","valueTime","valueUnsignedInt","valueUri","valueUrl","valueUuid","valueAddress","valueAge","valueAnnotation","valueAttachment","valueCodeableConcept","valueCoding","valueContactPoint","valueCount","valueDistance","valueDuration","valueHumanName","valueIdentifier","valueMoney","valuePeriod","valueQuantity","valueRange","valueRatio","valueReference","valueSampledData","valueSignature","valueTiming","valueContactDetail","valueContributor","valueDataRequirement","valueExpression","valueParameterDefinition","valueRelatedArtifact","valueTriggerDefinition","valueUsageContext","valueDosage","valueMeta"]), ], warnings: [], } diff --git a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bodyweight.ts b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bodyweight.ts index 1715c895..ea5354a5 100644 --- a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bodyweight.ts +++ b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bodyweight.ts @@ -29,6 +29,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; @@ -210,6 +211,7 @@ export class observation_bodyweightProfile { ...validateRequired(res, profileName, "subject"), ...validateReference(res, profileName, "subject", ["Patient"]), ...validateChoiceRequired(res, profileName, ["effectiveDateTime","effectivePeriod"]), + ...validateChoiceProhibited(res, profileName, ["effectiveTiming","effectiveInstant"]), ...validateReference(res, profileName, "hasMember", ["MolecularSequence","QuestionnaireResponse","Observation"]), ...validateReference(res, profileName, "derivedFrom", ["DocumentReference","ImagingStudy","Media","MolecularSequence","QuestionnaireResponse","Observation"]), ], diff --git a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bp.ts b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bp.ts index dc66a72c..4077ef52 100644 --- a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bp.ts +++ b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bp.ts @@ -37,6 +37,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; @@ -291,6 +292,7 @@ export class observation_bpProfile { ...validateRequired(res, profileName, "subject"), ...validateReference(res, profileName, "subject", ["Patient"]), ...validateChoiceRequired(res, profileName, ["effectiveDateTime","effectivePeriod"]), + ...validateChoiceProhibited(res, profileName, ["effectiveTiming","effectiveInstant"]), ...validateReference(res, profileName, "hasMember", ["MolecularSequence","QuestionnaireResponse","Observation"]), ...validateReference(res, profileName, "derivedFrom", ["DocumentReference","ImagingStudy","Media","MolecularSequence","QuestionnaireResponse","Observation"]), ...validateSliceCardinality(res, profileName, "component", {"code":{"coding":[{"code":"8480-6","system":"http://loinc.org"}]}}, "SystolicBP", 1, 1), diff --git a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts index 66f29771..a1bdfb8b 100644 --- a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts +++ b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts @@ -27,6 +27,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; @@ -203,6 +204,7 @@ export class observation_vitalsignsProfile { ...validateRequired(res, profileName, "subject"), ...validateReference(res, profileName, "subject", ["Patient"]), ...validateChoiceRequired(res, profileName, ["effectiveDateTime","effectivePeriod"]), + ...validateChoiceProhibited(res, profileName, ["effectiveTiming","effectiveInstant"]), ...validateReference(res, profileName, "hasMember", ["MolecularSequence","QuestionnaireResponse","Observation"]), ...validateReference(res, profileName, "derivedFrom", ["DocumentReference","ImagingStudy","Media","MolecularSequence","QuestionnaireResponse","Observation"]), ], diff --git a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreEthnicityExtension.ts b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreEthnicityExtension.ts index 1ebe13ec..02d37f75 100644 --- a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreEthnicityExtension.ts +++ b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreEthnicityExtension.ts @@ -40,6 +40,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; diff --git a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreIndividualSexExtension.ts b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreIndividualSexExtension.ts index bc040e76..0106b586 100644 --- a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreIndividualSexExtension.ts +++ b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreIndividualSexExtension.ts @@ -15,6 +15,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; @@ -92,6 +93,7 @@ export class USCoreIndividualSexExtensionProfile { ...validateRequired(res, profileName, "url"), ...validateFixedValue(res, profileName, "url", USCoreIndividualSexExtensionProfile.canonicalUrl), ...validateChoiceRequired(res, profileName, ["valueCoding"]), + ...validateChoiceProhibited(res, profileName, ["valueBase64Binary","valueBoolean","valueCanonical","valueCode","valueDate","valueDateTime","valueDecimal","valueId","valueInstant","valueInteger","valueMarkdown","valueOid","valuePositiveInt","valueString","valueTime","valueUnsignedInt","valueUri","valueUrl","valueUuid","valueAddress","valueAge","valueAnnotation","valueAttachment","valueCodeableConcept","valueContactPoint","valueCount","valueDistance","valueDuration","valueHumanName","valueIdentifier","valueMoney","valuePeriod","valueQuantity","valueRange","valueRatio","valueReference","valueSampledData","valueSignature","valueTiming","valueContactDetail","valueContributor","valueDataRequirement","valueExpression","valueParameterDefinition","valueRelatedArtifact","valueTriggerDefinition","valueUsageContext","valueDosage","valueMeta"]), ], warnings: [], } diff --git a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreInterpreterNeededExtension.ts b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreInterpreterNeededExtension.ts index c6f1c9b7..61fd6e7f 100644 --- a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreInterpreterNeededExtension.ts +++ b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreInterpreterNeededExtension.ts @@ -15,6 +15,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; @@ -92,6 +93,7 @@ export class USCoreInterpreterNeededExtensionProfile { ...validateRequired(res, profileName, "url"), ...validateFixedValue(res, profileName, "url", USCoreInterpreterNeededExtensionProfile.canonicalUrl), ...validateChoiceRequired(res, profileName, ["valueCoding"]), + ...validateChoiceProhibited(res, profileName, ["valueBase64Binary","valueBoolean","valueCanonical","valueCode","valueDate","valueDateTime","valueDecimal","valueId","valueInstant","valueInteger","valueMarkdown","valueOid","valuePositiveInt","valueString","valueTime","valueUnsignedInt","valueUri","valueUrl","valueUuid","valueAddress","valueAge","valueAnnotation","valueAttachment","valueCodeableConcept","valueContactPoint","valueCount","valueDistance","valueDuration","valueHumanName","valueIdentifier","valueMoney","valuePeriod","valueQuantity","valueRange","valueRatio","valueReference","valueSampledData","valueSignature","valueTiming","valueContactDetail","valueContributor","valueDataRequirement","valueExpression","valueParameterDefinition","valueRelatedArtifact","valueTriggerDefinition","valueUsageContext","valueDosage","valueMeta"]), ], warnings: [], } diff --git a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreRaceExtension.ts b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreRaceExtension.ts index 75eeb957..3b342f17 100644 --- a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreRaceExtension.ts +++ b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreRaceExtension.ts @@ -40,6 +40,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; diff --git a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreTribalAffiliationExtension.ts b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreTribalAffiliationExtension.ts index c333f1df..af73bcca 100644 --- a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreTribalAffiliationExtension.ts +++ b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreTribalAffiliationExtension.ts @@ -35,6 +35,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; diff --git a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBloodPressureProfile.ts b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBloodPressureProfile.ts index d46ab83f..cf2750a7 100644 --- a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBloodPressureProfile.ts +++ b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBloodPressureProfile.ts @@ -40,6 +40,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; @@ -409,6 +410,7 @@ export class USCoreBloodPressureProfile { ...validateRequired(res, profileName, "subject"), ...validateReference(res, profileName, "subject", ["Patient"]), ...validateChoiceRequired(res, profileName, ["effectiveDateTime","effectivePeriod"]), + ...validateChoiceProhibited(res, profileName, ["effectiveTiming","effectiveInstant"]), ...validateReference(res, profileName, "hasMember", ["MolecularSequence","QuestionnaireResponse","Observation"]), ...validateReference(res, profileName, "derivedFrom", ["DocumentReference","ImagingStudy","Media","MolecularSequence","QuestionnaireResponse","Observation"]), ...validateSliceCardinality(res, profileName, "component", {"code":{"coding":[{"system":"http://loinc.org","code":"8480-6"}]}}, "systolic", 1, 1), diff --git a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBodyWeightProfile.ts b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBodyWeightProfile.ts index 6b51714b..9b31b34c 100644 --- a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBodyWeightProfile.ts +++ b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBodyWeightProfile.ts @@ -32,6 +32,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; @@ -328,19 +329,11 @@ export class USCoreBodyWeightProfile { ...validateRequired(res, profileName, "subject"), ...validateReference(res, profileName, "subject", ["Patient"]), ...validateChoiceRequired(res, profileName, ["effectiveDateTime","effectivePeriod"]), + ...validateChoiceProhibited(res, profileName, ["effectiveTiming","effectiveInstant"]), ...validateReference(res, profileName, "hasMember", ["MolecularSequence","QuestionnaireResponse","Observation"]), ...validateReference(res, profileName, "derivedFrom", ["DocumentReference","ImagingStudy","Media","MolecularSequence","QuestionnaireResponse","Observation"]), ...validateReference(res, profileName, "performer", ["PractitionerRole","USCoreCareTeam","USCoreOrganizationProfile","Patient","USCorePractitionerProfile","USCoreRelatedPersonProfile"]), - ...validateExcluded(res, profileName, "valueCodeableConcept"), - ...validateExcluded(res, profileName, "valueString"), - ...validateExcluded(res, profileName, "valueBoolean"), - ...validateExcluded(res, profileName, "valueInteger"), - ...validateExcluded(res, profileName, "valueRange"), - ...validateExcluded(res, profileName, "valueRatio"), - ...validateExcluded(res, profileName, "valueSampledData"), - ...validateExcluded(res, profileName, "valueTime"), - ...validateExcluded(res, profileName, "valueDateTime"), - ...validateExcluded(res, profileName, "valuePeriod"), + ...validateChoiceProhibited(res, profileName, ["valueCodeableConcept","valueString","valueBoolean","valueInteger","valueRange","valueRatio","valueSampledData","valueTime","valueDateTime","valuePeriod"]), ], warnings: [ ...validateEnum(res, profileName, "category", ["social-history","vital-signs","imaging","laboratory","procedure","survey","exam","therapy","activity"]), diff --git a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreVitalSignsProfile.ts b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreVitalSignsProfile.ts index b9b41eec..38c052e2 100644 --- a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreVitalSignsProfile.ts +++ b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreVitalSignsProfile.ts @@ -31,6 +31,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; @@ -331,6 +332,7 @@ export class USCoreVitalSignsProfile { ...validateRequired(res, profileName, "subject"), ...validateReference(res, profileName, "subject", ["Patient"]), ...validateChoiceRequired(res, profileName, ["effectiveDateTime","effectivePeriod"]), + ...validateChoiceProhibited(res, profileName, ["effectiveTiming","effectiveInstant"]), ...validateReference(res, profileName, "hasMember", ["MolecularSequence","QuestionnaireResponse","Observation"]), ...validateReference(res, profileName, "derivedFrom", ["DocumentReference","ImagingStudy","Media","MolecularSequence","QuestionnaireResponse","Observation"]), ...validateReference(res, profileName, "performer", ["PractitionerRole","USCoreCareTeam","USCoreOrganizationProfile","Patient","USCorePractitionerProfile","USCoreRelatedPersonProfile"]), diff --git a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Patient_USCorePatientProfile.ts b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Patient_USCorePatientProfile.ts index f9d57316..09ada35e 100644 --- a/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Patient_USCorePatientProfile.ts +++ b/examples/typescript-r4-us-core/fhir-types/hl7-fhir-us-core/profiles/Patient_USCorePatientProfile.ts @@ -36,6 +36,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; diff --git a/examples/typescript-r4-us-core/fhir-types/profile-helpers.ts b/examples/typescript-r4-us-core/fhir-types/profile-helpers.ts index 72164416..31da3e89 100644 --- a/examples/typescript-r4-us-core/fhir-types/profile-helpers.ts +++ b/examples/typescript-r4-us-core/fhir-types/profile-helpers.ts @@ -437,6 +437,17 @@ export const validateChoiceRequired = (res: object, profileName: string, choices : [`${profileName}: at least one of ${choices.join(", ")} is required`]; }; +/** + * Checks that none of the listed prohibited choice-type variants is present. + * E.g. a profile narrowing `value[x]` to `valueAddress` prohibits every other variant. + */ +export const validateChoiceProhibited = (res: object, profileName: string, prohibited: string[]): string[] => { + const rec = res as Record; + return prohibited + .filter((c) => rec[c] !== undefined) + .map((c) => `${profileName}: field '${c}' must not be present`); +}; + /** * Checks that the value of `field` has a code within `allowed`. * Handles plain strings, Coding objects, and CodeableConcept objects. diff --git a/examples/typescript-r4-us-core/generate.ts b/examples/typescript-r4-us-core/generate.ts index 4d44342c..1d756e77 100644 --- a/examples/typescript-r4-us-core/generate.ts +++ b/examples/typescript-r4-us-core/generate.ts @@ -65,7 +65,7 @@ if (require.main === module) { }, }) .introspection({ - typeSchemas: "type-schemas", + typeSchemas: { target: "type-schemas", profileSnapshots: true }, typeTree: "type-tree.yaml", }) .outputTo("./examples/typescript-r4-us-core/fhir-types") diff --git a/package.json b/package.json index cf8340cb..4555be97 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "homepage": "https://github.com/atomic-ehr/codegen#readme", "dependencies": { "@atomic-ehr/fhir-canonical-manager": "0.0.24", - "@atomic-ehr/fhirschema": "0.0.11", + "@atomic-ehr/fhirschema": "0.0.13", "mustache": "^4.2.0", "picocolors": "^1.1.1", "yaml": "^2.9.0", diff --git a/src/api/writer-generator/introspection.ts b/src/api/writer-generator/introspection.ts index 8d4f6671..f74f3f98 100644 --- a/src/api/writer-generator/introspection.ts +++ b/src/api/writer-generator/introspection.ts @@ -1,13 +1,20 @@ import assert from "node:assert"; import * as Path from "node:path"; import type { RichFHIRSchema, RichStructureDefinition } from "@root/typeschema/types"; -import { type CanonicalUrl, extractNameFromCanonical, type TypeSchema } from "@root/typeschema/types"; +import { + type CanonicalUrl, + extractNameFromCanonical, + isSnapshotProfileTypeSchema, + type TypeSchema, +} from "@root/typeschema/types"; import type { TypeSchemaIndex } from "@root/typeschema/utils"; import YAML from "yaml"; import { FileSystemWriter, type FileSystemWriterOptions } from "./writer"; export interface IntrospectionWriterOptions extends FileSystemWriterOptions { - typeSchemas?: string /** if .ndjson -- put in one file, else -- split into separated files*/; + /** if target is .ndjson -- put in one file, else -- split into separated files; + * profileSnapshots additionally exports flattened profile snapshots next to profiles as .snapshot.json */ + typeSchemas?: string | { target: string; profileSnapshots?: boolean }; typeTree?: string /** .json or .yaml file */; fhirSchemas?: string /** if .ndjson -- put in one file, else -- split into separated files*/; structureDefinitions?: string /** if .ndjson -- put in one file, else -- split into separated files*/; @@ -21,7 +28,8 @@ const normalizeFileName = (str: string): string => { const typeSchemaToJson = (ts: TypeSchema, pretty: boolean): { filename: string; genContent: () => string } => { const pkgPath = normalizeFileName(ts.identifier.package); - const name = normalizeFileName(`${ts.identifier.name}(${extractNameFromCanonical(ts.identifier.url)})`); + const suffix = isSnapshotProfileTypeSchema(ts) ? ".snapshot" : ""; + const name = normalizeFileName(`${ts.identifier.name}(${extractNameFromCanonical(ts.identifier.url)})`) + suffix; const baseName = Path.join(pkgPath, name); return { @@ -65,10 +73,15 @@ export class IntrospectionWriter extends FileSystemWriter typeSchemaToJson(ts, true)); + const items = schemas.map((ts) => typeSchemaToJson(ts, true)); const seenFilenames = new Set(); const dedupedItems = items.filter((item) => { if (seenFilenames.has(item.filename)) return false; @@ -76,7 +89,7 @@ export class IntrospectionWriter extends FileSystemWriter { + this.cd(tsOpts.target, () => { for (const { filename, genContent } of dedupedItems) { const fileName = `${filename}.json`; this.cd(Path.dirname(fileName), () => { @@ -108,9 +121,7 @@ export class IntrospectionWriter extends FileSystemWriter 0) { diff --git a/src/typeschema/utils.ts b/src/typeschema/utils.ts index 35784fd1..ca8fb8c3 100644 --- a/src/typeschema/utils.ts +++ b/src/typeschema/utils.ts @@ -503,6 +503,7 @@ export const mkTypeSchemaIndex = ( const narrowMergedChoiceDeclarations = ( mergedFields: Record, constraintSchemas: TypeSchema[], + baseFields: Record = {}, ): Record => { const result = { ...mergedFields }; for (const [declName, declField] of Object.entries(result)) { @@ -524,17 +525,23 @@ export const mkTypeSchemaIndex = ( } } - // Compute prohibited for all choice declarations + // Compute prohibited for all choice declarations. Variants declared on + // the base specialization count even when the profile does not restate + // them — omitting a variant from a narrowed choice prohibits it. for (const [declName, declField] of Object.entries(result)) { if (!isChoiceDeclarationField(declField)) continue; const permitted = new Set(declField.excluded ? [] : declField.choices); - const prohibited = Object.entries(result) + const baseDecl = baseFields[declName]; + const baseChoices = isChoiceDeclarationField(baseDecl) ? baseDecl.choices : []; + const mergedInstances = Object.entries(result) .filter( (e): e is [string, ChoiceFieldInstance] => isChoiceInstanceField(e[1]) && e[1].choiceOf === declName, ) - .filter(([name]) => !permitted.has(name)) .map(([name]) => name); + const prohibited = [...new Set([...baseChoices, ...mergedInstances])].filter( + (name) => !permitted.has(name), + ); if (prohibited.length > 0) result[declName] = { ...declField, prohibited }; } @@ -570,7 +577,11 @@ export const mkTypeSchemaIndex = ( } } - const narrowedFields = narrowMergedChoiceDeclarations(mergedFields, constraintSchemas); + const narrowedFields = narrowMergedChoiceDeclarations( + mergedFields, + constraintSchemas, + (nonConstraintSchema as SpecializationTypeSchema).fields, + ); const dependencies = Object.values( Object.fromEntries( diff --git a/test/api/write-generator/__snapshots__/introspection.test.ts.snap b/test/api/write-generator/__snapshots__/introspection.test.ts.snap index 2c898125..b5b95968 100644 --- a/test/api/write-generator/__snapshots__/introspection.test.ts.snap +++ b/test/api/write-generator/__snapshots__/introspection.test.ts.snap @@ -2637,6 +2637,657 @@ exports[`IntrospectionWriter - TypeSchema output Check all introspection data in " `; +exports[`IntrospectionWriter - flat profile output Generated file list 1`] = ` +[ + "generated/introspection/hl7.fhir.r4.core/Annotation(Annotation).json", + "generated/introspection/hl7.fhir.r4.core/BackboneElement(BackboneElement).json", + "generated/introspection/hl7.fhir.r4.core/boolean(boolean).json", + "generated/introspection/hl7.fhir.r4.core/canonical(canonical).json", + "generated/introspection/hl7.fhir.r4.core/code(code).json", + "generated/introspection/hl7.fhir.r4.core/CodeableConcept(CodeableConcept).json", + "generated/introspection/hl7.fhir.r4.core/Coding(Coding).json", + "generated/introspection/hl7.fhir.r4.core/dateTime(dateTime).json", + "generated/introspection/hl7.fhir.r4.core/decimal(decimal).json", + "generated/introspection/hl7.fhir.r4.core/DomainResource(DomainResource).json", + "generated/introspection/hl7.fhir.r4.core/Duration(Duration).json", + "generated/introspection/hl7.fhir.r4.core/Element(Element).json", + "generated/introspection/hl7.fhir.r4.core/id(id).json", + "generated/introspection/hl7.fhir.r4.core/Identifier(Identifier).json", + "generated/introspection/hl7.fhir.r4.core/instant(instant).json", + "generated/introspection/hl7.fhir.r4.core/integer(integer).json", + "generated/introspection/hl7.fhir.r4.core/markdown(markdown).json", + "generated/introspection/hl7.fhir.r4.core/Meta(Meta).json", + "generated/introspection/hl7.fhir.r4.core/Narrative(Narrative).json", + "generated/introspection/hl7.fhir.r4.core/observation-bodyweight(bodyweight).json", + "generated/introspection/hl7.fhir.r4.core/observation-bodyweight(bodyweight).snapshot.json", + "generated/introspection/hl7.fhir.r4.core/observation-vitalsigns(vitalsigns).json", + "generated/introspection/hl7.fhir.r4.core/observation-vitalsigns(vitalsigns).snapshot.json", + "generated/introspection/hl7.fhir.r4.core/Observation(Observation).json", + "generated/introspection/hl7.fhir.r4.core/Period(Period).json", + "generated/introspection/hl7.fhir.r4.core/positiveInt(positiveInt).json", + "generated/introspection/hl7.fhir.r4.core/Quantity(Quantity).json", + "generated/introspection/hl7.fhir.r4.core/Range(Range).json", + "generated/introspection/hl7.fhir.r4.core/Ratio(Ratio).json", + "generated/introspection/hl7.fhir.r4.core/Reference(Reference).json", + "generated/introspection/hl7.fhir.r4.core/Resource(Resource).json", + "generated/introspection/hl7.fhir.r4.core/SampledData(SampledData).json", + "generated/introspection/hl7.fhir.r4.core/string(string).json", + "generated/introspection/hl7.fhir.r4.core/time(time).json", + "generated/introspection/hl7.fhir.r4.core/Timing(Timing).json", + "generated/introspection/hl7.fhir.r4.core/unsignedInt(unsignedInt).json", + "generated/introspection/hl7.fhir.r4.core/uri(uri).json", + "generated/introspection/hl7.fhir.r4.core/xhtml(xhtml).json", + "generated/introspection/shared/BodySite(urnfhirbindingBodySite).json", + "generated/introspection/shared/collisions/CommunicationReason(urnfhirbindingCommunicationReason)/1.json", + "generated/introspection/shared/collisions/CommunicationReason(urnfhirbindingCommunicationReason)/2.json", + "generated/introspection/shared/collisions/ObservationCategory(urnfhirbindingObservationCategory)/1.json", + "generated/introspection/shared/collisions/ObservationCategory(urnfhirbindingObservationCategory)/2.json", + "generated/introspection/shared/collisions/ObservationCategory(urnfhirbindingObservationCategory)/3.json", + "generated/introspection/shared/collisions/ObservationRangeMeaning(urnfhirbindingObservationRangeMeaning)/1.json", + "generated/introspection/shared/collisions/ObservationRangeMeaning(urnfhirbindingObservationRangeMeaning)/2.json", + "generated/introspection/shared/collisions/ObservationRangeMeaning(urnfhirbindingObservationRangeMeaning)/3.json", + "generated/introspection/shared/collisions/ObservationRangeMeaning(urnfhirbindingObservationRangeMeaning)/4.json", + "generated/introspection/shared/collisions/ObservationRangeMeaning(urnfhirbindingObservationRangeMeaning)/5.json", + "generated/introspection/shared/collisions/ObservationRangeMeaning(urnfhirbindingObservationRangeMeaning)/6.json", + "generated/introspection/shared/collisions/PaymentType(urnfhirbindingPaymentType)/1.json", + "generated/introspection/shared/collisions/PaymentType(urnfhirbindingPaymentType)/2.json", + "generated/introspection/shared/collisions/PaymentType(urnfhirbindingPaymentType)/3.json", + "generated/introspection/shared/collisions/ProcessPriority(urnfhirbindingProcessPriority)/1.json", + "generated/introspection/shared/collisions/ProcessPriority(urnfhirbindingProcessPriority)/2.json", + "generated/introspection/shared/collisions/ProcessPriority(urnfhirbindingProcessPriority)/3.json", + "generated/introspection/shared/collisions/TargetDisease(urnfhirbindingTargetDisease)/1.json", + "generated/introspection/shared/collisions/TargetDisease(urnfhirbindingTargetDisease)/2.json", + "generated/introspection/shared/DayOfWeek(urnfhirbindingDayOfWeek).json", + "generated/introspection/shared/EventTiming(urnfhirbindingEventTiming).json", + "generated/introspection/shared/FHIRResourceTypeExt(urnfhirbindingFHIRResourceTypeExt).json", + "generated/introspection/shared/IdentifierType(urnfhirbindingIdentifierType).json", + "generated/introspection/shared/IdentifierUse(urnfhirbindingIdentifierUse).json", + "generated/introspection/shared/Language(urnfhirbindingLanguage).json", + "generated/introspection/shared/NarrativeStatus(urnfhirbindingNarrativeStatus).json", + "generated/introspection/shared/ObservationCategory(urnfhirbindingObservationCategory).json", + "generated/introspection/shared/ObservationCode(urnfhirbindingObservationCode).json", + "generated/introspection/shared/ObservationInterpretation(urnfhirbindingObservationInterpretation).json", + "generated/introspection/shared/ObservationMethod(urnfhirbindingObservationMethod).json", + "generated/introspection/shared/ObservationRangeMeaning(urnfhirbindingObservationRangeMeaning).json", + "generated/introspection/shared/ObservationRangeType(urnfhirbindingObservationRangeType).json", + "generated/introspection/shared/ObservationStatus(urnfhirbindingObservationStatus).json", + "generated/introspection/shared/ObservationValueAbsentReason(urnfhirbindingObservationValueAbsentReason).json", + "generated/introspection/shared/QuantityComparator(urnfhirbindingQuantityComparator).json", + "generated/introspection/shared/SecurityLabels(urnfhirbindingSecurityLabels).json", + "generated/introspection/shared/Status(urnfhirbindingStatus).json", + "generated/introspection/shared/Tags(urnfhirbindingTags).json", + "generated/introspection/shared/TimingAbbreviation(urnfhirbindingTimingAbbreviation).json", + "generated/introspection/shared/UnitsOfTime(urnfhirbindingUnitsOfTime).json", + "generated/introspection/shared/VitalSigns(urnfhirbindingVitalSigns).json", + "generated/introspection.ndjson", +] +`; + +exports[`IntrospectionWriter - flat profile output Check bodyweight flat profile next to the regular profile 1`] = ` +"{ + "identifier": { + "kind": "profile-snapshot", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "observation-bodyweight", + "url": "http://hl7.org/fhir/StructureDefinition/bodyweight" + }, + "base": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Observation", + "url": "http://hl7.org/fhir/StructureDefinition/Observation" + }, + "description": "FHIR Body Weight Profile", + "fields": { + "status": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "code", + "url": "http://hl7.org/fhir/StructureDefinition/code" + }, + "required": true, + "excluded": false, + "array": false, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "Status", + "url": "urn:fhir:binding:Status" + }, + "enum": { + "isOpen": false, + "values": [ + "registered", + "preliminary", + "final", + "amended", + "corrected", + "cancelled", + "entered-in-error", + "unknown" + ] + }, + "mustSupport": true + }, + "category": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "CodeableConcept", + "url": "http://hl7.org/fhir/StructureDefinition/CodeableConcept" + }, + "required": true, + "excluded": false, + "array": true, + "min": 1, + "slicing": { + "discriminator": [ + { + "type": "value", + "path": "coding.code" + }, + { + "type": "value", + "path": "coding.system" + } + ], + "rules": "open", + "ordered": false, + "slices": { + "VSCat": { + "min": 1, + "max": 1, + "match": { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category" + } + ] + }, + "required": [ + "coding" + ], + "elements": [ + "coding" + ], + "nameCandidates": { + "candidates": [ + "VSCat", + "CategoryVSCat", + "CategoryVSCatSlice" + ], + "recommended": "VSCat" + } + } + } + }, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "ObservationCategory", + "url": "urn:fhir:binding:ObservationCategory" + }, + "enum": { + "isOpen": true, + "values": [ + "social-history", + "vital-signs", + "imaging", + "laboratory", + "procedure", + "survey", + "exam", + "therapy", + "activity" + ] + }, + "mustSupport": true + }, + "code": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "CodeableConcept", + "url": "http://hl7.org/fhir/StructureDefinition/CodeableConcept" + }, + "required": true, + "excluded": false, + "array": false, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "VitalSigns", + "url": "urn:fhir:binding:VitalSigns" + }, + "enum": { + "isOpen": true, + "values": [ + "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" + ] + }, + "valueConstraint": { + "kind": "fixed", + "type": "CodeableConcept", + "value": { + "coding": [ + { + "code": "29463-7", + "system": "http://loinc.org" + } + ] + } + }, + "mustSupport": true + }, + "subject": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Reference", + "url": "http://hl7.org/fhir/StructureDefinition/Reference" + }, + "required": true, + "excluded": false, + "reference": [ + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Patient", + "url": "http://hl7.org/fhir/StructureDefinition/Patient" + } + ], + "array": false, + "mustSupport": true + }, + "effective": { + "required": true, + "excluded": false, + "array": false, + "choices": [ + "effectiveDateTime", + "effectivePeriod" + ], + "mustSupport": true, + "prohibited": [ + "effectiveTiming", + "effectiveInstant" + ] + }, + "effectiveDateTime": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "dateTime", + "url": "http://hl7.org/fhir/StructureDefinition/dateTime" + }, + "required": false, + "excluded": false, + "array": false, + "choiceOf": "effective", + "mustSupport": true + }, + "effectivePeriod": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Period", + "url": "http://hl7.org/fhir/StructureDefinition/Period" + }, + "required": false, + "excluded": false, + "array": false, + "choiceOf": "effective", + "mustSupport": true + }, + "dataAbsentReason": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "CodeableConcept", + "url": "http://hl7.org/fhir/StructureDefinition/CodeableConcept" + }, + "required": false, + "excluded": false, + "array": false, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "ObservationValueAbsentReason", + "url": "urn:fhir:binding:ObservationValueAbsentReason" + }, + "enum": { + "isOpen": true, + "values": [ + "unknown", + "asked-unknown", + "temp-unknown", + "not-asked", + "asked-declined", + "masked", + "not-applicable", + "unsupported", + "as-text", + "error", + "not-a-number", + "negative-infinity", + "positive-infinity", + "not-performed", + "not-permitted" + ] + }, + "mustSupport": true + }, + "hasMember": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Reference", + "url": "http://hl7.org/fhir/StructureDefinition/Reference" + }, + "required": false, + "excluded": false, + "reference": [ + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "MolecularSequence", + "url": "http://hl7.org/fhir/StructureDefinition/MolecularSequence" + }, + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "QuestionnaireResponse", + "url": "http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse" + }, + { + "kind": "profile", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "observation-vitalsigns", + "url": "http://hl7.org/fhir/StructureDefinition/vitalsigns" + } + ], + "array": true + }, + "derivedFrom": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Reference", + "url": "http://hl7.org/fhir/StructureDefinition/Reference" + }, + "required": false, + "excluded": false, + "reference": [ + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "DocumentReference", + "url": "http://hl7.org/fhir/StructureDefinition/DocumentReference" + }, + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "ImagingStudy", + "url": "http://hl7.org/fhir/StructureDefinition/ImagingStudy" + }, + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Media", + "url": "http://hl7.org/fhir/StructureDefinition/Media" + }, + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "MolecularSequence", + "url": "http://hl7.org/fhir/StructureDefinition/MolecularSequence" + }, + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "QuestionnaireResponse", + "url": "http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse" + }, + { + "kind": "profile", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "observation-vitalsigns", + "url": "http://hl7.org/fhir/StructureDefinition/vitalsigns" + } + ], + "array": true + }, + "component": { + "type": { + "kind": "nested", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "component", + "url": "http://hl7.org/fhir/StructureDefinition/Observation#component" + }, + "array": true, + "required": false, + "excluded": false + }, + "valueQuantity": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Quantity", + "url": "http://hl7.org/fhir/StructureDefinition/Quantity" + }, + "required": false, + "excluded": false, + "array": false, + "choiceOf": "value" + } + }, + "dependencies": [ + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "CodeableConcept", + "url": "http://hl7.org/fhir/StructureDefinition/CodeableConcept" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Quantity", + "url": "http://hl7.org/fhir/StructureDefinition/Quantity" + }, + { + "kind": "profile", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "observation-vitalsigns", + "url": "http://hl7.org/fhir/StructureDefinition/vitalsigns" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "VitalSigns", + "url": "urn:fhir:binding:VitalSigns" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "BackboneElement", + "url": "http://hl7.org/fhir/StructureDefinition/BackboneElement" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "boolean", + "url": "http://hl7.org/fhir/StructureDefinition/boolean" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "code", + "url": "http://hl7.org/fhir/StructureDefinition/code" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "dateTime", + "url": "http://hl7.org/fhir/StructureDefinition/dateTime" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "integer", + "url": "http://hl7.org/fhir/StructureDefinition/integer" + }, + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Observation", + "url": "http://hl7.org/fhir/StructureDefinition/Observation" + }, + { + "kind": "nested", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "component", + "url": "http://hl7.org/fhir/StructureDefinition/Observation#component" + }, + { + "kind": "nested", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "referenceRange", + "url": "http://hl7.org/fhir/StructureDefinition/Observation#referenceRange" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Period", + "url": "http://hl7.org/fhir/StructureDefinition/Period" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Range", + "url": "http://hl7.org/fhir/StructureDefinition/Range" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Ratio", + "url": "http://hl7.org/fhir/StructureDefinition/Ratio" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Reference", + "url": "http://hl7.org/fhir/StructureDefinition/Reference" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "SampledData", + "url": "http://hl7.org/fhir/StructureDefinition/SampledData" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "string", + "url": "http://hl7.org/fhir/StructureDefinition/string" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "time", + "url": "http://hl7.org/fhir/StructureDefinition/time" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "ObservationCategory", + "url": "urn:fhir:binding:ObservationCategory" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "ObservationInterpretation", + "url": "urn:fhir:binding:ObservationInterpretation" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "ObservationValueAbsentReason", + "url": "urn:fhir:binding:ObservationValueAbsentReason" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "Status", + "url": "urn:fhir:binding:Status" + } + ] +}" +`; + exports[`IntrospectionWriter - StructureDefinition output Generated file list 1`] = ` [ "generated/structure-definitions/hl7.fhir.r4.core/BackboneElement(BackboneElement).json", diff --git a/test/api/write-generator/__snapshots__/typescript.test.ts.snap b/test/api/write-generator/__snapshots__/typescript.test.ts.snap index be239c4b..7b5daa37 100644 --- a/test/api/write-generator/__snapshots__/typescript.test.ts.snap +++ b/test/api/write-generator/__snapshots__/typescript.test.ts.snap @@ -532,6 +532,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; @@ -713,6 +714,7 @@ export class observation_bodyweightProfile { ...validateRequired(res, profileName, "subject"), ...validateReference(res, profileName, "subject", ["Patient"]), ...validateChoiceRequired(res, profileName, ["effectiveDateTime","effectivePeriod"]), + ...validateChoiceProhibited(res, profileName, ["effectiveTiming","effectiveInstant"]), ...validateReference(res, profileName, "hasMember", ["MolecularSequence","QuestionnaireResponse","Observation"]), ...validateReference(res, profileName, "derivedFrom", ["DocumentReference","ImagingStudy","Media","MolecularSequence","QuestionnaireResponse","Observation"]), ], @@ -770,6 +772,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; @@ -1024,6 +1027,7 @@ export class observation_bpProfile { ...validateRequired(res, profileName, "subject"), ...validateReference(res, profileName, "subject", ["Patient"]), ...validateChoiceRequired(res, profileName, ["effectiveDateTime","effectivePeriod"]), + ...validateChoiceProhibited(res, profileName, ["effectiveTiming","effectiveInstant"]), ...validateReference(res, profileName, "hasMember", ["MolecularSequence","QuestionnaireResponse","Observation"]), ...validateReference(res, profileName, "derivedFrom", ["DocumentReference","ImagingStudy","Media","MolecularSequence","QuestionnaireResponse","Observation"]), ...validateSliceCardinality(res, profileName, "component", {"code":{"coding":[{"code":"8480-6","system":"http://loinc.org"}]}}, "SystolicBP", 1, 1), @@ -1084,6 +1088,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; @@ -1351,6 +1356,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; @@ -1720,6 +1726,7 @@ export class USCoreBloodPressureProfile { ...validateRequired(res, profileName, "subject"), ...validateReference(res, profileName, "subject", ["Patient"]), ...validateChoiceRequired(res, profileName, ["effectiveDateTime","effectivePeriod"]), + ...validateChoiceProhibited(res, profileName, ["effectiveTiming","effectiveInstant"]), ...validateReference(res, profileName, "hasMember", ["MolecularSequence","QuestionnaireResponse","Observation"]), ...validateReference(res, profileName, "derivedFrom", ["DocumentReference","ImagingStudy","Media","MolecularSequence","QuestionnaireResponse","Observation"]), ...validateSliceCardinality(res, profileName, "component", {"code":{"coding":[{"system":"http://loinc.org","code":"8480-6"}]}}, "systolic", 1, 1), @@ -1778,6 +1785,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; @@ -2074,19 +2082,11 @@ export class USCoreBodyWeightProfile { ...validateRequired(res, profileName, "subject"), ...validateReference(res, profileName, "subject", ["Patient"]), ...validateChoiceRequired(res, profileName, ["effectiveDateTime","effectivePeriod"]), + ...validateChoiceProhibited(res, profileName, ["effectiveTiming","effectiveInstant"]), ...validateReference(res, profileName, "hasMember", ["MolecularSequence","QuestionnaireResponse","Observation"]), ...validateReference(res, profileName, "derivedFrom", ["DocumentReference","ImagingStudy","Media","MolecularSequence","QuestionnaireResponse","Observation"]), ...validateReference(res, profileName, "performer", ["PractitionerRole","USCoreCareTeam","USCoreOrganizationProfile","Patient","USCorePractitionerProfile","USCoreRelatedPersonProfile"]), - ...validateExcluded(res, profileName, "valueCodeableConcept"), - ...validateExcluded(res, profileName, "valueString"), - ...validateExcluded(res, profileName, "valueBoolean"), - ...validateExcluded(res, profileName, "valueInteger"), - ...validateExcluded(res, profileName, "valueRange"), - ...validateExcluded(res, profileName, "valueRatio"), - ...validateExcluded(res, profileName, "valueSampledData"), - ...validateExcluded(res, profileName, "valueTime"), - ...validateExcluded(res, profileName, "valueDateTime"), - ...validateExcluded(res, profileName, "valuePeriod"), + ...validateChoiceProhibited(res, profileName, ["valueCodeableConcept","valueString","valueBoolean","valueInteger","valueRange","valueRatio","valueSampledData","valueTime","valueDateTime","valuePeriod"]), ], warnings: [ ...validateEnum(res, profileName, "category", ["social-history","vital-signs","imaging","laboratory","procedure","survey","exam","therapy","activity"]), @@ -2146,6 +2146,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; diff --git a/test/api/write-generator/introspection.test.ts b/test/api/write-generator/introspection.test.ts index 451023bd..773271e4 100644 --- a/test/api/write-generator/introspection.test.ts +++ b/test/api/write-generator/introspection.test.ts @@ -90,6 +90,50 @@ describe("IntrospectionWriter - TypeSchema output", async () => { }); }); +describe("IntrospectionWriter - flat profile output", async () => { + const result = await new APIBuilder({ register: r4Manager, logger: mkErrorLogger() }) + .typeSchema({ + treeShake: { + "hl7.fhir.r4.core": { + "http://hl7.org/fhir/StructureDefinition/bodyweight": {}, + "http://hl7.org/fhir/StructureDefinition/DomainResource": { + ignoreFields: ["extension", "modifierExtension"], + }, + "http://hl7.org/fhir/StructureDefinition/BackboneElement": { + ignoreFields: ["modifierExtension"], + }, + "http://hl7.org/fhir/StructureDefinition/Element": { + ignoreFields: ["extension"], + }, + }, + }, + }) + .introspection({ typeSchemas: { target: "introspection", profileSnapshots: true } }) + .introspection({ typeSchemas: { target: "introspection.ndjson", profileSnapshots: true } }) + .generate(); + + const files = result.filesGenerated.introspection!; + + it("generates successfully", () => { + expect(result.success).toBeTrue(); + }); + + it("Generated file list", () => { + expect(Object.keys(files)).toMatchSnapshot(); + }); + it("Check bodyweight flat profile next to the regular profile", () => { + const profile = files["generated/introspection/hl7.fhir.r4.core/observation-bodyweight(bodyweight).json"]; + expect(profile).toBeDefined(); + const snapshot = + files["generated/introspection/hl7.fhir.r4.core/observation-bodyweight(bodyweight).snapshot.json"]; + expect(snapshot).toBeDefined(); + expect(snapshot).toMatchSnapshot(); + }); + it("Check flat profiles are included in the ndjson file", () => { + expect(files["generated/introspection.ndjson"]).toContain('"kind":"profile-snapshot"'); + }); +}); + describe("IntrospectionWriter - typeTree", async () => { const result = await new APIBuilder({ register: r4Manager, logger: mkErrorLogger() }) .typeSchema({ diff --git a/test/api/write-generator/multi-package/__snapshots__/local-package.test.ts.snap b/test/api/write-generator/multi-package/__snapshots__/local-package.test.ts.snap index a0e3ef88..94a6b778 100644 --- a/test/api/write-generator/multi-package/__snapshots__/local-package.test.ts.snap +++ b/test/api/write-generator/multi-package/__snapshots__/local-package.test.ts.snap @@ -60,6 +60,7 @@ import { validateEnum, validateReference, validateChoiceRequired, + validateChoiceProhibited, validateMustSupport, } from "../../profile-helpers"; diff --git a/test/unit/typeschema/utils.test.ts b/test/unit/typeschema/utils.test.ts index 20f024ee..aba29f25 100644 --- a/test/unit/typeschema/utils.test.ts +++ b/test/unit/typeschema/utils.test.ts @@ -33,6 +33,38 @@ const booleanType: TypeIdentifier = { url: "http://example.org/StructureDefinition/boolean" as CanonicalUrl, }; +const _dateTimeType: TypeIdentifier = { + name: "dateTime" as Name, + package: "test", + kind: "primitive-type", + version: "1.0.0", + url: "http://example.org/StructureDefinition/dateTime" as CanonicalUrl, +}; + +const _periodType: TypeIdentifier = { + name: "Period" as Name, + package: "test", + kind: "complex-type", + version: "1.0.0", + url: "http://example.org/StructureDefinition/Period" as CanonicalUrl, +}; + +const _rangeType: TypeIdentifier = { + name: "Range" as Name, + package: "test", + kind: "complex-type", + version: "1.0.0", + url: "http://example.org/StructureDefinition/Range" as CanonicalUrl, +}; + +const _ageType: TypeIdentifier = { + name: "Age" as Name, + package: "test", + kind: "complex-type", + version: "1.0.0", + url: "http://example.org/StructureDefinition/Age" as CanonicalUrl, +}; + describe("TypeSchema Index", () => { describe("hierarchy", () => { it("should return a single element hierarchy for a constraint resource", () => { @@ -781,4 +813,83 @@ describe("TypeSchema Index", () => { expect(snap.inheritedRequiredFields).not.toContain("value[x]"); }); }); + + describe("choice prohibited fields", () => { + it("keeps undeclared instances prohibited in a choice type", () => { + const baseSchema: SpecializationTypeSchema = { + identifier: { + name: "Base" as Name, + package: "test", + kind: "resource", + version: "1.0.0", + url: "http://example.org/StructureDefinition/Base" as CanonicalUrl, + }, + fields: { + onset: { + choices: ["onsetDateTime", "onsetPeriod", "onsetRange", "onsetAge"], + }, + }, + }; + const constraintSchema: ProfileTypeSchema = { + identifier: { + name: "Constraint" as Name, + package: "test", + kind: "profile", + version: "1.0.0", + url: "http://example.org/StructureDefinition/Constraint" as CanonicalUrl, + }, + base: baseSchema.identifier, + fields: { + onset: { + choices: ["onsetRange", "onsetAge"], + }, + }, + }; + const emptyTransitSchema: ProfileTypeSchema = { + identifier: { + name: "EmptyTransitSchema" as Name, + package: "test", + kind: "profile", + version: "1.0.0", + url: "http://example.org/StructureDefinition/EmptyTransitSchema" as CanonicalUrl, + }, + base: constraintSchema.identifier, + }; + const leafSchema: ProfileTypeSchema = { + identifier: { + name: "LeafConstraint" as Name, + package: "test", + kind: "profile", + version: "1.0.0", + url: "http://example.org/StructureDefinition/LeafConstraint" as CanonicalUrl, + }, + base: emptyTransitSchema.identifier, + fields: { + onset: { + choices: ["onsetAge"], + }, + }, + }; + + const tsIndex = mkTypeSchemaIndex([baseSchema, constraintSchema, emptyTransitSchema, leafSchema], {}); + + const flatConstraintSchema = tsIndex.flatProfile(constraintSchema); + expect(flatConstraintSchema.fields?.onset).toMatchObject({ + choices: ["onsetRange", "onsetAge"], + prohibited: ["onsetDateTime", "onsetPeriod"], + }); + + const flatEmptyTransitSchema = tsIndex.flatProfile(emptyTransitSchema); + expect(flatEmptyTransitSchema.fields?.onset).toMatchObject({ + choices: ["onsetRange", "onsetAge"], + prohibited: ["onsetDateTime", "onsetPeriod"], + }); + + const flatLeafSchema = tsIndex.flatProfile(leafSchema); + expect(flatLeafSchema.fields?.onset).toMatchObject({ + choices: ["onsetAge"], + prohibited: ["onsetDateTime", "onsetPeriod", "onsetRange"], + }); + }); + }); });