Skip to main content

fraiseql_cli/schema/intermediate/
subscriptions.rs

1//! Subscription/observer structs: `IntermediateSubscription`,
2//! `IntermediateSubscriptionFilter`, `IntermediateFilterCondition`,
3//! `IntermediateObserver`, `IntermediateRetryConfig`.
4
5use serde::{Deserialize, Serialize};
6
7use super::{operations::IntermediateArgument, types::IntermediateDeprecation};
8
9// =============================================================================
10// Subscription Definitions
11// =============================================================================
12
13/// Subscription definition in intermediate format.
14///
15/// Subscriptions provide real-time event streams for GraphQL clients.
16///
17/// # Example JSON
18///
19/// ```json
20/// {
21///   "name": "orderUpdated",
22///   "return_type": "Order",
23///   "arguments": [
24///     {"name": "orderId", "type": "ID", "nullable": true}
25///   ],
26///   "topic": "order_events",
27///   "filter": {
28///     "conditions": [
29///       {"argument": "orderId", "path": "$.id"}
30///     ]
31///   },
32///   "description": "Stream of order update events"
33/// }
34/// ```
35#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
36pub struct IntermediateSubscription {
37    /// Subscription name (e.g., "orderUpdated")
38    pub name: String,
39
40    /// Return type name (e.g., "Order")
41    pub return_type: String,
42
43    /// Subscription arguments (for filtering events)
44    #[serde(default)]
45    pub arguments: Vec<IntermediateArgument>,
46
47    /// Subscription description (from docstring)
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub description: Option<String>,
50
51    /// Event topic to subscribe to (e.g., "order_events")
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub topic: Option<String>,
54
55    /// Filter configuration for event matching
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub filter: Option<IntermediateSubscriptionFilter>,
58
59    /// Fields to project from event data
60    #[serde(default, skip_serializing_if = "Vec::is_empty")]
61    pub fields: Vec<String>,
62
63    /// Deprecation info (from @deprecated directive)
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub deprecated: Option<IntermediateDeprecation>,
66}
67
68/// Subscription filter definition for event matching.
69///
70/// Maps subscription arguments to JSONB paths in event data.
71#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
72pub struct IntermediateSubscriptionFilter {
73    /// Filter conditions mapping arguments to event data paths
74    pub conditions: Vec<IntermediateFilterCondition>,
75}
76
77/// A single filter condition for subscription event matching.
78#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
79pub struct IntermediateFilterCondition {
80    /// Argument name from subscription arguments
81    pub argument: String,
82
83    /// JSON path to the value in event data (e.g., "$.id", "$.order_status")
84    pub path: String,
85}
86
87// =============================================================================
88// Observer Definitions
89// =============================================================================
90
91/// Observer definition in intermediate format.
92///
93/// Observers listen to database change events (INSERT/UPDATE/DELETE) and execute
94/// actions (webhooks, Slack, email) when conditions are met.
95///
96/// # Example JSON
97///
98/// ```json
99/// {
100///   "name": "onHighValueOrder",
101///   "entity": "Order",
102///   "event": "INSERT",
103///   "condition": "total > 1000",
104///   "actions": [
105///     {
106///       "type": "webhook",
107///       "url": "https://api.example.com/orders",
108///       "headers": {"Content-Type": "application/json"}
109///     },
110///     {
111///       "type": "slack",
112///       "channel": "#sales",
113///       "message": "New order: {id}",
114///       "webhook_url_env": "SLACK_WEBHOOK_URL"
115///     }
116///   ],
117///   "retry": {
118///     "max_attempts": 3,
119///     "backoff_strategy": "exponential",
120///     "initial_delay_ms": 100,
121///     "max_delay_ms": 60000
122///   }
123/// }
124/// ```
125#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
126pub struct IntermediateObserver {
127    /// Observer name (unique identifier)
128    pub name: String,
129
130    /// Entity type to observe (e.g., "Order", "User")
131    pub entity: String,
132
133    /// Event type: INSERT, UPDATE, or DELETE
134    pub event: String,
135
136    /// Actions to execute when observer triggers
137    pub actions: Vec<IntermediateObserverAction>,
138
139    /// Optional condition expression in FraiseQL DSL
140    #[serde(skip_serializing_if = "Option::is_none")]
141    pub condition: Option<String>,
142
143    /// Retry configuration for action execution
144    pub retry: IntermediateRetryConfig,
145}
146
147/// Observer action (webhook, Slack, email, etc.).
148///
149/// Actions are stored as flexible JSON objects since they have different
150/// structures based on action type.
151pub type IntermediateObserverAction = serde_json::Value;
152
153/// Retry configuration for observer actions.
154///
155/// # Example JSON
156///
157/// ```json
158/// {
159///   "max_attempts": 5,
160///   "backoff_strategy": "exponential",
161///   "initial_delay_ms": 100,
162///   "max_delay_ms": 60000
163/// }
164/// ```
165#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
166pub struct IntermediateRetryConfig {
167    /// Maximum number of retry attempts
168    pub max_attempts: u32,
169
170    /// Backoff strategy: exponential, linear, or fixed
171    pub backoff_strategy: String,
172
173    /// Initial delay in milliseconds
174    pub initial_delay_ms: u32,
175
176    /// Maximum delay in milliseconds
177    pub max_delay_ms: u32,
178}