Skip to main content

gestalt/
api.rs

1use std::collections::BTreeMap;
2use std::convert::Infallible;
3
4use tonic::codegen::async_trait;
5
6use crate::catalog::Catalog;
7use crate::error::{Error, Result};
8
9#[derive(Clone, Debug, Default, Eq, PartialEq)]
10/// Identifies the caller that initiated an operation.
11pub struct Subject {
12    /// Stable subject id.
13    pub id: String,
14    /// Subject kind, such as user or service account.
15    pub kind: String,
16    /// Human-readable display name.
17    pub display_name: String,
18    /// Authentication source that produced the subject.
19    pub auth_source: String,
20}
21
22#[derive(Clone, Debug, Default, Eq, PartialEq)]
23/// Describes the resolved credential used to authorize an operation.
24pub struct Credential {
25    /// Credential mode used by the host.
26    pub mode: String,
27    /// Subject id associated with the credential.
28    pub subject_id: String,
29    /// Connection id or name associated with the credential.
30    pub connection: String,
31    /// Provider instance id or name associated with the credential.
32    pub instance: String,
33}
34
35#[derive(Clone, Debug, Default, Eq, PartialEq)]
36/// Summarizes the host-side access decision attached to an operation.
37pub struct Access {
38    /// Policy name or id applied to the request.
39    pub policy: String,
40    /// Effective role granted to the request.
41    pub role: String,
42}
43
44#[derive(Clone, Debug, Default, Eq, PartialEq)]
45/// Describes public host metadata attached to a request.
46pub struct Host {
47    /// Public base URL for the Gestalt host.
48    pub public_base_url: String,
49}
50
51#[derive(Clone, Debug, Default, PartialEq)]
52/// Carries execution-scoped metadata into typed operation handlers.
53pub struct Request {
54    /// Request token supplied to hosted HTTP operation handlers.
55    pub token: String,
56    /// Connection parameters resolved by the host.
57    pub connection_params: BTreeMap<String, String>,
58    /// Subject that initiated the request.
59    pub subject: Subject,
60    /// Credential used to authorize the request.
61    pub credential: Credential,
62    /// Access decision attached to the request.
63    pub access: Access,
64    /// Public host metadata attached to the request.
65    pub host: Host,
66    /// Idempotency key supplied by the host.
67    pub idempotency_key: String,
68    /// Workflow callback metadata uses a JSON-style lowerCamelCase object
69    /// such as `runId`, `target.plugin.pluginName`, `trigger.scheduleId`, and
70    /// `trigger.event.specVersion`.
71    pub workflow: serde_json::Map<String, serde_json::Value>,
72    /// Invocation token used to call host services.
73    pub invocation_token: String,
74}
75
76impl Request {
77    /// Returns one resolved connection parameter by name.
78    pub fn connection_param(&self, name: &str) -> Option<&str> {
79        self.connection_params.get(name).map(String::as_str)
80    }
81
82    /// Returns the invocation token used to call host services.
83    pub fn invocation_token(&self) -> &str {
84        &self.invocation_token
85    }
86
87    /// Creates a plugin invoker using this request's invocation token.
88    pub async fn invoker(
89        &self,
90    ) -> std::result::Result<crate::PluginInvoker, crate::PluginInvokerError> {
91        crate::PluginInvoker::connect(self.invocation_token()).await
92    }
93
94    /// Creates a workflow manager using this request's invocation token.
95    pub async fn workflow_manager(
96        &self,
97    ) -> std::result::Result<crate::WorkflowManager, crate::WorkflowManagerError> {
98        crate::WorkflowManager::connect_with_idempotency_key(
99            self.invocation_token(),
100            self.idempotency_key.trim(),
101        )
102        .await
103    }
104
105    /// Creates an agent manager using this request's invocation token.
106    pub async fn agent_manager(
107        &self,
108    ) -> std::result::Result<crate::AgentManager, crate::AgentManagerError> {
109        crate::AgentManager::connect(self.invocation_token()).await
110    }
111}
112
113#[derive(Clone, Debug, Eq, PartialEq)]
114/// Wraps a typed handler response plus an optional explicit HTTP status code.
115pub struct Response<T> {
116    /// Optional explicit HTTP-style status code.
117    pub status: Option<u16>,
118    /// Typed response body returned by the handler.
119    pub body: T,
120}
121
122impl<T> Response<T> {
123    /// Creates a response with an explicit HTTP status code.
124    pub fn new(status: u16, body: T) -> Self {
125        Self {
126            status: Some(status),
127            body,
128        }
129    }
130}
131
132/// Returns a successful JSON response with status code `200`.
133pub fn ok<T>(body: T) -> Response<T> {
134    Response::new(200, body)
135}
136
137/// Converts handler return values into a typed [`Response`].
138pub trait IntoResponse<T> {
139    /// Converts a handler return value into a typed response wrapper.
140    fn into_response(self) -> Response<T>;
141}
142
143impl<T> IntoResponse<T> for Response<T> {
144    fn into_response(self) -> Response<T> {
145        self
146    }
147}
148
149impl<T> IntoResponse<T> for T {
150    fn into_response(self) -> Response<T> {
151        ok(self)
152    }
153}
154
155#[derive(Clone, Debug, Default, Eq, PartialEq)]
156/// Describes provider metadata that should be surfaced by the runtime.
157pub struct RuntimeMetadata {
158    /// Provider name to report to the host.
159    pub name: String,
160    /// Human-readable provider display name.
161    pub display_name: String,
162    /// Human-readable provider description.
163    pub description: String,
164    /// Provider version string.
165    pub version: String,
166}
167
168#[async_trait]
169/// Shared lifecycle contract for Gestalt integration providers.
170pub trait Provider: Send + Sync + 'static {
171    /// Configures the provider before it starts serving requests.
172    async fn configure(
173        &self,
174        _name: &str,
175        _config: serde_json::Map<String, serde_json::Value>,
176    ) -> Result<()> {
177        Ok(())
178    }
179
180    /// Returns runtime metadata that should augment the static manifest.
181    fn metadata(&self) -> Option<RuntimeMetadata> {
182        None
183    }
184
185    /// Returns non-fatal warnings the host should surface to users.
186    fn warnings(&self) -> Vec<String> {
187        Vec::new()
188    }
189
190    /// Performs an optional health check.
191    async fn health_check(&self) -> Result<()> {
192        Ok(())
193    }
194
195    /// Starts provider-owned background work after configuration.
196    async fn start(&self) -> Result<()> {
197        Ok(())
198    }
199
200    /// Reports whether this provider can derive additional operations from the
201    /// current request context.
202    fn supports_session_catalog(&self) -> bool {
203        false
204    }
205
206    /// Returns an optional request-scoped catalog extension.
207    async fn catalog_for_request(&self, _request: &Request) -> Result<Option<Catalog>> {
208        Ok(None)
209    }
210
211    /// Shuts the provider down before the runtime exits.
212    async fn close(&self) -> Result<()> {
213        Ok(())
214    }
215}
216
217impl From<Infallible> for Error {
218    fn from(_value: Infallible) -> Self {
219        Error::internal("unreachable infallible error")
220    }
221}