ihe-iti 0.0.44

The IHE IT Infrastructure (ITI) identity profiles: PIXm (ITI-83), PDQm (ITI-78, ITI-119), mCSD (ITI-90, ITI-91), PMIR (ITI-93, ITI-94) and XCPD (ITI-55), one feature per profile, with their audit records behind `balp` and `atna`
Documentation
// SPDX-FileCopyrightText: Cadasto B.V.
// SPDX-License-Identifier: BUSL-1.1

//! The ITI-90 and ITI-91 audit records of the Query Client and the Update
//! Client (mCSD 4.0.0 §2:3.90.5.1, §2:3.91.5.1), feature `balp`.
//!
//! [`McsdClient::audited`](super::client::McsdClient::audited) gives the
//! client a [`AuditRecorder`](ihe_atna::balp::AuditRecorder); it records one
//! [`Exchange`] per search ([`query()`], on the BALP Query pattern) and per
//! history ([`updates()`]), with the first request as sent in the query
//! entity. A care services search names no patient, so no patient entity is
//! written. The client is told of no user, so each record is the client's
//! own system's ([`OnBehalfOf::System`]) and names no user agent.

use jiff::Timestamp;
use url::Url;

use super::error::McsdError;
use ihe_atna::balp::{
    Coded, DESTINATION_ROLE, Direction, Entity, EventKind, Exchange, HISTORY_TYPE, Outcome, Peer,
    REST, SEARCH, SOURCE_ROLE, request_text,
};
use ihe_atna::user::OnBehalfOf;

/// The `ITI-90` subtype.
pub const ITI_90: Coded = Coded::fixed(
    ihe_atna::balp::IHE_TRANSACTIONS,
    "ITI-90",
    "Find Matching Care Services",
);

/// The `ITI-91` subtype.
pub const ITI_91: Coded = Coded::fixed(
    ihe_atna::balp::IHE_TRANSACTIONS,
    "ITI-91",
    "Request Care Services Updates",
);

/// What the Find Matching Care Services for Query audit profile fixes.
#[must_use]
pub fn query() -> EventKind {
    EventKind {
        profile: "https://profiles.ihe.net/ITI/mCSD/StructureDefinition/IHE.mCSD.Audit.CareServices.Query",
        event_type: REST,
        subtypes: vec![ITI_90, SEARCH],
        action: "E",
        client: SOURCE_ROLE,
        server: DESTINATION_ROLE,
    }
}

/// What the Request Care Services Updates audit profile fixes.
#[must_use]
pub fn updates() -> EventKind {
    EventKind {
        profile: "https://profiles.ihe.net/ITI/mCSD/StructureDefinition/IHE.mCSD.Audit.CareServices.Updates",
        event_type: REST,
        subtypes: vec![HISTORY_TYPE, ITI_91],
        action: "E",
        client: SOURCE_ROLE,
        server: DESTINATION_ROLE,
    }
}

/// The audit record of one search or history of `kind` that asked the
/// directory at `base` with the first request `request`, and ended in
/// `result`.
///
/// A search that follows `next` links is one transaction, recorded once
/// with the request that began it (no specification governs paging: our own
/// design).
pub(super) fn exchange<T>(
    kind: EventKind,
    base: &Url,
    request: &Url,
    result: &Result<T, McsdError>,
) -> Exchange {
    let outcome = match result {
        Ok(_) => Outcome::Success,
        Err(error) if error.answered() => Outcome::MinorFailure,
        Err(_) => Outcome::SeriousFailure,
    };
    Exchange {
        kind,
        recorded: Timestamp::now(),
        outcome,
        outcome_description: None,
        direction: Direction::Sent {
            server: Peer::server(base),
        },
        on_behalf: OnBehalfOf::System,
        entities: vec![Entity::Query(request_text(request))],
        security: Vec::new(),
    }
}