aion-server 0.29.0

Aion workflow server library: HTTP, gRPC, WebSocket, and worker endpoints. Run it with the `aion` binary from the aion-cli crate.
Documentation
//! The documentation model of one deployed revision.
//!
//! # The same model the CLI prints
//!
//! `aion awl doc --json` and this endpoint call the SAME derivation
//! (`aion_awl::doc::derive_for_revision`) over the same two inputs — the
//! archived document text and the archived schema files. There is no second
//! assembly here to drift from it; `tests/awl_deployed_doc_agreement.rs`
//! holds the two byte-equal for every corpus document the deploy fixture can
//! package.
//!
//! # No workspace document is ever substituted
//!
//! When the archived source is `absent`, `not_persisted` or `unreadable`, the
//! response still carries the CONTRACT half — which comes from the archive's
//! committed contract, not from the document — and NAMES the state. It never
//! reaches for a same-named document in the operator's workspace: that would
//! be a page about an artifact that does not exist (VISUAL-AUTHORING-SURFACE
//! D7, "a stale artifact must never silently narrate as fresh").
//!
//! # No staging directory
//!
//! Unlike [`super::projection`], this path needs no filesystem at all: the
//! doc deriver takes the archived schema bytes in memory
//! (`aion_awl::SchemaSources::InMemory`). The staging invariant is therefore
//! satisfied vacuously here, and the pin that says so lives beside its
//! sibling in `tests.rs`.

use aion_awl::doc::{ContractDoc, DocumentDoc, SourceState};
use aion_package::{ExtractionLimits, Package};
use aion_store::PackageRecord;

use super::types::DeployedError;

/// Reads the documentation model for `(workflow_type, content_hash)`.
///
/// Blocking: extracts an archive. Callers on an async runtime must run this
/// on the blocking pool.
///
/// # Errors
///
/// Returns [`DeployedError::NotFound`] when no persisted archive matches the
/// pair, and [`DeployedError::DocModel`] when the archived document will not
/// derive a model.
pub(super) fn read_doc(
    archives: &[PackageRecord],
    workflow_type: &str,
    content_hash: &str,
    loaded_types: &[String],
) -> Result<DocumentDoc, DeployedError> {
    let not_found = || DeployedError::NotFound {
        workflow_type: workflow_type.to_owned(),
        content_hash: content_hash.to_owned(),
    };
    let Some(record) = archives
        .iter()
        .find(|record| record.content_hash == content_hash)
    else {
        // A version the engine holds without a persisted archive is a real,
        // distinguishable state (an operator-file startup package), not a
        // 404: the caller asked about a revision that exists and whose
        // source the server never held.
        if loaded_types.iter().any(|held| held == workflow_type) {
            return Ok(aion_awl::doc::without_source(
                workflow_type.to_owned(),
                aion_awl::doc::FAMILY_UNKNOWN.to_owned(),
                Some(content_hash.to_owned()),
                SourceState::NotPersisted,
                ContractDoc::committed(
                    None,
                    None,
                    None,
                    Vec::new(),
                    Vec::new(),
                    Vec::new(),
                    Vec::new(),
                ),
            ));
        }
        return Err(not_found());
    };
    // Unbounded for the same reason the engine's own reload is: these are the
    // server's persisted bytes, admitted under the operator's inflate ceiling
    // at deploy time.
    let package = match Package::load_from_bytes(&record.archive, ExtractionLimits::unbounded()) {
        Ok(package) => package,
        Err(error) => {
            return Ok(aion_awl::doc::without_source(
                workflow_type.to_owned(),
                aion_awl::doc::FAMILY_UNKNOWN.to_owned(),
                Some(content_hash.to_owned()),
                SourceState::Unreadable {
                    reason: error.to_string(),
                },
                ContractDoc::committed(
                    None,
                    None,
                    None,
                    Vec::new(),
                    Vec::new(),
                    Vec::new(),
                    Vec::new(),
                ),
            ));
        }
    };
    if !super::document::declares_workflow_type(&package, workflow_type) {
        return Err(not_found());
    }
    aion_awl::doc::from_package(&package, workflow_type).map_err(|error| DeployedError::DocModel {
        workflow_type: workflow_type.to_owned(),
        content_hash: content_hash.to_owned(),
        reason: error.to_string(),
    })
}