pub struct InteractionEngine { /* private fields */ }Expand description
Reads, writes and settles persisted interactions (spec §15).
Implementations§
Source§impl InteractionEngine
impl InteractionEngine
Sourcepub fn new(store: Arc<dyn InteractionStore>, config: InteractionConfig) -> Self
pub fn new(store: Arc<dyn InteractionStore>, config: InteractionConfig) -> Self
Builds an engine over store.
Sourcepub fn with_observer(self, observer: Arc<dyn Observer>) -> Self
pub fn with_observer(self, observer: Arc<dyn Observer>) -> Self
Sends this stage’s signals to observer (spec §26.2).
Orchestrator sets it from its own
observer when it is built, so a card settled out of band — the
§15.7 free-text channel, an operator tool, a reconciliation job — is
counted on the same series as one a turn settled.
Sourcepub const fn config(&self) -> &InteractionConfig
pub const fn config(&self) -> &InteractionConfig
The interaction configuration in force.
Sourcepub async fn create(
&self,
spec: InteractionSpec,
account: &AccountId,
conversation: ConversationId,
turn_id: TurnId,
now: DateTime<Utc>,
) -> Result<(Interaction, Vec<InteractionId>), InteractionError>
pub async fn create( &self, spec: InteractionSpec, account: &AccountId, conversation: ConversationId, turn_id: TurnId, now: DateTime<Utc>, ) -> Result<(Interaction, Vec<InteractionId>), InteractionError>
Materializes one specification and writes it (spec §15.5, §15.6).
A blocking card replaces the case’s current blocking occupant and invalidates it; a non-blocking one is inserted next to whatever is there. The identifier is derived from the turn and the specification key, so a replay writes the same card rather than a duplicate.
§Errors
InteractionError::InvalidSpecwhen the card could not be answered;InteractionError::NotPersistedwhen the store refused the write — after which the response must not mention the card.
Sourcepub async fn persist(
&self,
specs: &[InteractionSpec],
account: &AccountId,
conversation: ConversationId,
turn_id: TurnId,
now: DateTime<Utc>,
) -> PersistedInteractions
pub async fn persist( &self, specs: &[InteractionSpec], account: &AccountId, conversation: ConversationId, turn_id: TurnId, now: DateTime<Utc>, ) -> PersistedInteractions
Writes every specification, stopping at the first failure.
Nothing here is best effort in the sense of “carry on and hope”: the first failure stops the loop and is reported, precisely so the caller cannot describe cards that are not there.
Sourcepub async fn accept(
&self,
context: ResponseContext<'_>,
response: &InteractionResponse,
) -> Result<ResponseAdmission, InteractionError>
pub async fn accept( &self, context: ResponseContext<'_>, response: &InteractionResponse, ) -> Result<ResponseAdmission, InteractionError>
Validates a client answer and takes the card into Resolving
(spec §15.5, §23 step C).
current_revision is the revision the case is actually at, read fresh:
a card bound to an older one is
Stale whatever the client echoed.
§Errors
InteractionError::Rejected carrying the §15.5 rejection. An
identifier of another tenant is InteractionRejection::NotFound,
exactly like one that never existed (spec §25.4).
Sourcepub async fn settle(
&self,
account: &AccountId,
id: &InteractionId,
outcome: ResolutionOutcome,
) -> Result<InteractionRecord, InteractionError>
pub async fn settle( &self, account: &AccountId, id: &InteractionId, outcome: ResolutionOutcome, ) -> Result<InteractionRecord, InteractionError>
Settles a Resolving card.
§Errors
InteractionError::NotPersisted when the store refused, and
InteractionError::Rejected with
NotFound when the card is not there.
Sourcepub async fn mark_resolved(
&self,
account: &AccountId,
id: &InteractionId,
event_ids: Vec<EventId>,
) -> Result<InteractionRecord, InteractionError>
pub async fn mark_resolved( &self, account: &AccountId, id: &InteractionId, event_ids: Vec<EventId>, ) -> Result<InteractionRecord, InteractionError>
Marks a card Resolved — only legitimate once the command its answer
authorized has committed, which is why the events are required (§15.5).
§Errors
See Self::settle.
Sourcepub async fn mark_failed(
&self,
account: &AccountId,
id: &InteractionId,
code: impl Into<String>,
) -> Result<InteractionRecord, InteractionError>
pub async fn mark_failed( &self, account: &AccountId, id: &InteractionId, code: impl Into<String>, ) -> Result<InteractionRecord, InteractionError>
Sourcepub async fn restore(
&self,
account: &AccountId,
id: &InteractionId,
) -> Result<InteractionRecord, InteractionError>
pub async fn restore( &self, account: &AccountId, id: &InteractionId, ) -> Result<InteractionRecord, InteractionError>
Sourcepub async fn open_for_conversation(
&self,
account: &AccountId,
conversation: &ConversationId,
) -> Result<Vec<Interaction>, InteractionError>
pub async fn open_for_conversation( &self, account: &AccountId, conversation: &ConversationId, ) -> Result<Vec<Interaction>, InteractionError>
The open cards of a conversation, oldest first.
§Errors
InteractionError::NotPersisted when the store could not answer;
silently pretending there are none would let a blocking card be
bypassed (I19).
Sourcepub async fn open_for_case(
&self,
account: &AccountId,
case_key: &CaseKey,
) -> Result<Vec<Interaction>, InteractionError>
pub async fn open_for_case( &self, account: &AccountId, case_key: &CaseKey, ) -> Result<Vec<Interaction>, InteractionError>
Sourcepub async fn blocking_answered_at(
&self,
account: &AccountId,
case_key: &CaseKey,
revision: CaseRevision,
) -> Result<bool, InteractionError>
pub async fn blocking_answered_at( &self, account: &AccountId, case_key: &CaseKey, revision: CaseRevision, ) -> Result<bool, InteractionError>
Whether the user has already answered a blocking card of this case at this revision.
What stops a declined card going straight back up. See
InteractionReader::blocking_answered_at.
§Errors
Sourcepub async fn get(
&self,
account: &AccountId,
id: &InteractionId,
) -> Result<InteractionRecord, InteractionError>
pub async fn get( &self, account: &AccountId, id: &InteractionId, ) -> Result<InteractionRecord, InteractionError>
One stored card, whatever its status.
§Errors
See Self::open_for_conversation; an identifier of another tenant is
InteractionRejection::NotFound.