FHIR Terminology Ecosystem IG, published by HL7 FHIR Product Director. This guide is not an authorized publication; it is the continuous build for version 1.9.6 built by the FHIR (HL7® FHIR® Standard) CI Build. This version is based on the current content of https://github.com/HL7/fhir-tx-ecosystem-ig/ and changes regularly. See the Directory of published versions
This document describes the requirements for all servers that are part of the HL7 terminology ecosystem. Note that systems do not need to conform to these requirements to be described as 'FHIR Terminology servers', but they do need to conform to these requirements to be part of the ecosystem.
Every requirement on this page is enforced by one or more of the test cases, and the test cases are the definitive statement of what the requirement means: where this page and a test case disagree, that is a defect in one of them, and should be reported. Each section names the test suites that cover it, so that an implementer can go from a rule to the tests that check it.
Most requirements apply to every server. Some apply only to a server that supports a particular code system or a particular feature, and those are stated conditionally - "if a server supports X, then it SHALL …". In the test cases these conditional requirements are gated by a mode: a test gated on a mode you do not pass to the runner does not run, and does not count as either a pass or a failure. So a server declares which of these sections apply to it by which modes it asks to be tested in. See Modes.
The test cases are written in R5 but run against R4 servers as well, with the runner converting requests and responses on the fly. Where a requirement takes a different form in R4, it says so, and R4 and the Test Cases has the detail.
A server speaks one version of FHIR, and the test runner discovers which:
$versions operation at the root, or return a
CapabilityStatement from /metadata that populates CapabilityStatement.fhirVersion. A server
that does neither cannot be tested, and cannot be registered(covered throughout; the http-code property on a test case says what is expected)
application/fhir+json). XML is not requiredParameters resource POSTed to the operation
endpoint. GET forms are used in places by clients but are not required by the ecosystem$validate-code that finds the code invalid is a successful operation, and returns
200 with result = falseOperationOutcome when it could not carry out the
operation at all: the value set could not be resolved, the request was malformed, the code system
is unknown to a $subsumes call, the expansion is too costly. The test cases state the expected
status as 4xx rather than a specific code, so any 4xx is acceptedEvery error, warning and comment the server produces is carried as an OperationOutcome.issue, either
in a returned OperationOutcome or in the issues parameter of an operation response. For all of them:
severity, code, details.coding, and details.textdetails.coding SHALL include a coding from http://hl7.org/fhir/tools/CodeSystem/tx-issue-type.
This is what lets a validator process the issue correctly, and it matters more than issue.code,
for which more than one value is often acceptedexpression naming it, so that
a validator can locate the issue in the resource being validateddiagnostics MAY be populated; the test cases ignore ithttp://hl7.org/fhir/StructureDefinition/operationoutcome-message-id
extension with the server's own identifier for the message. Only tx.fhir.org is required toA server in the ecosystem is exposed to requests it did not write, from clients it does not control, so
it has to be able to say no. (suites: big, regex-bad)
tx-issue-type is too-costly. What the limit is, and how it is calculated,
is up to the server; that there is a limit, and that exceeding it produces this answer, is nottx-issue-type is vs-invalid, rather than looping(suite: metadata)
$versions at the root, returning the FHIR versions it supports. It is the
first thing the test runner asks for, and it is the only way for a server that speaks more than one
version of FHIR to say so$versions, it SHALL declare it in CapabilityStatement.rest.operation{root}/metadataurl, version, name, title, status (active), date, and kind
(instance)software.name, software.version and software.releaseDate. The name is what
the test runner uses to identify the server in its outputfhirVersion, and rest[mode = server].security.serviceapplication/fhir+json in formatCapabilityStatement.instantiates value of
http://hl7.org/fhir/CapabilityStatement/terminology-serverrest.resource for CodeSystem, the operations lookup, subsumes and
validate-coderest.resource for ValueSet, the read and search-type interactions and
the operations expand and validate-codecache-control
operation at the system level in rest.operation. This declaration is the only way a client
discovers that the protocol is availableCodeSystemAsParameter, saying whether the server accepts CodeSystem resources in the tx-resource
parameter. The feature SHALL be present; its value may be true or false{root}/metadata?mode=terminologyversion, name, title, status and dateTerminologyCapabilities.codeSystem.uri, with the
versions in codeSystem.version.code. Code systems SHALL be listed here whether or not they are
available through /CodeSystem search - this list, not the /CodeSystem endpoint, is what the
ecosystem uses to decide whether to send a request to a serverexpansion.parameter, at least the parameters activeOnly,
check-system-version, count, displayLanguage, excludeNested, force-system-version,
includeDefinition, includeDesignations, offset, property, system-version and tx-resourcedefault-valueset-version. Note that caching is not declared here: cache-id is no longer an
expansion parameter, and support for caching is declared by the $cache-control operation in the
CapabilityStatement - see CachingA 'supported' CodeSystem is any code system that the server supports correctly for calls to $expand, $validate-code, and $lookup.
A 'pre-defined' CodeSystem is any value set that the server makes available through the /CodeSystem endpoint.
There are two kinds of servers that are made available to the ecosystem: general purpose terminology servers that support arbitrary CodeSystem resources, and servers that are code system specific - they only support one code system (or a select short list of CodeSystems).
Servers are encouraged to make all the code systems that they support available on the /CodeSystem endpoint (search/read) but
they do not have to, and code systems such as SNOMED CT and LOINC often are not. But they must be listed in the
TerminologyCapabilities statement so that the ecosystem knows which code systems the server supports.
TerminologyCapabilities.codeSystem.uri, and all the versions in TerminologyCapabilities.codeSystem.version.code. Code systems SHALL be listed here whether or not they are available through code system search/CodeSystem endpoint for search and read operationsurl and version search parametersNote that it's the bigger code systems such as SNOMED CT and LOINC that might not be available through /CodeSystem. Also note that the
terminology ecosystem does not make use of any search parameters
/CodeSystem/CodeSystem MAY have content = not-present; the tools will not consider this when choosing whether to try using the code system (uses TerminologyCapabilities.codeSystem.uri)Servers are encouraged to ensure that all code systems conform to the ShareableCodeSystem Profile found in the CRMI specification, but this is not a technical requirement for being part of the ecosystem.
It's up to the server how to manage what content they support and implement. Servers can choose to support update/create if they want, though it SHOULD only do so for authenticated clients.
Servers SHALL ensure that they only send correct results for the code systems for which it is registered as 'authoritative' (e.g., not allow not appropriately authorised users to change the results by posting resources)
All predefined code systems SHALL have a web representation that is appropriate for a human to look at (see below)
Terminology servers can choose to accept CodeSystems in the tx-resource parameter. General purpose servers SHALL do this (and are required to do this to pass the general tests).
CodeSystemAsParameter feature in the CapabilityStatement (see above)Terminology servers SHOULD support passing CodeSystem supplements, particularly language packs. Servers that don't support language packs should only choose not to support language packs when there is governance over the use of language translations, and only by negotiation with the terminology ecosystem managers (see also notes below)
Servers SHALL indicate their support or not for passing CodeSystem supplements in the tx-resource parameter using [tbd]
Servers intended to be used as the primary server for the validator and IG publisher SHALL accept CodeSystems in the tx-resource parameter. (e.g. this rule does not apply servers registered with the ecosystem)
Servers are required to support code system supplements. Specifically, this means:
Where a server makes language specific authoritative claims in the ecosystem registration (the languages property):
complete or fragment), not just the supplements - it will receive the whole operation, not just the language-specific part$expand, $validate-code, and $lookup SHALL return correct displays and designations in that language (typically via loaded supplements). This is verified by the language routing test cases, not by the coordination serverServers are required to support the following properties in the CodeSystem resource:
CodeSystem.caseSensitive SHALL be supported: validation SHALL correctly check case. In the case of non-case sensitive code systems, expansions SHOULD just contain the code as defined (in the code system or the value set enumeration), and not all the case variants that could be generated. (suite: case)
CodeSystem.valueSet. Tbd what this means
CodeSystem.hierarchyMeaning. If the code system defines a hierarchy, both $expand and $validate SHALL correctly handle the hierarchy when interpreting filters and validating codes
CodeSystem.compositional. A server SHALL not accept compositional grammars for codes unless the CodeSystem is marked as Compositional. Servers that do not know the grammar for a CodeSystem that is marked as compositional SHALL note this in validation errors for the CodeSystem
CodeSystem.versionNeeded. If a CodeSystem says that version is needed, the $validate-code operation SHALL check that version is populated, and return an error if it's not
CodeSystem.content. Servers SHALL not process $expand or $validate-code requests on CodeSystems that have content = not-present or example. Servers SHALL reflect content = fragment in an error message if the code is not valid against a fragment. Specifically, where the code system is a fragment, a code that is not found SHALL NOT be reported as invalid: the answer is that the server cannot tell, and the message SHALL say that the code system is a fragment (suite: fragment)
CodeSystem.supplements. Servers SHALL not mistake supplements and code systems for each other.
A number of concept properties defined at
concept-properties change how a concept behaves, and a server
SHALL honour them. The rule for recognising them is the same in each case, and is tested in detail for
notSelectable (suite: notSelectable):
uri from concept-properties, the
server SHALL recognise it whatever local code the code system gave ituri, it is a different property, and
the server SHALL NOT treat it as the standard oneThe properties that matter to the ecosystem are status, inactive, deprecated, notSelectable,
definition, parent and child. See
Inactive, Deprecated and Not-Selectable Codes for what
the first four mean.
A 'supported' value set is any value set that can be used in $expand or $validate-code operations, including value sets imported into other value sets, and including implicit value sets. A 'pre-defined' value set is any value set that the server makes available through the /ValueSet endpoint.
All servers are required to fully support value sets as defined in this document (per below).
/ValueSet endpoints for search and read operationsurl and version search parameters_summary search parameterServers are encouraged to ensure that all value sets conform to the ShareableValueSet Profile found in the CRMI specification, but this is not a technical requirement for being part of the ecosystem.
It's up to the server how to manage what content they support and implement. Servers can choose to support update/create if they want, though it SHOULD only do so for authenticated clients.
$expand and $validate-code operations.TerminologyCapabilities.expansion.parameterValueSet.composeValueSet.compose.include and ValueSet.compose.exclude| Servers SHALL support extensionally defined value sets (by enumerating codes in ValueSet.compose.in | exclude.concept) |
The ecosystem makes no rules - at this time - about the handling of value sets that have an expansion with no definition.
(suites: translate, translate2)
A server that supports $translate holds ConceptMaps in the same way it holds CodeSystems and
ValueSets, and the same rules apply:
tx-resource parameter, and use them for
$translate requests in the same call/ConceptMap endpoint for read
and search, and SHALL support the url and version search parameters if it doessourceScope / targetScope and ConceptMaps that
do not. A ConceptMap with no scope is still usable; it is only the server's ability to find it
without being told which map to use that is affectedThe content on that page MAY be static or active; it is at the discretion of the server to decide what's on the page, but it SHOULD be more than just the json/xml for the resource (and it isn't limited to information in the resource, e.g. the server MAY choose to make additional process/provenance/context information available)
The server MAY choose to make this content available at the end-point for the relevant resource. e.g. a request for {root}/CodeSystem/123 with an Accept header of 'application/fhir+json' returns the resource, and the same URL with an Accept header of 'text/html' returns a web page suitable for human consumption. Servers are not required to do this; they MAY choose to make the content available elsewhere.
If the server chooses to make them available elsewhere, it SHALL populate the extension http://hl7.org/fhir/StructureDefinition/web-source in any resources it makes available with a valueUrl where the web view can be found. This SHALL be populated when the CodeSystem and ValueSet are read, and also in any $expand of the value set (just for the root value set in this case).
These apply to $expand and $validate-code alike.
If a server receives a terminology resource it does not process correctly, it SHALL return an error
system-version, check-system-version and force-system-version, and
SHALL observe the difference between them: system-version supplies a default that anything explicit
in the value set overrides, force-system-version overrides the value set, and check-system-version
produces an error if the value set asks for a different version. See
Code System VersionsA terminology client - the validator, the IG publisher - validates and expands thousands of times
against the same value sets and code systems, and many of them are ones the server does not already
have, because the IG being built is what defines them. Re-sending those definitions on every call is
the single largest source of wasted traffic in the ecosystem. The $cache-control protocol lets a
client send each resource to the server once and refer to it by url thereafter.
The protocol is defined in the FHIR Tools IG - see Terminology Caching for the protocol and the reasoning behind it, and the $cache-control OperationDefinition for the formal definition. That IG is normative for the protocol; what follows is what the ecosystem requires of a server that is part of it.
Note that this replaces the earlier cache-id parameter. The cache-id is now issued by the server
and carried in an HTTP header, and the parameter is withdrawn: servers SHOULD NOT accept it, and it is
no longer listed in TerminologyCapabilities.expansion.parameter.
$cache-control. This is not yet a SHALL, but it makes a very large
difference to build times and to network use, and it is expected to become onecache-control operation at the system level in
CapabilityStatement.rest.operation. A client uses the protocol only against a server that
advertises it, and inlines the resources on every request against any other - correct, just not
optimisedaffectsState = true; servers SHALL accept start and end by POSTNote that declaring the operation has an immediate consequence: the test runner uses it. A server
whose CapabilityStatement advertises system-level cache-control will have each test suite's setup
resources front-loaded with mode=start and then referred to by url under the returned cache-id,
instead of being re-sent with every request. The runner does not pass sealed, so it takes the
server's default, which SHOULD be true - a sealed cache holding exactly the suite's setup. A server
that advertises the operation but does not implement it correctly will fail the whole suite rather than
one test, so declare it when it works, not before.
cache-id output parameter of
a mode=start call. A client does not invent one. This is what makes it possible for the server to
say authoritatively whether a cache-id it is handed is one it actually hasX-Cache-Id HTTP header, not as an
operation parameter, so that it is transport metadata: readable by proxies and load balancers, and
by the server before the body is parsedmode=start response SHALL report sealed, so that a client never has to assume a defaultstart call.
Resources sent inline on later requests are used for that request and SHALL NOT be added to the cachesealed = false) SHALL accumulate each resource the server sees under that
cache-id - whether sent as tx-resource or as the primary valueSet / codeSystem - the first time
it is seen, and resolve it by reference thereafterThe three cases - never issued, released, timed out - are indistinguishable to the client unless the server distinguishes them, so:
$validate-code, $expand or any other operation carries an X-Cache-Id the server does
not have, the server SHALL fail the request with HTTP 404 and an OperationOutcome whose issue
carries cache-id-unknown from http://hl7.org/fhir/tools/CodeSystem/tx-issue-type. It is the
coded issue, not the status, that a client keys on: cache-id-unknown means the client's cache
is gone and it should start a new one, where an unknown value set is an authoring error. A server
that reports a lost cache as a content error sends implementers hunting for a mistake that is not
theremode=end for a cache the server does not have SHALL NOT be an error. It returns HTTP 200, because
the client's intent - that the cache be gone - is already satisfiedA client can depend on a server-side cache it has not used for a long time, because its own local cache
is answering everything; the failure then shows up as the first rare code that does reach the server,
long into a build. mode=check exists for this.
mode=check SHALL return HTTP 200 whatever the answer - it is not an error to ask about a cache
that is gone. A client polling its cache has to be able to tell "the server is up and says my cache
is gone" from "I could not reach the server": the first means start a new cache, the second means
retry, and collapsing them into a failed request loses exactly thatvalid = true and sealed, and SHOULD carry
resource-count and idle (the idle time as it was before the check reset it)timeout, the server's idle timeout in seconds, where the server is
willing to state one. A client has no other way to discover it, and without it can only guess at how
often to checkvalid = false and an outcome
parameter holding an OperationOutcome with the same cache-id-unknown issue that a request using
the cache-id would have failed withurl + version - front-loaded at
start, and again on later requests - and the server SHALL tolerate it. A client is not expected to
track what it has already registeredurl + version is required to be identical, so a server MAY treat
any copy as authoritative - first, last, or any other. A server MAY verify that repeated copies
match and reject a request that redefines a url + version with different content, but a client
SHALL NOT rely on that check being madeexpansion
has that expansion cached as suppliedtx-resource or as an entry's primary valueSet / codeSystem -
SHALL be populated into the cache before any entry is evaluated. An entry may therefore refer by
url to a resource supplied by a different entry, whatever order the two appear in, and population
SHALL NOT depend on whether individual entries succeeded. The batch's effect on the cache is
all-or-nothing at the level of the batch, not the entry. None of this applies to a sealed cache,
which does not growCaching does not change which version a versionless reference resolves to, but it makes a disagreement about it both more durable and harder to see.
(suites: simple-cases, parameters, properties, exclude, search, notSelectable, inactive,
deprecated, fragment, big, overload, other, errors, default-valueset-version, version,
regex-bad, language)
url, by valueSet (a resource in the request), or by
a resource passed in tx-resource and named by urlValueSet.expansion.parameterused-codesystem expansion
parameter with the value {url}|{version}. Where a supplement was used, it SHALL also report
used-supplement; where another value set was imported, used-valueset. This is how a client knows
what the expansion actually depended onexpansion.identifier and expansion.timestampexpansion.total where it knows the totalexcludeNested, which forces a flat expansioninclude is skipped, not an error: the expansion contains
the codes that do exist, and no issue is raisedcount and offset. offset is used by the ecosystem's clients only with
the value 0, but both are testedactiveOnly, includeDesignations, includeDefinition, property and
designationdisplayLanguage, and the Accept-Language header, and
ValueSet.language, as specified in Languagesfilter (text search). The ecosystem makes no requirements about the
quality of the text search - only that the parameter is supported and that obvious matches are
found and obvious non-matches are not (suite: search)abstract in the sense described under
notSelectabledefault-valueset-version, which supplies a version for imported value
sets that are referenced without one. Where it is supplied and names a value set version that does
not exist, the server SHALL return an error rather than silently falling back
(suite: default-valueset-version)The server SHALL support all the filters defined in the base specification, for all the code systems it
supports, and SHALL observe the distinctions between them (suites: simple-cases, tho):
is-a includes the code named in the filter as well as everything below itdescendent-of excludes the code named in the filter - that is the only difference from is-ais-not-a excludes the code named in the filter and everything below it; everything else in the
code system, including the code above it and other branches, is includedchild-of includes the direct children only, and not the code named in the filter=, in, not-in, exists and regex on concept propertiesparent /
subsumedBy property. A code system may state more than one parent for a concept, which nesting
cannot express, and the filters SHALL honour all of them(suite: properties)
property parameter, or with
ValueSet.compose.property in the value set definition - the server SHALL return themValueSet.compose.property SHALL be supported in both its forms: an enumerated list of property
codes, and * meaning every property the code system definesValueSet.expansion.property, giving each
one's code and, where it has one, its canonical uriValueSet.expansion.contains.propertyValueSet.compose.property, ValueSet.expansion.property and
ValueSet.expansion.contains.property do not exist as elements. An R4 server SHALL read and write
them as the cross-version extensions
http://hl7.org/fhir/5.0/StructureDefinition/extension-ValueSet.compose.property,
http://hl7.org/fhir/5.0/StructureDefinition/extension-ValueSet.expansion.property and
http://hl7.org/fhir/5.0/StructureDefinition/extension-ValueSet.expansion.contains.property. This
is a requirement, not an option: an R4 server that ignores the request or drops the response is
not conformant. R4 and the Test Cases has worked examples of
bothcompose.exclude in all its forms: excluding enumerated codes, excluding by
filter, and excluding by importing another value set (suite: exclude)overload)system-version parameter is also supplied, it is only a default, and SHALL NOT override a version
the value set states explicitly(suites: validation, version, language2, permutations, overload, notSelectable, inactive,
deprecated, errors, fragment, other, tho)
code + system (+ version) (+ display), Coding, and
CodeableConcept. A CodeableConcept SHALL be validated as a whole: the result is true if any of its
codings validates, and the issues report what was wrong with the others$expand)inferSystem is true and the value set contains the same code in two code systems, the server SHALL
report the ambiguity as an error rather than choosing onelenient-display-validation, and SHALL distinguish the two answers: with it
set, a display that is wrong but recognisable produces a warning; without it, an errorabstract, which says whether the caller will accept a code that is not
selectableReturn parameters:
issues parameter with paths to the actual locations so a validator can locate the issue correctlyhttp://hl7.org/fhir/tools/CodeSystem/tx-issue-type system, and helps validators process the errors correctly. The diagnostics property may be populated;
this is ignored by the test casessystem and code parmaeters)display parameter) but this is not always possible (e.g. some codes do not have displays) or required for some causes of validation failurex-caused-by-unknown-system parameter for each code system it did not support. This helps validators inform users of missing resourcesnormalized-code parameter where appropriate (e.g. case insensitive code systems, code systems with complex grammars)processing-note when it has not fully validated the code e.g. an SCT expression against the MRCMThe difference between "this code is not valid" and "this code is not in this value set" is a real one,
and the tx-issue-type coding SHALL say which: invalid-code for the first, not-in-vs for the
second. A message SHALL name both the code and the value set or code system it was checked against.
The same rules apply, less the value set. CodeSystem/$validate-code is tested separately throughout
(the test cases call it cs-validate-code), because a server can get one right and the other wrong.
result = false with
an invalid-code issue, not an error(suite: batch)
$validate-code request carrying repeated validation
parameters, each one a Parameters resource describing one code to validate. This is what makes
validating a large resource affordableurl, tx-resource, lenient-display-validation
and so on - apply to every validation in the batchvalidation overrides the top-level one for that validation
onlyvalidation parameter for each one in the request, in the same
order, each holding either the Parameters that $validate-code would have returned, or an
OperationOutcome where that one validation could not be performed(suites: simple-cases, parameters, omop, UCUM, icd-11, snomed)
$lookup is not used heavily by the ecosystem's tools, so there are few tests, but what it returns has
to be right.
display, and the name of the code system, and its versionproperty parameter, including property=*, which asks for every
property the code system defines for the conceptproperty=* is asked for, the server SHALL return definition, the hierarchy (parent and
child properties), and the standard concept properties it knows - inactive, notSelectable,
status - as well as the code system's own propertiesuse and, where known, their languageabstractcode SHALL be echoed as it was asked for, not normalised. Where the server normalises the code, the
normal form belongs in the code property, which is what that property is forOperationOutcome(suites: simple-cases, tho, snomed, UCUM, mimetypes, langcodes, tx.fhir.org)
$subsumes on CodeSystem, in both forms: system + codeA + codeB,
and codingA + codingBoutcome parameter with one of equivalent, subsumes,
subsumed-by or not-subsumedequivalent SHALL be returned when the two codes are the same code, and when they are two spellings
of the same thing in a code system whose grammar makes that possiblesubsumedBy or parent property rather than by nesting, the server SHALL
read it, including where a concept has more than one parentnot-subsumed. So are
two parents of the same codeOperationOutcome. The message SHALL
name the code and the code system(suites: translate, translate2, omop, tx.fhir.org)
A server that holds ConceptMaps SHALL support ConceptMap/$translate. This section is new, and the
tests are correspondingly more detailed than the text; where they disagree, the tests are right.
sourceCode + sourceSystem, as sourceCoding, or as
sourceCodeableConceptcode + system, coding and codeableConcept, and the
target system is targetsystem (lower case s) rather than targetSystem. An R4 server SHALL accept
the R4 namestargetSystem, and SHOULD accept targetScope (target in R4), to
constrain what the answer may beurl, the server SHALL use that map and no other. Naming a map
does not exempt it from the scope rule below: a client cannot put an out-of-scope code into a map's
scope by nominating the map, so the answer is still that the map does not applysourceScope and
targetScope fit the request, and use them. A server MAY decline to do this - not every server is
willing to choose a map on the client's behalf - in which case it SHALL say so rather than returning
an empty resultsourceScope.
sourceScope limits the scope of the map, so a code outside it is not the map's business at all:
the map is not a candidate - whether the server chose it or the client named it - and in particular
its unmapped SHALL NOT fire for that code. Were it otherwise, a map with unmapped mode = fixed
would answer for every code in the code system its group names, whatever its author declared, and
sourceScope would have no effect on any map that has an unmapped - which is most of themunmapped / other-map target is consumed by that
delegation, and SHALL NOT also be consulted as a candidate map in its own right. The delegating map
swallows it. Were it consulted independently as well, every other-map delegation would yield a
duplicate match whenever the two maps share a scope - which is the normal case, since a map normally
delegates to one covering the same ground - and other-map would be unusableoriginMap, as {url}|{version}, the map the server entered - not
the map that ultimately supplied the value. Where a delegation was followed, the match belongs to
the delegating map. In R4 this parameter is named sourceused-conceptmap parameter. That, and not a second match, is how the server discloses
where a delegated value came fromresult SHALL be true where any mapping was found, and false where none was. There are three
different negative answers, and a server SHALL distinguish them:
result |
match |
|
|---|---|---|
| a map applies and says explicitly that there is no mapping | true |
one, with noMap = true and no concept |
a map applies, has no element for the code, and its unmapped fires |
true |
the unmapped concept |
| no map applies at all | false |
none |
The third is a successful operation with a negative answer, so it returns HTTP 200 with a message
saying why - not a 4xx
match parameter, whose parts are concept (the target Coding),
relationship, originMap, and sourceConcept (the Coding that was translated). In R4 the
relationship is reported in equivalence, using the R4 codes (equivalent, narrower, wider,
relatedto …), and the R5 relationship element SHALL NOT be present; in R5 it is the other way
roundsourceComment SHALL be returned where the ConceptMap element carries a commentnoMap in R5, a target with no
code in R4 - the server SHALL return a match with noMap = true and no concept, and
result = true. "There is definitively no mapping" is an answer, not a failureunmapped answers the question "this map covers the code, but has no element for it". A code that
is outside the map's sourceScope is not covered at all, and is disposed of by the scope rule above
rather than by unmappedConceptMap.group.unmapped in its modes:
fixed: any code not otherwise mapped translates to the stated codeother-map: the translation is delegated to the named ConceptMap, and used-conceptmap reports ittargetCode,
targetCoding or targetCodeableConcept. The reverse parameter does not exist in R5, and a server
that receives it SHALL return a 4xx with a not-supported issue rather than guessing what was meantreverse parameter does exist, and an R4 server SHALL support it, as well as the
targetcode form$closure is tested by the closure suite, and its requirements are not yet written up here.
The $compare operation - determining whether two value sets are equivalent, or one a subset of the
other, or they overlap, or they are disjoint - is being trialled on tx.fhir.org. It is not a
requirement for registration in the ecosystem, and its tests are gated behind the tx.fhir.org mode.
This section will be written when the operation is settled.
(suites: version, overload, default-valueset-version, valueset-version)
Version handling is the largest single body of tests in the suite, because it is where servers most often differ. The rules:
used-codesystem for $expand, and the version parameter for $validate-codesystem-version supplies a default. Anything the value set states explicitly overrides itforce-system-version overrides the value setcheck-system-version SHALL produce an error where the value set asks for a different versionCode systems vary in whether codes and/or designations can be labelled as inactive, and if they do, how it is done. SNOMED CT defines 'inactive' explicitly. For other Code Systems, codes or designations are labelled as 'should not use' in any fashion, they are regard as inactive.
For the CodeSystem resource:
If a concept is defined as inactive:
If a designation is defined as inactive:
A concept may also be deprecated or withdrawn by the value set rather than by the code system, using
the valueset-deprecated or standards-status extension on ValueSet.compose.include.concept. A
server SHALL honour that too, and report it in the same way (suite: deprecated).
Not-selectable is a different thing again: the code exists and is valid, but it is not meant to be used
in an instance (suite: notSelectable).
http://hl7.org/fhir/concept-properties#notSelectable with the value true - see
Concept Properties for how that property is recognisednotSelectable SHALL be usable as a filter, with both the = and the in operatorsabstract$validate-code SHALL accept a not-selectable code only where the request says it will take one:
with abstract = true the code validates, and without it the server SHALL report that the code is
not selectableThe following extensions SHALL be supported:
http://hl7.org/fhir/StructureDefinition/codesystem-alternate - if code system has alternate codes (TODO: this is subject to further discussion)http://hl7.org/fhir/StructureDefinition/codesystem-conceptOrder - if code system has order, then this SHOULD be echoed (nothing else needed)http://hl7.org/fhir/StructureDefinition/codesystem-label - if code system supports 'labels', then this SHOULD be echoed (nothing else needed)http://hl7.org/fhir/StructureDefinition/coding-sctdescid - if sct is in scope (exact use cases need discussion)http://hl7.org/fhir/StructureDefinition/itemWeight - echo in value set if defined in code system or value sethttp://hl7.org/fhir/StructureDefinition/rendering-style - echo in value set if defined in code system or value sethttp://hl7.org/fhir/StructureDefinition/rendering-xhtml - echo in value set if defined in code system or value sethttp://hl7.org/fhir/StructureDefinition/valueset-concept-definition - populate if requested in expansion requesthttp://hl7.org/fhir/StructureDefinition/valueset-deprecated - populate in the response if code system concept is deprecatedhttp://hl7.org/fhir/StructureDefinition/valueset-supplement - check for this, blow up if supplement is properly supportedhttp://hl7.org/fhir/StructureDefinition/valueset-label - echo in value set if defined in code system or value sethttp://hl7.org/fhir/StructureDefinition/valueset-conceptOrder - echo in value set if defined in code system or value sethttp://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status - may be found on either a concept or a concept designation. The status codes 'withdrawn' and 'deprecated' mean that the concept / designation is inactive. In addition, it might be found on a ValueSet.compose.include.concept to indicate that the concept's inclusion in the value set is deprecated/withdrawn etcNote that some of these extensions may be supported by rejecting instances that contain them, depending on the specific use cases that the server supports. E.g., if the server does not support externally derived code systems then the code system extensions are not relevant.
In R4, several R5 elements are carried as cross-version extensions. Those are not optional either - see
R4 and the Test Cases, and ValueSet.compose.property above.
The requirements above apply to every server. The requirements below apply only to a server that supports the code system in question, and each corresponds to a mode in the test runner. A server declares which of these apply to it by the modes it asks to be tested in; a server that claims a mode SHALL pass that mode's tests.
(mode: snomed; suites: snomed, sct-ecl)
The SNOMED tests run against a fixed test subontology, not against a real edition: it is published in
the tx-source directory of this IG's repository, under the edition
http://snomed.info/xsct/31000003106. A server that claims the snomed mode SHALL load it, because
the tests assert specific concepts, displays and counts that only that subontology has.
?fhir_vs (all of SNOMED), ?fhir_vs=isa/{sctid}, ?fhir_vs=refset/{sctid} and
?fhir_vs=ecl/{expression}. Where the implicit value set names a refset or a concept that does not
exist, the server SHALL return an error naming the value set that was asked forconcept filter with is-a, descendent-of, is-not-a and
child-of, and the SNOMED-specific expressions filterexpressions = false, a well-formed expression SHALL be rejected as not
in the value set, even where its focus concept is in the value setexpressions = true, a well-formed expression whose focus concept is in
the value set SHALL be accepted$validate-code on an expression SHALL check the expression against the machine-readable concept
model (MRCM) where the server has it, and report a violation as an error: an attribute outside its
domain, a value outside its range, an attribute used more times than its cardinality allows, an
ungrouped attribute written inside a relationship group or vice versa, laterality on a body structure
that is not in the lateralizable reference set, and the concrete-value rules (a concrete value where
a concept is required, or the reverse, and concrete values outside their stated range)processing-note issue
rather than silently passing it$subsumes SHALL work between expressions, and between an expression and a precoordinated concept,
in both directions. Where the structure of the two expressions does not settle the question - they
refine different attributes, or the same attribute with unrelated values - the server SHALL return an
error saying it cannot determine the relationship. Finding no relationship is not the same as
establishing that there is none, and the server SHALL NOT report not-subsumed in that case$subsumes SHALL reject an expression that is not valid under the MRCM rather than answering it(mode: tx.fhir.org at present; suite: tx.fhir.org)
LOINC requirements are currently tested only against tx.fhir.org, because they depend on which LOINC
release and accessory files the server has loaded. A server that wants to be tested for LOINC should
contact the FHIR product director. The behaviour tested covers the LOINC properties (COMPONENT,
METHOD_TYP, ORDER_OBS, SCALE_TYP, CLASS), STATUS, the parent/child hierarchy, answer lists,
parts, and the answers-for and list filters.
(mode: tx.fhir.org at present; suite: UCUM)
$lookup SHALL work for a UCUM expression, including one carrying an annotation$subsumes on UCUM SHALL report equivalent for two spellings of the same unit - a named derived
unit and the expression it is defined as, a unit with and without an annotation - and
not-subsumed otherwise. UCUM has no hierarchy, so a scaled unit is not subsumed by the unit it
scales, two units sharing a canonical unit but differing in magnitude are not related, and neither
are two dimensionless units that differ only in magnitude$subsumes request SHALL produce an error(mode: mimetypes; suite: mimetypes)
Media types have no hierarchy between types and subtypes, but parameters narrow a media type, and that is what makes subsumption meaningful.
base filter, selecting by type (text) or by type and subtype
(text/plain). Parameters on a code SHALL NOT stop it matching a base filterregistered filter, selecting media types that are or are not in the
IANA registrybase filter or registered=false:
those are unbounded, and the server SHALL answer too-costly rather than returning a partial
expansion presented as complete. registered=true can be expanded, narrowed by a base filter, and
SHALL be marked as an unclosed expansion$subsumes SHALL treat a media type carrying a parameter as subsumed by the same type without it,
and a parameter set as subsumed by a superset of it. Media types are case-insensitivetext/plain and text/plain; charset=us-ascii are equivalent, and text/plain and
text/plain; charset=utf-8 are not-subsumed - the parameter contradicts the default rather than
narrowing it(mode: tx.fhir.org at present; suite: langcodes)
QM..QZ) SHALL be acceptednormalized-code and note the difference$subsumes SHALL implement RFC 4647 extended filtering: a tag subsumes any tag that adds subtags to
it, including where the added subtag sits between two the shorter tag names (en-US subsumes
en-Latn-US). A grandfathered tag subsumes nothinglanguage, region and script filters. An absent component does not
match a fixed one: a tag with no region is not in a region=US value set, and a tag with no script
is not in a script=Latn value set, even where that is the script the language is written inlanguage filter is finite and SHALL be expandable (paged). A region filter alone is finite but
far too large, and a script filter alone leaves the language open: both SHALL answer too-costly.
Expanding all language codes SHALL return the common-languages base value set, marked as unclosed(mode: omop; suite: omop)
The OMOP tests are based on a stable subset maintained for the ecosystem. Some servers support only OMOP, and the tests are written so that they can.
code+system, Coding and CodeableConcept, with and
without a value set, and SHALL check both the display and the version$lookup SHALL work for standard OMOP concepts. A non-standard concept may be looked up or refused,
depending on the server(mode: icd-11; suite: icd-11)
The ICD-11 tests assert correct FHIR behaviour rather than the behaviour of any particular server, and
tests/icd-11/doco.txt in this IG's repository explains each one. Three code systems are in play, each
with its own canonical: the Foundation (http://id.who.int/icd/entity), the MMS linearization
(http://id.who.int/icd/release/11/mms) and ICF (http://id.who.int/icd/release/11/icf).
$lookup and $validate-code against each of the three, by code and by
entity URI, including grouper concepts and residual categories$lookup SHALL echo code as it was asked for. A request by entity URI gets the entity URI back;
the normalised short form belongs in the code property$lookup and $validate-code, including
clustered and repeated axis values, and SHALL reject an invalid axiscount, offset and
filter