conversation_api/execution/
facade.rs1use async_trait::async_trait;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7use crate::execution::{
8 ActionId, ApprovalRequirement, ExternalError, InvocationContext, OperationId,
9};
10
11#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
13pub struct FacadeRequest {
14 pub operation_id: OperationId,
16 pub target: String,
18 pub input: Value,
20}
21
22pub use crate::{
23 ClientTask, InteractionKind, InteractionPreview, InteractionTone, PreviewDetail, PreviewValue,
24};
25
26#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
28pub struct PreparedAction {
29 pub action_id: ActionId,
31 pub operation_id: OperationId,
33 pub preview: InteractionPreview,
35 #[serde(default)]
37 pub payload: Value,
38 pub revision: u64,
40 pub approval: ApprovalRequirement,
42 pub interaction_kind: InteractionKind,
44}
45
46#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
48pub struct FacadeResult {
49 pub value: Value,
51 #[serde(default, skip_serializing_if = "Option::is_none")]
53 pub continuation: Option<PreparedAction>,
54 #[serde(default, skip_serializing_if = "Option::is_none")]
56 pub context_invalidation: Option<crate::execution::ContextInvalidation>,
57}
58
59#[async_trait]
64pub trait AppFacade: Send + Sync {
65 async fn invoke(
67 &self,
68 context: &InvocationContext,
69 request: FacadeRequest,
70 ) -> Result<FacadeResult, ExternalError>;
71
72 async fn prepare(
74 &self,
75 context: &InvocationContext,
76 request: FacadeRequest,
77 ) -> Result<PreparedAction, ExternalError>;
78
79 async fn commit(
81 &self,
82 context: &InvocationContext,
83 action: PreparedAction,
84 ) -> Result<FacadeResult, ExternalError>;
85
86 async fn reject(
88 &self,
89 context: &InvocationContext,
90 action: PreparedAction,
91 ) -> Result<(), ExternalError>;
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn typed_preview_rejects_incomplete_client_work() {
100 let mut preview = InteractionPreview::confirmation(
101 "integration.install",
102 "Integration required",
103 "Install it, then continue.",
104 );
105 preview.client_task = Some(ClientTask::InstallIntegrations {
106 integration_ids: Vec::new(),
107 });
108 assert!(!preview.is_valid());
109
110 preview.client_task = Some(ClientTask::InstallIntegrations {
111 integration_ids: vec!["matter".to_owned()],
112 });
113 assert!(preview.is_valid());
114 let encoded = serde_json::to_value(preview).expect("serializes");
115 assert_eq!(encoded["clientTask"]["type"], "install_integrations");
116 assert_eq!(encoded["clientTask"]["integrationIds"][0], "matter");
117 }
118}