Repository navigation
4.3.3
·
30 commits
to main
since this release
Immutable
release. Only release title and notes can be modified.
Added
- The
openehr-itsclient sends DPoP-bound tokens (#3558).
Credentials::DpopwritesAuthorization: DPoP <token>(RFC 9449 §7.1),
and aDpopProvergiven withClient::with_dpop_proversigns theDPoP
proof over each request's final method, URL and access token. A
use_dpop_noncechallenge (RFC 9449 §8 and §9) is answered with one re-send
carrying the supplied nonce. openehr-sdtprints a SMART scope back in the grammar (#3557).
SmartScopeandResourceScopeimplementDisplay, writing the canonical
form (permissions inc/r/u/d/sorder), and
SmartScope::format_allprints a scope list as the space-delimited string
SmartScope::parse_allreads.
Changed
ClientError::DeadlineElapsedsays whether a send went out (#3560).
Its newsentfield isfalsewhen the deadline passed before anything was
sent, andtruewhen it passed before a retry or the re-send answering a
DPoPuse_dpop_noncechallenge, after the service had received the request.- The container images name FerroHEALTH as vendor and author (#3561).
org.opencontainers.image.vendorisFerroHEALTHand
org.opencontainers.image.authorsis
FerroHEALTH <https://github.com/FerroHEALTH>on every image. openehr-itstypes thePrefer-dependent response bodies (#3548). A
response the OAS declares asoneOfa resource andIdentifier(the
201and200answers of every create and update: EHR,EHR_STATUS,
COMPOSITION, DIRECTORY, CONTRIBUTION and the demographic parties) is now
rest::runtime::Representation<T>, eitherFull(T)or
Identifier(Identifier), in place ofserde_json::Value. A generator test
pins the bodies that remain untyped, with their reason.- The generated CONTRIBUTION envelope reads a deleted version and an
AUDIT_DETAILSaudit (#3547).UpdateVersion.datais optional, because a
logical deletion commits a version with no data (RM common master06
§Logical Deletion;ORIGINAL_VERSION.datais0..1), andUpdateAudit
reads_type: "AUDIT_DETAILS"as its base, as thecontribution_create
docs text tells servers to. ferroehr-restdecodes every ITS-REST request through the generated
contract (#3545). Each handler reads its parameters with the generated
*Params::from_request, the CDR's own parameter decoder is gone, and the
Simplified Formats CONTRIBUTION path reads the typedNewContribution
envelope.- Every ITS-REST request body has a public decoder (#3551). Each operation
with a body gets{operation}_request_body(content_type, bytes)in its API
group, which returns the type the server trait receives (for
contribution_create, aPayloadof the canonical or the Simplified Formats
envelope) and is the decoding the generated router itself runs. The body
decoding moved torest::decode, so it needs only therestfeature. ROUTE_REQUEST_MEDIAadmits every media type theContent-Typeparameter
declares (#3552). The released OAS lists onlyapplication/jsonunder
requestBody.contentwhere the operation'sContent-Typeparameter and the
docs text also admit XML and both Simplified Formats;request_medianow
carries the union, and a test holds it equal to the parameter.- The
openehr-*crates step to 0.0.81. openehr-itsnames the openEHR identifier class of every path
parameter (#3539).rest::routes::Paramgainsidentifier, an
IdentifierClasswhoseas_str()is the BASE class name: the
class the ITS-REST OAS states only in the parameter's description, as the
BMM type of the RM attribute the value is "taken from".ehr_id,
versioned_object_uidandcontribution_uidcarryHIER_OBJECT_ID,
version_uidcarriesOBJECT_VERSION_ID, anduid_based_idkeeps its
per-operation class:UID_BASED_IDon a read,HIER_OBJECT_IDon an
update,OBJECT_VERSION_IDon a delete (IdentifierClass::UidBased,
HierObject,ObjectVersion). The table is generated from a
decision map that a codegen test holds to the vendored OAS descriptions.- Every generated
*Paramsstruct decodes a matched request (#3540).
from_request(matched, query, headers)takes theRouteMatchfrom
routes::lookup, the query string and the headers, and runs the decoding
the generated router runs: the declared keys into their fields, the members
of a form-exploded object such as the AQLquery_parametersread as JSON
where they parse,+as a literal plus, and a400naming the parameter
for a missing, repeated or invalid one. The generated handlers call the
same decoding, so an intermediary and the router cannot read a request
differently. The decoding moved fromrest::serverto the new
rest::decode, which needs only therestfeature, not the axum server
half. - The ITS-REST route table names each operation's request-body media
types (#3543). Each API group carriesROUTE_REQUEST_MEDIA, aligned with
ROUTES, holding the OASrequestBody.contentkeys, andRouteMatch
carries them asrequest_media.definition_query_version_store, which
declares noContent-Typeparameter, now namestext/plain, so a
forwarding intermediary can set the header. - A Simplified Formats CONTRIBUTION has a typed envelope (#3541). The
generatedNewContributionis generic over its version content,
NewContribution<T = Versionable>, so the bare name stays the canonical
body andNewContribution<serde_json::Value>reads the envelope of a FLAT
or STRUCTURED CONTRIBUTION, whose envelope stays canonical while each
versions[i].datais simplified (ITS-RESTcontribution_create
§Simplified Formats). The generated server admits
application/openehr.wt.flat+jsonand
application/openehr.wt.structured+jsonon every operation whose
Content-Typeparameter declares them, and hands the implementation a
rest::runtime::Payload(Canonical,Flat,Structured); XML and
any other type stay415. - The
openehr-*crates step to 0.0.80. - The
openehr-rmattribute model covers the BASE foundation types and
names reference targets (#3537). The model (v1_1::model,
v1_2::model) now holds every class of the BASE + RM BMM, including the
primitives (Boolean,Character,Integer,Integer64,Octet,
Real,Double,String,Uri), theOrderedandNumericmarkers and
the containers. Each class carries its BMMpackage, whether the
generated crates carry a type for it (generated_type), and its full BMM
ancestry asancestors, soDV_ORDEREDcarriesOrdered. The new
is_primitive,is_foundation_typeandconforms_to_orderedlookups
answer the AQL ORDER BY question ("primitives andOrderedtypes", QUERY
master03 §ORDER BY). The AQL engine expands a declared type to its
descendants for RM classes only; a foundation type stays a value type. A
reference-typed attribute (OBJECT_REF,PARTY_REF,LOCATABLE_REF)
carriesref_target, the class the RM text says it points to
(EHR.ehr_status→EHR_STATUS,PARTY_RELATIONSHIP.target→
VERSIONED_PARTY), orNonewhere the text names no single class. A
consumer that read "not in the model" as "primitive" now asks
is_primitive. - The
openehr-*crates step to 0.0.79. openehr-itsexposes theAuthorizationvalue it sends (#3535).
Credentials::header_valueis public, so a consumer can validate a
credential at configuration load with exactly the value the client sends.
Basic credentials are checked against RFC 7617 §2 (no colon in the user-id,
no control character in either part) and bearer tokens against the
b64tokensyntax of RFC 6750 §2.1. A refusal is a typed
InvalidCredentials, which never carries the secret; a request whose
credentials break these rules fails withClientError::InvalidCredentials
before anything is sent.- The
openehr-*crates step to 0.0.78. openehr-queryclassifies AQL function calls (#3529). A call naming
one of the built-in functions in QUERY master03 §Functions parses to
FunctionCall::Builtinwith a typedBuiltinFunction(StringFunction,
NumericFunction,DateTimeFunction); any other name parses to
FunctionCall::Other. Both keep the name as written, soprinter::to_aql
returns it unchanged,CONTAINSincluded.FunctionCall::Namedis gone, and
theToken::Containslexer token now carries its spelling. The CDR's AQL
engine uses this classification instead of a function list of its own.openehr-itsnames every operation's declared parameters (#3530).
Each generated*Paramsstruct carries aPARAMStable of the path, query
and header parameters it holds (name as the OAS spells it, location,
required, explode, and the value kind its schema states), written in the
same pass as the struct. Each API group carriesROUTE_PARAMS, aligned with
ROUTES, androutes::lookupreturns the matched operation's parameters
withquery_param,query_keyandheader_paramlookups, so an
intermediary can pass on exactly what an operation declares.openehr-itsReqwestTransportconstructors take a
reqwest::ClientBuilder(#3531).ReqwestTransport::newand the new
with_builder_timeout(replacingwith_client_timeout) build the client
themselves, with redirects switched off.- The
openehr-*crates step to 0.0.77. - FerroTERM moves to 0.1.6 at
ghcr.io/ferrohealth/ferroterm(#3521).
FerroTERM moved to the FerroHEALTH organization, and its releases from 0.1.6
on publish there. The terminology compose overlay, the hosted sandbox, the
Helm chart (terminology.image, chart 10.1.6) and the FerroTERM integration
test pin0.1.6@sha256:7ec4c962…, and the book links
https://github.com/FerroHEALTH/FerroTERM.
Fixed
- AQL honours the per-EHR
EHR_ACCESSdecision (#3562). A query could
return rows from an EHR whose path routes refused the caller: the gate ran
only on routes carrying anehr_idin the path. Ad-hoc, stored and cohort
queries now withhold every EHR the caller may not read, whether the query
scopes it by theWHEREclause, anEHRpredicate, theehr_idparameter,
or not at all. - A
+in a query string is a literal plus (#3545). The CDR decoded
query strings as HTML forms do, where+means a space, so an unencoded
offset such asversion_at_time=2024-01-01T00:00:00+01:00lost its sign.
Query strings now decode per RFC 3986, as the OASstyle: form
parameters (RFC 6570) define; a space is sent as%20. Client::forwardreturns a redirect as received (#3531). The client
built byReqwestTransport::with_timeoutused reqwest's default redirect
policy and followed up to ten redirects, re-sending the request and its
credentials to whatever host aLocationnamed. EveryReqwestTransport
constructor now switches redirects off, so a3xxreaches the caller and
nothing is sent further. No ITS-REST 1.1.0 operation declares a3xx, so a
generated client call that receives one refuses it as an undocumented
status.- A FerroTERM pin the sandbox's index cannot serve is refused on the pull
request (#3520). FerroTERM 0.1.5 moved its artifact layout to 7, and the
v4.3.2 sandbox leg failed on the layout-6 index the box carried. A
ferroterm-index-layoutCI job reads the layout at the pinned FerroTERM tag
and refuses a pin that differs fromdeploy/hosted/ferroterm-index.env, the
record of the box's index. The box now runs FerroTERM 0.1.6 on a rebuilt
layout-7 index, anddeploy/hosted/README.mddocuments the rebuild. - The generated ITS-REST types keep the members an open schema allows
(#3526). OpenAPI 3.0.3 makesadditionalPropertiesdefault totrue, so
every ITS-REST object schema that does not set it tofalseadmits members
beyond its declared properties. Theopenehr-itsREST types for those
schemas now carry them in anadditional_propertiesmap, written back at
the object's own level, where they used to be dropped on decode.Erroris
one of them, and the server now builds its error body from the generated
type with theerrormember in that map. The body carries the same three
members, witherrornow written aftermessageandvalidationErrors.