Skip to content

zen-fhir/zen.fhir

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

About

zen.fhir is a zen-lang namespace that is used in all packages generated by zen-fhir tool.

zen.fhir extends zen/schema to allow describing FHIR-specific info such as bindings, references, extensions, etc.

Table of Contents

How to

This section contains examples of describing FHIR constraints with zen.

⚠️ Note that when you mention a new namespace you need to add it to :import.

Another common thing is :zen.fhir/version property of schemas. It defines which zen.fhir syntax version your schema will use. Currently, schemas are required to specify the version and the current version is ~:zen.fhir/version “0.5.0”~. In the future, this property may be deprecated in a backward-compatible way.

describe a profile on

existing FHIR resource type

Create a schema with the zen.fhir/profile-schema tag. The schema must contain :zen.fhir/profileUri, which will be referenced by resources with meta.profile property. Here’s an example of a profile on the Patient resource type:

{:ns MyProfiles
 :import #{zen.fhir hl7-fhir-r4-core.Patient}

 MyPatientProfile
 {:zen/tags #{zen/schema zen.fhir/profile-schema}
  :zen.fhir/version "0.5.0"
  :zen.fhir/profileUri "urn:fhir:extension:MyPatientProfile"
  :confirms #{hl7-fhir-r4-core.Patient/schema}}}

Note the :confirms #{hl7-fhir-r4-core.Patient/schema} part: it means that the data which will be validated by your schema also should be validated by the base Patient schema.

ℹ️ Note that in this case hl7-fhir-r4-core.Patient was added to the :import.

existing FHIR profile

This is done in the same way as adding a profile on a existing FHIR resource type but you need to set :confirms to a profile instead of a base schema

MyPatientProfileOnAProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
 :zen.fhir/version "0.5.0"
 :zen.fhir/profileUri "urn:fhir:extension:MyPatientProfileOnAProfile"
 :confirms #{MyPatientProfile}}

existing FHIR data type

describe a constraint

When you have a symbol with the zen.fhir/profile-schema tag you can describe constraints. The important thing to keep in mind is that :confirms property is not an inheritance. Your schema doesn’t implicitly receive any properties from schemas mentioned in :confirms. The effect of this is that to describe a constraint you need to explicitly describe some data types and your data structure. The next example will illustrate this.

require (cardinality 1..x)

In zen-lang to require a key to be present you need to set a :require property. Here’s the updated example of the profile on the Patient resource type with :active and :name keys required:

MyPatientProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
 :zen.fhir/version "0.5.0"
 :zen.fhir/profileUri "urn:fhir:extension:MyPatientProfile"
 :confirms #{hl7-fhir-r4-core.Patient/schema}
 :type zen/map
 :require #{:active :name}}

Note that we had to describe the :type zen/map to use the :require property. Your schema doesn’t implicitly inherit the :type zen/map from the :confirms #{hl7-fhir-r4-core.Patient/schema}. And because :require is a property of the zen/map type you need to specify the type explicitly in your schema.

forbid (cardinality 0..0)

Currently, there’s no way to forbid using an element in zen-lang. Here’s the issue to track the status of this feature.

min and max elements in an array

:type zen/vector provides :minItems and :maxItems properties. Example of limiting a Patient.name to exactly one element:

MyPatientProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
 :zen.fhir/version "0.5.0"
 :zen.fhir/profileUri "urn:fhir:extension:MyPatientProfile"
 :confirms #{hl7-fhir-r4-core.Patient/schema}
 :type zen/map
 :require #{:name}
 :keys {:name {:type zen/vector
               :minItems 1
               :maxItems 1}}}

Setting that a zen/vector has a minimum 1 element does not make a key that stores this value required to be present. We need to explicitly state that the key is also required.

value pattern

In FHIR profiles pattern[x] most of the time is used in slicings. :match property of zen/schema allows to describe a pattern matching.

Below is an example defining a pattern to the Observation.code element, the pattern describes that in the :coding array should be at least one object with ~:system “my-system”~ and ~:code “my-code”~:

MyObservationProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
 :zen.fhir/version "0.5.0"
 :zen.fhir/profileUri "urn:fhir:extension:MyObservationProfile"
 :confirms #{hl7-fhir-r4-core.Observation/schema}
 :type zen/map
 :keys {:code {:match {:coding #{{:system "my-system", :code "my-code"}}}}}}

:match pattern matching syntax is a recursive data structure that consists of several parts:

  • {} contains keys and their patterns applied to an object, each key from the {} should be present in the object and match the pattern. The object may contain any extra keys not mentioned in the pattern.
  • #{} contains patterns applied to arrays, for each pattern from the #{} there should be at least one match in a data array. The array may contain any other elements not matched by the pattern.
  • any other primitive value means that data should be a constant value

Break down of the example above:

  1. The pattern is {:coding #{{:system "my-system", :code "my-code"}}};
  2. Top level of the pattern is {:coding ...}, it expects data to be an object containing key :coding;
  3. To a value of the key :coding the pattern #{{:system ...}} is applied;
  4. The #{} syntax expects the data to be an array containing at least one match to the pattern {:system "my-system", :code "my-code"};
  5. {:system "my-system", :code "my-code"} expects data to be an object containing keys :system and :code with values “my-system” and “my-code” respectively.

fixed value

To define a fixed value use :const property of zen/schema allows to describe a constant value. The same way as patterns, value[x] most of the times is used in slicings and also to set an extension URL. Zen FHIR offers first-class extensions instead, in case you want to define an extension refer to the extension example. Here’s an example setting a value of the Observation.status element to be always final if it is present:

MyObservationProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
 :zen.fhir/version "0.5.0"
 :zen.fhir/profileUri "urn:fhir:extension:MyObservationProfile"
 :confirms #{hl7-fhir-r4-core.Observation/schema}
 :type zen/map
 :keys {:status {:const {:value "final"}}}}

reference to another resource

When you have a schema with the zen.fhir/profile-schema tag you can describe reference to another schema. Use :zen.fhir/reference property and describe set of profile names in :refers property. In this example, we have described the :managingOrganization field, which can only refer to the MyOrganizationProfileName profile.

MyPatientProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
 :zen.fhir/version "0.5.0"
 :zen.fhir/profileUri "urn:fhir:extension:MyPatientProfile"
 :confirms #{hl7-fhir-r4-core.Patient/schema}
 :type zen/map
 :keys {:managingOrganization {:confirms #{hl7-fhir-r4-core.Reference/schema zen.fhir/Reference}
                               :zen.fhir/reference {:refers #{MyOrganizationProfileName}}}}}
                               
MyOrganizationProfileName
{:zen/tags #{zen/schema zen.fhir/profile-schema}
 :zen.fhir/version "0.5.0"
 :zen.fhir/profileUri "urn:fhir:extension:MyOrganizationProfileName"
 :confirms #{hl7-fhir-r4-core.Organization/schema}}
                             

slicing on an array

You can define slicing in your schema with :slicing property which allows you to validate particular elements of vector using separate shemas for each element cathegory. To specify validation rules of single slice describe it under :slices property. Note that each slice should have :filter property which should contains :match property. Element becomes part of particular slice if specified data in :match property matches with element data.

MyObservation
{:zen/tags #{zen.fhir/profile-schema zen/schema},
 :zen.fhir/profileUri "urn:fhir:Observation"
 :zen.fhir/version "0.5.0"
 :confirms #{hl7-fhir-r4-core.Observation/schema}
 :type zen/map
 :keys {:code {:confirms #{hl7-fhir-r4-core.CodeableConcept/schema}
        :type zen/map
        :keys {:coding
                {:slicing
                 {:type zen/vector
                  :every {:confirms #{hl7-fhir-r4-core.Coding/schema}
                          :slices {"HeartRateCode"
                                   {:schema {:type zen/vector
                                             :minItems 1  
                                             :maxItems 1
                                             :every {:type zen/map
                                                     :keys {:system {:const {:value "http://loinc.org"}
                                                                     :confirms #{hl7-fhir-r4-core.uri/schema}}
                                                            :code   {:const {:value "8867-4"}
                                                                     :confirms #{hl7-fhir-r4-core.code/schema}}
                                                            :require #{:system :code}}}}
                                    :filter {:engine  :match,
                                             :match   {:code "8867-4", :system "http://loinc.org"}}}}}}}}}}}

binding to a value set

When you have a schema with the zen.fhir/profile-schema tag you can specify value-set. Use zen.fhir/value-set property and describe existing value-set name in :symbol property. You also need to specify one of the supported expansions of value-set in :strength property. Supported expansions:

  • :required To be conformant, the concept in this element SHALL be from the specified value set.
  • :extensible To be conformant, the concept in this element SHALL be from the specified value set if any of the codes within the value set can apply to the concept being communicated. If the value set does not cover the concept (based on human review), alternate codings (or, data type allowing, text) may be included instead.
  • :preferred Instances are encouraged to draw from the specified codes for interoperability purposes but are not required to do so to be considered conformant.
  • :example Instances are not expected or even encouraged to draw from the specified value set. The value set merely provides examples of the types of concepts intended to be included.
MyPatientProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
 :zen.fhir/version "0.5.0"
 :zen.fhir/profileUri "urn:fhir:extension:MyPatientProfile"
 :confirms #{hl7-fhir-r4-core.Patient/schema}
 :type zen/map
 :keys {:gender {:confirms #{hl7-fhir-r4-core.code/schema},
                 :zen.fhir/value-set {:symbol hl7-fhir-r4-core.administrative-gender/schema,
                                      :strength :required}}

invariant

describe a constraint to a nested element

As it was mentioned before, there’s no inheritance in zen-lang, thus to describe a constraint of a nested element you need to describe structure containing the element. The following example requires Patient.name.given to be present and contain at least one element:

MyPatientProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
 :zen.fhir/version "0.5.0"
 :zen.fhir/profileUri "urn:fhir:extension:MyPatientProfile"
 :confirms #{hl7-fhir-r4-core.Patient/schema}
 :type zen/map
 :require #{:name}
 :keys {:name {:type zen/vector
               :minItems 1
               :every {:type zen/map
                       :require #{:given}
                       :keys {:given {:type zen/vector
                                      :minItems 1}}}}}}

describe an extension

Zen FHIR offers first-class extensions approach instead of regular FHIR way via slicings. First-class extensions are described in the same way as any other attributes and constraints, but with addition of an extension url. The following example describes the us-core-race first-class extension right inside of a profile:

MyPatientProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
 :zen.fhir/version "0.5.0"
 :zen.fhir/profileUri "urn:fhir:extension:MyPatientProfile"
 :confirms #{hl7-fhir-r4-core.Patient/schema}
 :type zen/map
 :keys {:race
        {:type zen/map
         :zen/desc "US Core Race Extension",
         :fhir/extensionUri "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race"
         :require #{:text}
         :keys {:ombCategory {:type zen/vector
                              :maxItems 5
                              :every {:confirms #{hl7-fhir-r4-core.Coding/schema}
                                      :zen/desc "American Indian or Alaska Native|Asian|Black or African American|Native Hawaiian or Other Pacific Islander|White"
                                      :zen.fhir/value-set {:symbol omb-race-category-value-set
                                                           :strength :required}}}
                :detailed {:type zen/vector
                           :every {:confirms #{hl7-fhir-r4-core.Coding/schema}
                                   :zen/desc "Extended race codes"
                                   :zen.fhir/value-set {:symbol detailed-race-value-set
                                                        :strength :required}}}
                :text {:confirms #{hl7-fhir-r4-core.string/schema}
                       :zen/desc "Race Text"}}}}}

Note that extension elements have :confirms to a FHIR primitive or complex type specified. Previously these were specified in the base profile schema. These :confirms and the :fhir/extensionUri are needed to allow zen FHIR <-> FHIR format transformation

The structure defined by this schema describes data of such shape:

resourceType: Patient
id: sample-pt
race:
  category:
  - {system: 'urn:oid:2.16.840.1.113883.6.238', code: 2028-9, display: Asian}
  detailed:
  - {system: 'urn:oid:2.16.840.1.113883.6.238', code: 2029-7, display: Asian Indian}
  text: Asian Indian

This then can be converted to FHIR format and result in this

resourceType: Patient
id: sample-pt
extension:
- url: http://hl7.org/fhir/us/core/StructureDefinition/us-core-race
  extension:
  - url: ombCategory
    valueCoding: {system: 'urn:oid:2.16.840.1.113883.6.238', code: 2028-9, display: Asian}
  - url: detailed
    valueCoding: {system: 'urn:oid:2.16.840.1.113883.6.238', code: 2029-7, display: Asian Indian}
  - url: text
    valueString: Asian Indian

Extensions can be extracted to a separate schema if you’re going to reuse them across different profiles. Here’s the us-core-race profile updated in a such manner:

us-core-race
{:zen/tags #{zen/schema zen.fhir/structure-schema}
 :zen.fhir/version "0.5.0"
 :zen.fhir/profileUri "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race"
 :type zen/map
 :require #{:text}
 :keys {:ombCategory {:type zen/vector
                      :maxItems 5
                      :every {:confirms #{hl7-fhir-r4-core.Coding/schema}
                              :zen/desc "American Indian or Alaska Native|Asian|Black or African American|Native Hawaiian or Other Pacific Islander|White"
                              :zen.fhir/value-set {:symbol omb-race-category-value-set
                                                   :strength :required}}}
        :detailed {:type zen/vector
                   :every {:confirms #{hl7-fhir-r4-core.Coding/schema}
                           :zen/desc "Extended race codes"
                           :zen.fhir/value-set {:symbol detailed-race-value-set
                                                :strength :required}}}
        :text {:confirms #{hl7-fhir-r4-core.string/schema}
               :zen/desc "Race Text"}}}

MyPatientProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
 :zen.fhir/version "0.5.0"
 :zen.fhir/profileUri "urn:fhir:extension:MyPatientProfile"
 :confirms #{hl7-fhir-r4-core.Patient/schema}
 :type zen/map
 :keys {:race {:confirms #{us-core-race}
               :zen/desc "US Core Race Extension"
               :fhir/extensionUri "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race"}}}

More complex example of an extension:

MyPatient
{:zen/tags #{zen/schema zen.fhir/profile-schema}
 :zen/desc "Patient resource schema with first-class extension definition examples"
 :zen.fhir/version "0.5.20"
 :confirms #{zen.fhir/Resource}
 :zen.fhir/type "Patient"
 :zen.fhir/profileUri "urn:profile:MyPatientProfile"
 :type zen/map
 :keys {:meta {:type zen/map
               :keys {:tenant-id
                      {:confirms #{hl7-fhir-r4-core.string/schema}
                       :zen/desc "Patient.meta.tenant-id first-class extension"
                       :fhir/extensionUri "http://tenant-id-extension-url"}}}

        :form {:type zen/vector
               :zen/desc "Patient.form.[*] array first-class extension"
               :every {:confirms #{hl7-fhir-r4-core.uri/schema}
                       :fhir/extensionUri "http://patient-form-url"}}

        :info {:type zen/map
               :zen/desc "Patient.info nested first-class extension"
               :fhir/extensionUri "http://patient-info"
               :keys {:registration {:confirms #{hl7-fhir-r4-core.dateTime/schema}
                                     :zen/desc "Patient.info.registration
                                                extension.url deduced from key"}

                      :referral {:confirms #{hl7-fhir-r4-core.uri/schema}
                                 :fhir/extensionUri "http://patient-info-referral"
                                 :zen/desc "Patient.info.referral
                                            extension.url is specified"}}}}}

of a primitive type

of a complex type

with a nested extension

constraint

add an extension to a profile

describe a new value set

constraint some existing value set or code system

describe arbitrary resource

About

zen.fhir zen-lang namespace that is used by all zen-fhir packages

Resources

Stars

Watchers

Forks

Packages

No packages published