Skip to main content

platform_provider/
protocol.rs

1use chrono::{DateTime, Utc};
2use platform_core::{ActorContext, TraceContext};
3use platform_module::{AdminPage, ModuleManifest};
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use std::collections::BTreeMap;
7
8pub const PROVIDER_PROTOCOL: &str = "lenso.provider.v1";
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11#[serde(rename_all = "camelCase", deny_unknown_fields)]
12pub struct ProviderDescriptor {
13    pub protocol: String,
14    pub protocol_contract_digest: String,
15    pub service_id: String,
16    pub service_release_version: String,
17    pub service_release_digest: String,
18    pub runtime_instance_id: String,
19    #[serde(default)]
20    pub features: Vec<String>,
21    pub transports: Vec<ProviderTransportBinding>,
22    pub exports: Vec<ProviderExport>,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
26#[serde(rename_all = "snake_case")]
27pub enum ProviderTransportBinding {
28    HttpJson,
29    Grpc,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
33#[serde(rename_all = "camelCase", deny_unknown_fields)]
34pub struct ProviderExport {
35    pub export_key: String,
36    pub module_id: String,
37    pub module_version: String,
38    pub module_release_digest: String,
39    pub manifest_digest: String,
40    pub manifest: ModuleManifest,
41    #[serde(default)]
42    pub contract_digests: BTreeMap<String, String>,
43    pub ready: bool,
44    #[serde(default)]
45    pub readiness_reasons: Vec<String>,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49#[serde(rename_all = "snake_case")]
50pub enum ProviderOperationKind {
51    HttpRoute,
52    AdminList,
53    AdminGet,
54    AdminQuery,
55    AdminAction,
56    RuntimeFunction,
57    EventHandler,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
61#[serde(rename_all = "snake_case")]
62pub enum ProviderInvocationMode {
63    ReadOnly,
64    Durable,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
68#[serde(rename_all = "camelCase", deny_unknown_fields)]
69pub struct ProviderInvocation {
70    pub protocol: String,
71    pub invocation_id: String,
72    pub request_id: String,
73    pub attempt: u32,
74    pub deadline: String,
75    pub service_release_digest: String,
76    pub export_key: String,
77    pub module_release_digest: String,
78    pub manifest_digest: String,
79    pub operation_kind: ProviderOperationKind,
80    pub operation_name: String,
81    pub operation_version: String,
82    pub mode: ProviderInvocationMode,
83    pub input_contract_digest: String,
84    pub output_contract_digest: String,
85    #[serde(default)]
86    pub tenant_id: Option<String>,
87    pub actor: ActorContext,
88    #[serde(default)]
89    pub delegation: Option<Value>,
90    #[serde(default)]
91    pub locale: Option<String>,
92    #[serde(default)]
93    pub context: BTreeMap<String, Value>,
94    pub correlation_id: String,
95    #[serde(default)]
96    pub causation_id: Option<String>,
97    pub trace: TraceContext,
98    pub content_type: String,
99    pub payload: Value,
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize)]
103#[serde(rename_all = "snake_case")]
104pub enum ProviderOutcomeStatus {
105    Pending,
106    Succeeded,
107    Rejected,
108    Failed,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
112#[serde(rename_all = "camelCase", deny_unknown_fields)]
113pub struct ProviderOutcome {
114    pub protocol: String,
115    pub invocation_id: String,
116    pub status: ProviderOutcomeStatus,
117    #[serde(default)]
118    pub result: Option<Value>,
119    #[serde(default)]
120    pub error: Option<ProviderErrorBody>,
121    #[serde(default)]
122    pub effect_evidence: Vec<Value>,
123    #[serde(default)]
124    pub host_effects: ProviderHostEffectBatch,
125    pub outcome_digest: String,
126}
127
128#[derive(Debug, Clone, Default, Serialize, Deserialize)]
129#[serde(rename_all = "camelCase", deny_unknown_fields)]
130pub struct ProviderHostEffectBatch {
131    #[serde(default)]
132    pub events: Vec<ProviderHostEventEffect>,
133    #[serde(default)]
134    pub runtime_function_requests: Vec<ProviderHostRuntimeFunctionRequest>,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
138#[serde(rename_all = "camelCase", deny_unknown_fields)]
139pub struct ProviderHostEventEffect {
140    pub event_id: String,
141    pub event_name: String,
142    pub event_version: u16,
143    pub source_module: String,
144    pub aggregate_type: String,
145    pub aggregate_id: String,
146    pub correlation_id: String,
147    #[serde(default)]
148    pub causation_id: Option<String>,
149    pub occurred_at: DateTime<Utc>,
150    pub payload: Value,
151    #[serde(default)]
152    pub headers: Value,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
156#[serde(rename_all = "camelCase", deny_unknown_fields)]
157pub struct ProviderHostRuntimeFunctionRequest {
158    pub request_id: String,
159    pub function_name: String,
160    pub input: Value,
161    pub correlation_id: String,
162    pub actor: ActorContext,
163    #[serde(default)]
164    pub tenant_id: Option<String>,
165    #[serde(default)]
166    pub trace: TraceContext,
167    #[serde(default)]
168    pub causation_id: Option<String>,
169    #[serde(default)]
170    pub max_attempts: Option<i32>,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
174#[serde(rename_all = "camelCase", deny_unknown_fields)]
175pub struct ProviderHealth {
176    pub protocol: String,
177    pub service_id: String,
178    pub service_release_digest: String,
179    pub live: bool,
180    pub ready: bool,
181    pub observed_at: String,
182    #[serde(default)]
183    pub exports: BTreeMap<String, ProviderExportHealth>,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
187#[serde(rename_all = "camelCase", deny_unknown_fields)]
188pub struct ProviderExportHealth {
189    pub ready: bool,
190    #[serde(default)]
191    pub reasons: Vec<String>,
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
195#[serde(rename_all = "camelCase", deny_unknown_fields)]
196pub struct ProviderInvocationReference {
197    pub invocation_id: String,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize)]
201#[serde(rename_all = "camelCase", deny_unknown_fields)]
202pub struct ProviderInvocationAcknowledgement {
203    pub invocation_id: String,
204    pub outcome_digest: String,
205}
206
207pub type ProviderManifestResponse = ModuleManifest;
208
209/// Standard error response shape for the Provider Service protocol.
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct ProviderErrorEnvelope {
212    pub error: ProviderErrorBody,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
216pub struct ProviderErrorBody {
217    pub code: String,
218    pub message: String,
219    #[serde(default)]
220    pub retryable: bool,
221    #[serde(default)]
222    pub retry_after_ms: Option<u64>,
223    #[serde(default)]
224    pub provider_trace_reference: Option<String>,
225    #[serde(default)]
226    pub details: Vec<ProviderErrorDetail>,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct ProviderErrorDetail {
231    pub field: Option<String>,
232    pub reason: String,
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct ProviderListResponse {
237    pub records: Vec<Value>,
238    pub next_cursor: Option<String>,
239}
240
241impl From<ProviderListResponse> for AdminPage {
242    fn from(value: ProviderListResponse) -> Self {
243        Self {
244            records: value.records,
245            next_cursor: value.next_cursor,
246        }
247    }
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct ProviderGetResponse {
252    pub record: Option<Value>,
253}
254
255#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct ProviderActionInvokeResponse {
257    pub result: Value,
258}
259
260#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct ProviderQueryResponse {
262    pub data: Value,
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize)]
266pub struct ProviderAdminListRequest {
267    pub entity: String,
268    pub limit: i64,
269    pub cursor: Option<String>,
270}
271
272#[derive(Debug, Clone, Serialize, Deserialize)]
273pub struct ProviderAdminGetRequest {
274    pub entity: String,
275    pub id: String,
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct ProviderAdminActionInvokeRequest {
280    pub action: String,
281    pub input: Value,
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
285pub struct ProviderAdminQueryRequest {
286    pub query: String,
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct ProviderHttpProxyInvokeRequest {
291    pub request_id: String,
292    pub correlation_id: String,
293    pub module_name: String,
294    pub method: String,
295    pub declared_path: String,
296    pub provider_path: String,
297    pub path_params: BTreeMap<String, String>,
298    pub headers: BTreeMap<String, String>,
299    pub body: Option<Value>,
300}
301
302#[derive(Debug, Clone, Serialize, Deserialize)]
303pub struct ProviderHttpProxyInvokeResponse {
304    pub status_code: u16,
305    pub body: Option<Value>,
306}
307
308#[derive(Debug, Clone, Serialize, Deserialize)]
309pub struct ProviderFunctionInvokeRequest {
310    pub request_id: String,
311    pub function_run_id: String,
312    pub function_name: String,
313    pub attempt: u32,
314    pub correlation_id: String,
315    pub causation_id: Option<String>,
316    pub actor: ActorContext,
317    pub trace: TraceContext,
318    pub input: Value,
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize)]
322pub struct ProviderFunctionInvokeResponse {
323    pub output: Value,
324}
325
326#[derive(Debug, Clone, Serialize, Deserialize)]
327pub struct ProviderEventHandleRequest {
328    pub request_id: String,
329    pub outbox_event_id: String,
330    pub handler_name: String,
331    pub event_name: String,
332    pub event_version: u16,
333    pub source_module: String,
334    pub aggregate_type: String,
335    pub aggregate_id: String,
336    pub correlation_id: String,
337    pub causation_id: Option<String>,
338    pub occurred_at: String,
339    pub actor: ActorContext,
340    pub trace: TraceContext,
341    pub payload: Value,
342    pub headers: Value,
343}
344
345#[derive(Debug, Clone, Default, Serialize, Deserialize)]
346pub struct ProviderEventHandleResponse {
347    #[serde(default)]
348    pub actions: Vec<ProviderEventResultAction>,
349}
350
351#[derive(Debug, Clone, Serialize, Deserialize)]
352#[serde(tag = "type", rename_all = "snake_case")]
353pub enum ProviderEventResultAction {
354    EnqueueFunction { function_name: String, input: Value },
355}