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, PartialEq, Eq, 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)]
216#[serde(rename_all = "camelCase")]
217pub struct ProviderErrorBody {
218    pub code: String,
219    pub message: String,
220    #[serde(default)]
221    pub retryable: bool,
222    #[serde(default, alias = "retry_after_ms")]
223    pub retry_after_ms: Option<u64>,
224    #[serde(default, alias = "provider_trace_reference")]
225    pub provider_trace_reference: Option<String>,
226    #[serde(default)]
227    pub details: Vec<ProviderErrorDetail>,
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
231#[serde(rename_all = "camelCase")]
232pub struct ProviderErrorDetail {
233    pub field: Option<String>,
234    pub reason: String,
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct ProviderListResponse {
239    pub records: Vec<Value>,
240    pub next_cursor: Option<String>,
241}
242
243impl From<ProviderListResponse> for AdminPage {
244    fn from(value: ProviderListResponse) -> Self {
245        Self {
246            records: value.records,
247            next_cursor: value.next_cursor,
248        }
249    }
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize)]
253pub struct ProviderGetResponse {
254    pub record: Option<Value>,
255}
256
257#[derive(Debug, Clone, Serialize, Deserialize)]
258pub struct ProviderActionInvokeResponse {
259    pub result: Value,
260}
261
262#[derive(Debug, Clone, Serialize, Deserialize)]
263pub struct ProviderQueryResponse {
264    pub data: Value,
265}
266
267#[derive(Debug, Clone, Serialize, Deserialize)]
268pub struct ProviderAdminListRequest {
269    pub entity: String,
270    pub limit: i64,
271    pub cursor: Option<String>,
272}
273
274#[derive(Debug, Clone, Serialize, Deserialize)]
275pub struct ProviderAdminGetRequest {
276    pub entity: String,
277    pub id: String,
278}
279
280#[derive(Debug, Clone, Serialize, Deserialize)]
281pub struct ProviderAdminActionInvokeRequest {
282    pub action: String,
283    pub input: Value,
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize)]
287pub struct ProviderAdminQueryRequest {
288    pub query: String,
289}
290
291#[derive(Debug, Clone, Serialize, Deserialize)]
292pub struct ProviderHttpProxyInvokeRequest {
293    pub request_id: String,
294    pub correlation_id: String,
295    pub module_name: String,
296    pub method: String,
297    pub declared_path: String,
298    pub provider_path: String,
299    pub path_params: BTreeMap<String, String>,
300    pub headers: BTreeMap<String, String>,
301    pub body: Option<Value>,
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
305pub struct ProviderHttpProxyInvokeResponse {
306    pub status_code: u16,
307    pub body: Option<Value>,
308}
309
310#[derive(Debug, Clone, Serialize, Deserialize)]
311pub struct ProviderFunctionInvokeRequest {
312    pub request_id: String,
313    pub function_run_id: String,
314    pub function_name: String,
315    pub attempt: u32,
316    pub correlation_id: String,
317    pub causation_id: Option<String>,
318    pub actor: ActorContext,
319    pub trace: TraceContext,
320    pub input: Value,
321}
322
323#[derive(Debug, Clone, Serialize, Deserialize)]
324pub struct ProviderFunctionInvokeResponse {
325    pub output: Value,
326}
327
328#[derive(Debug, Clone, Serialize, Deserialize)]
329pub struct ProviderEventHandleRequest {
330    pub request_id: String,
331    pub outbox_event_id: String,
332    pub handler_name: String,
333    pub event_name: String,
334    pub event_version: u16,
335    pub source_module: String,
336    pub aggregate_type: String,
337    pub aggregate_id: String,
338    pub correlation_id: String,
339    pub causation_id: Option<String>,
340    pub occurred_at: String,
341    pub actor: ActorContext,
342    pub trace: TraceContext,
343    pub payload: Value,
344    pub headers: Value,
345}
346
347#[derive(Debug, Clone, Default, Serialize, Deserialize)]
348pub struct ProviderEventHandleResponse {
349    #[serde(default)]
350    pub actions: Vec<ProviderEventResultAction>,
351}
352
353#[derive(Debug, Clone, Serialize, Deserialize)]
354#[serde(tag = "type", rename_all = "snake_case")]
355pub enum ProviderEventResultAction {
356    EnqueueFunction { function_name: String, input: Value },
357}