Write a custom zen profile

This article is work-in-progress. Please contact us if you want to get details on how to create a custom zen profile.

Note: you can not use Attributes and zen profiles on the same resource at the same time

Validation modes supported with zen schemas

Zen schemas are used by Aidbox for validating resources e.g. in FHIR CRUD API. Such zen schemas must be tagged with either zen.fhir/base-schema or zen.fhir/profile-schema. Additionally they must have :zen.fhir/type and :zen.fhir/profileUri keys specified.

zen.fhir/base-schema

Schemas tagged with zen.fhir/base-schema are used to validate every resource of a specified type. When loaded into Aidbox they will be used in place of the default JSON schemas.

zen.fhir/base-schema
{:zen/tags #{zen/schema zen/tag}
  :zen/desc "This schema should be used to validate all resources of its type"
  :confirms #{structure-schema}
  :type     zen/map
  :require  #{:zen.fhir/type}}

zen.fhir/profile-schema

Schemas tagged with zen.fhir/profile-schema are used to validate resources that mention their :zen.fhir/profileUri in the meta.profile attribute.

Those schemas must be tagged with zen.fhir/profile-schema, describe data structure in the Aidbox format and conform to the following schema:

zen.fhir/profile-schema
{:zen/tags #{zen/tag zen/schema}
 :type     zen/map
 :confirms #{zen.fhir/structure-schema}
 :keys     {:zen.fhir/type       {:type zen/string}
            :zen.fhir/profileUri {:type zen/string}}}

zen.fhir/profile-schema, by virtue of being a zen.fhir/nested-schema, must also conform to the following schema:

zen.fhir/nested-schema
{:zen/tags #{zen/schema}
 :type zen/map
 :keys {:fhir/flags {:type zen/set}
        :fhir/extensionUri {:type zen/string}
        :fhir/polymorphic {:type zen/boolean}
        :zen.fhir/reference {:type zen/map
                             :keys {:refers {:type zen/set
                                             :every {:type zen/symbol}}}}
        :zen.fhir/value-set {:type zen/map
                             :keys {:symbol {:type zen/symbol}
                                    :strength {:type zen/keyword
                                               :enum [{:value :required}
                                                      {:value :extensible}
                                                      {:value :preferred}
                                                      {:value :example}]}}}
        :keys {:type zen/map
               :values {:confirms #{nested-schema}}}
        :every {:confirms #{nested-schema}}}}

Description of various schema keys

keydescription

zen.fhir/type

required

Used to find schema by matching against resourceType of incoming data

zen.fhir/profileUri

required

Used to find schema by matching against meta.profile of incoming data

fhir/flags

optional

A set of keywords derived from FHIR ElementDefinition boolean attributes: isModifier (:?!), isSummary (:SU), mustSupport (:MS)

fhir/extensionUri

optional

Used in Aidbox->FHIR format transformations to create extension element with url described in this key

fhir/polymorphic

optional

Used in Aidbox->FHIR format transformations to detect whether the element is a choice type

zen.fhir/reference

optional

Used to specify resource types that can be referenced

zen.fhir/reference.refers

optional

set of symbols referring to other zen.fhir/profile-schemas or zen.fhir/base-schemas, used on reference validation

zen.fhir/value-set.symbol

optional

symbol referring to zen.fhir/value-set schema, used on validation to check data against a valueSet

zen.fhir/value-set.strength

optional

keyword specifying strength of binding

Examples

:zen.fhir/reference

Patient.managingOrganization is a reference to an Organiazation resource:

hl7-fhir-r4-core.Patient
 schema
 {#_...
  :keys
  {#_...
   :managingOrganization
   {:confirms #{hl7-fhir-r4-core.Reference/schema zen.fhir/Reference}
    :zen.fhir/reference {:refers #{hl7-fhir-r4-core.Organization/schema}}}}}

zen.fhir/value-set

To enable value-set validation in zen concept resources with valueset attribute populated must be loaded into Aidbox.

:zen.fhir/value-set key must be a symbol tagged with zen.fhir/value-set

hl7-fhir-r4-core.Patient
 schema
 {#_...
  :keys
  {#_...
   :gender
   {#_...
    :zen.fhir/value-set
    {:symbol hl7-fhir-r4-core.value-set.administrative-gender/value-set
     :strength :required}}}}

zen.fhir/value-set symbol definition must contain :uri and :fhir/code-systems attributes

:uri is a ValueSet.url, which must be mentioned in a Concept.valueset.*

:fhir/code-systems :fhir/url is a CodeSystem.url, which must be a value of Concept.system :zen.fhir/content can have a value :bundled , this means that a CodeSystem content is accessible as Aidbox Concept resources. Other option is :not-present, Aidbox will skip validation of such concept assuming that the CodeSystem content is not loaded into Aidbox

hl7-fhir-r4-core.value-set.administrative-gender
  value-set
  {:zen/tags #{zen.fhir/value-set},
   #_...
   :uri "http://hl7.org/fhir/ValueSet/administrative-gender",
   :fhir/code-systems #{{:fhir/url "http://hl7.org/fhir/administrative-gender",
                         :zen.fhir/content :bundled}}}

:slicing

:slicing allows to validate a part of some array with a zen-schema. Such part is called a slice. :slicing allows to reproduce FHIR profiling Slicing operation

Before applying :schema to a slice zen needs to determine elements of the array this slice consists of, this logic is described using :filter key. :filter must specify an :engine. Currently there're two engines available :zen and :match

:zen engine matches elements against specified zen schema

:match engine is a pattern matching DSL:

  • primitive (e.g. string or a number): constant, i.e. literal match of a value

  • {} : objects validated with this match must contain and conform to every key from this pattern

  • #{} : at least one element of an array must conform each element of this pattern

From example below: :match {:code "29463-7", :system "http://loinc.org"} means that elements with specified code and systems will be considered as a part of the slice.

:schema is a zen schema which will be applied to a slice.

Following example describes that in the code.coding array there is a slice called BodyWeightCode consisting of exactly one element with code="29463-7" and system="http://loinc.org":

hl7-fhir-r4-core.bodyweight
 schema
 {#_...
  :keys
  {#_...
   :code
   {:confirms #{hl7-fhir-r4-core.CodeableConcept/schema}
    :type zen/map
    :keys
    {:coding
     {:type zen/vector
      :every {:confirms #{hl7-fhir-r4-core.Coding/schema}}
      :slicing
      {:slices
       {"BodyWeightCode"
        {:filter
         {:engine :match
          :match {:code "29463-7", :system "http://loinc.org"}}
         :schema
         {:type zen/vector
          :minItems 1
          :maxItems 1
          :every
          {:type zen/map
           :require #{:system :code}}}}}}}}}}}

Slicing on Organization.identifier constraining max one element in both NPI and CLIA slices:

hl7-fhir-us-core.us-core-organization
 schema
 {#_...
  :keys
  {#_...
   :identifier
   {:type  zen/vector
    :every {:confirms #{hl7-fhir-r4-core.Identifier/schema}}
    :slicing
    {:slices
     {"NPI"
      {:schema {:type zen/vector, :maxItems 1}
       :filter
       {:engine :match
        :match  {:system "http://hl7.org/fhir/sid/us-npi"}}}
      "CLIA"
      {:schema {:type zen/vector, :maxItems 1}
       :filter {:engine :match
                :match  {:system "urn:oid:2.16.840.1.113883.4.7"}}}}}}}}

Last updated