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)]
13#[serde(deny_unknown_fields)]
14pub struct FacadeRequest {
15 pub operation_id: OperationId,
17 pub target: String,
19 pub input: Value,
21}
22
23pub use crate::{
24 ClientTask, InteractionKind, InteractionPreview, InteractionTone, PreviewDetail, PreviewValue,
25};
26
27#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
29#[serde(deny_unknown_fields)]
30pub struct PreparedAction {
31 pub action_id: ActionId,
33 pub operation_id: OperationId,
35 pub preview: InteractionPreview,
37 #[serde(default)]
39 pub payload: Value,
40 pub revision: u64,
42 pub approval: ApprovalRequirement,
44 pub interaction_kind: InteractionKind,
46}
47
48#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
50#[serde(deny_unknown_fields)]
51pub struct FacadeResult {
52 pub value: Value,
54 #[serde(default, skip_serializing_if = "Option::is_none")]
56 pub continuation: Option<PreparedAction>,
57 #[serde(default, skip_serializing_if = "Option::is_none")]
59 pub context_invalidation: Option<crate::execution::ContextInvalidation>,
60}
61
62#[async_trait]
67pub trait AppFacade: Send + Sync {
68 async fn invoke(
70 &self,
71 context: &InvocationContext,
72 request: FacadeRequest,
73 ) -> Result<FacadeResult, ExternalError>;
74
75 async fn prepare(
77 &self,
78 context: &InvocationContext,
79 request: FacadeRequest,
80 ) -> Result<PreparedAction, ExternalError>;
81
82 async fn commit(
84 &self,
85 context: &InvocationContext,
86 action: PreparedAction,
87 ) -> Result<FacadeResult, ExternalError>;
88
89 async fn reject(
91 &self,
92 context: &InvocationContext,
93 action: PreparedAction,
94 ) -> Result<(), ExternalError>;
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn typed_preview_rejects_incomplete_client_work() {
103 let mut preview = InteractionPreview::confirmation(
104 "integration.install",
105 "Integration required",
106 "Install it, then continue.",
107 );
108 preview.client_task = Some(ClientTask::InstallIntegrations {
109 integration_ids: Vec::new(),
110 });
111 assert!(!preview.is_valid());
112
113 preview.client_task = Some(ClientTask::InstallIntegrations {
114 integration_ids: vec!["matter".to_owned()],
115 });
116 assert!(preview.is_valid());
117 let encoded = serde_json::to_value(preview).expect("serializes");
118 assert_eq!(encoded["clientTask"]["type"], "install_integrations");
119 assert_eq!(encoded["clientTask"]["integrationIds"][0], "matter");
120 }
121}