ferrobridge-term
The FerroBRIDGE FHIR terminology client for $lookup, $translate,
$validate-code, $expand and $subsumes.
Part of FerroBRIDGE, a pure-Rust bridge between openEHR and two interoperability targets: HL7 FHIR through the FHIRconnect specification, and the OMOP Common Data Model through the OMOCL specification.
What it does
Five operations on one configured server:
CodeSystem/$lookupresolves a code's display;lookup_atasks for it as the code system stood on adate.ConceptMap/$translatemaps a code into another code system.ValueSet/$validate-codetests membership of a value set, at an optionaldate.ValueSet/$expandlists the codes of a value set, withvalueSetVersion,date,filter, paging (offsetandcount) andincludeDesignations. The answer keeps the nesting ofexpansion.contains, and each entry carries itssystem,version,code,display,abstract,inactiveand designations.CodeSystem/$subsumestests how two codes relate, answered asSubsumesOutcome:Equivalent,Subsumes,SubsumedByorNotSubsumed.
code_system_count asks GET [base]/CodeSystem?url=<system>&_summary=count,
so a caller can tell an unserved code system from an unknown code.
validate takes the input forms $validate-code declares: a code with its
system, systemVersion and display, a code with the context that implies
its system (Coded::InContext, R4 and R4B), a Coding, or a
CodeableConcept, with an optional valueSetVersion (R4 and R4B). validate_all answers several validations
by position, one call for one and one batch for several.
batch sends several of them as one batch Bundle and answers them by
position; an entry the server refused is that entry's own typed error and
leaves the others alone.
A negative answer an operation states in its own out parameters is an
outcome: LookupOutcome::NotFound, TranslateOutcome::NoMatch,
ValidateOutcome::Invalid, and ExpandOutcome::NotFound for a value set the
server does not know. An expansion the server could not make, too-costly
among them, is a refusal and never an empty expansion. Everything else is a typed error carrying the
upstream status, the body, and any tx-issue-type coding the
OperationOutcome holds. A missing display is NotFound, never an empty
string, so the caller decides between a refusal and a configured fallback.
translate returns every match with its equivalence and offers accepted(),
which yields the equivalent and equal matches alone. The $translate
definition asks the caller to check match.equivalence for each match, so a
wider, narrower or inexact answer never reaches a target system as a
translation.
The request and response contracts come from the generated
fhir-types operation modules; this crate models
no FHIR of its own.
Three wire versions
WireVersion selects STU3, R4 or R4B, and the release decides which generated
model writes the request and reads the answer. R4 and R4B declare the same in
and out parameters for the five operations. STU3 (3.0.2) differs:
$validate-codenames the code system versionversionwhere R4 names itsystemVersion, and the client sends it under that name. STU3 has novalueSetVersion.$validate-coderequires a system for a code, where R4 and R4B accept acontextin its place, so an STU3 client refusesCoded::InContextwithError::SystemRequiredbefore anything is sent.$expandhas novalueSetVersioneither.$translatehas nourl, so STU3 cannot name the concept map to use.$lookuptypesproperty.valueascode, so every property value of an STU3 answer is avalueCode.$translateanswers the samematchparts (equivalence,concept,product,source) with the sameConceptMapEquivalencecodes, soaccepted()reads both releases the same way.
A request that names a parameter STU3 has no equivalent for (a concept map URL
on translate, a value_set_version on validate or expand) is refused with
Error::UnsupportedParameter before anything is sent. In a batch the whole
batch is refused. The request is never sent with the parameter dropped,
because that would ask the server a different question.
A server that serves several releases side by side puts the release in its base path, so the base URL and the wire version are configured together.
An absent server
Client::new and Client::with_http are the only ways to get a client, so a
deployment that configures no terminology server holds Option<Client> and
the absence is visible at every call site. Keep it that way: a None is a refusal to translate, not a
reason to pass a source code through under a target system.
The Client::translate documentation carries the worked example.
Mutual TLS and private trust roots
Client::with_http takes an HTTP client you build, so a server that asks for
a client certificate, or whose certificate chains to a private root, is
reached with the reqwest::Identity and reqwest::Certificate you give that
client. The configured timeout still bounds each request, and the
credentials, the retry budget and the typed errors apply as for
Client::new.
The client credentials token of ferrohealth-smart
Behind the smart feature, smart::SmartToken turns the TokenSource of
ferrohealth-smart's client credentials grant into the client's
TokenProvider: each request carries the cached token, and a token the
server refuses with 401 is dropped, so the repeated request carries one
fetched anew (RFC 6750 §3.1). Pass it as Credentials::OAuth; no product
writes this adapter itself.
Status
The crate is on the crate line (docs/VERSIONS.md): every change moves it one
patch, a breaking change included, so pin it exactly.
Licence
Business Source License 1.1 (LICENSE): free for every non-production use and
for non-commercial production use; a commercial licence for other production
use; Apache License 2.0 four years after each version.