conversation_api/execution/
facade.rs1use async_trait::async_trait;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7use crate::execution::{ActionId, ExternalError, InvocationContext, OperationId};
8
9#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
11#[serde(deny_unknown_fields)]
12pub struct FacadeRequest {
13 pub operation_id: OperationId,
15 pub target: String,
17 pub input: Value,
19}
20
21pub use crate::{
22 ClientTask, InteractionKind, InteractionPreview, InteractionTone, PreviewDetail, PreviewValue,
23};
24
25#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
27#[serde(deny_unknown_fields)]
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 interaction_kind: InteractionKind,
42}
43
44#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
46#[serde(deny_unknown_fields)]
47pub struct FacadeResult {
48 pub value: Value,
50 #[serde(default, skip_serializing_if = "Option::is_none")]
52 pub continuation: Option<PreparedAction>,
53 #[serde(default, skip_serializing_if = "Option::is_none")]
55 pub context_invalidation: Option<crate::execution::ContextInvalidation>,
56}
57
58#[async_trait]
63pub trait AppFacade: Send + Sync {
64 async fn invoke(
66 &self,
67 context: &InvocationContext,
68 request: FacadeRequest,
69 ) -> Result<FacadeResult, ExternalError>;
70
71 async fn prepare(
73 &self,
74 context: &InvocationContext,
75 request: FacadeRequest,
76 ) -> Result<PreparedAction, ExternalError>;
77
78 async fn commit(
80 &self,
81 context: &InvocationContext,
82 action: PreparedAction,
83 ) -> Result<FacadeResult, ExternalError>;
84
85 async fn reject(
87 &self,
88 context: &InvocationContext,
89 action: PreparedAction,
90 ) -> Result<(), ExternalError>;
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn typed_preview_rejects_incomplete_client_work() {
99 let mut preview = InteractionPreview::confirmation(
100 "integration.install",
101 "Integration required",
102 "Install it, then continue.",
103 );
104 preview.client_task = Some(ClientTask::ConnectIntegration {
105 integration_id: String::new(),
106 session_id: "session".into(),
107 });
108 assert!(!preview.is_valid());
109
110 preview.client_task = Some(ClientTask::ConnectIntegration {
111 integration_id: "provider.example".to_owned(),
112 session_id: "session".into(),
113 });
114 assert!(preview.is_valid());
115 let encoded = serde_json::to_value(preview).expect("serializes");
116 assert_eq!(encoded["clientTask"]["type"], "connect_integration");
117 assert_eq!(encoded["clientTask"]["integrationId"], "provider.example");
118 }
119}