1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use thiserror::Error;
4
5use crate::{
6 ControllerQueryId, DataSourceId, IdentifierError, ProviderKind, validate_surface_identifier,
7};
8
9pub const MIN_PROVIDER_REFRESH_INTERVAL_SECONDS: u32 = 10;
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(rename_all = "snake_case", tag = "kind")]
13pub enum DataSourceKind {
14 Static { data: Value },
15 ControllerQuery { query_id: ControllerQueryId },
16 ProviderQuery { operation_id: String },
17}
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(rename_all = "snake_case", tag = "type")]
21pub enum RefreshPolicy {
22 Manual,
23 Interval { seconds: u32 },
24 Sse { topic: ControllerSseTopicId },
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
28#[serde(transparent)]
29pub struct ControllerSseTopicId(String);
30
31impl ControllerSseTopicId {
32 pub fn new(value: impl Into<String>) -> Result<Self, IdentifierError> {
39 let value = value.into();
40 validate_surface_identifier(&value)?;
41 Ok(Self(value))
42 }
43
44 #[must_use]
45 pub const fn as_str(&self) -> &str {
46 self.0.as_str()
47 }
48}
49
50impl<'de> Deserialize<'de> for ControllerSseTopicId {
51 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
52 where
53 D: serde::Deserializer<'de>,
54 {
55 let value = String::deserialize(deserializer)?;
56 Self::new(value).map_err(serde::de::Error::custom)
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
61#[serde(rename_all = "snake_case", tag = "type")]
62pub enum SchemaContract {
63 Any,
64 Object,
65 Array,
66 String,
67 Integer,
68 Number,
69 Boolean,
70 Null,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
74pub struct DataSourceDescriptor {
75 pub data_source_id: DataSourceId,
76 pub kind: DataSourceKind,
77 pub result_schema: SchemaContract,
78 #[serde(default, skip_serializing_if = "Option::is_none")]
79 pub pagination: Option<DataSourcePagination>,
80 #[serde(default, skip_serializing_if = "Option::is_none")]
81 pub sorting: Option<DataSourceSorting>,
82 #[serde(default, skip_serializing_if = "Option::is_none")]
83 pub filtering: Option<DataSourceFiltering>,
84 pub refresh_policy: RefreshPolicy,
85 #[serde(default, skip_serializing_if = "Option::is_none")]
86 pub empty_state: Option<DataSourceEmptyState>,
87}
88
89#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
90pub struct DataSourcePagination {
91 pub default_page_size: u16,
92 pub max_page_size: u16,
93}
94
95#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
96pub struct DataSourceSorting {
97 #[serde(default, skip_serializing_if = "Vec::is_empty")]
98 pub sortable_fields: Vec<String>,
99 #[serde(default, skip_serializing_if = "Option::is_none")]
100 pub default_sort_field: Option<String>,
101}
102
103#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
104pub struct DataSourceFiltering {
105 #[serde(default, skip_serializing_if = "Vec::is_empty")]
106 pub filter_fields: Vec<String>,
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
110pub struct DataSourceEmptyState {
111 pub title: String,
112 #[serde(default, skip_serializing_if = "Option::is_none")]
113 pub description: Option<String>,
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
117pub enum DataSourceValidationError {
118 #[error("service providers cannot declare controller_query data sources")]
119 ServiceControllerQueryForbidden,
120 #[error(
121 "provider_query interval refresh must be at least {MIN_PROVIDER_REFRESH_INTERVAL_SECONDS} seconds"
122 )]
123 ProviderIntervalTooLow,
124}
125
126impl DataSourceDescriptor {
127 pub fn validate_for_provider(
137 &self,
138 provider_kind: ProviderKind,
139 ) -> Result<(), DataSourceValidationError> {
140 if provider_kind == ProviderKind::Service
141 && matches!(self.kind, DataSourceKind::ControllerQuery { .. })
142 {
143 return Err(DataSourceValidationError::ServiceControllerQueryForbidden);
144 }
145
146 if matches!(self.kind, DataSourceKind::ProviderQuery { .. })
147 && matches!(
148 self.refresh_policy,
149 RefreshPolicy::Interval { seconds }
150 if seconds < MIN_PROVIDER_REFRESH_INTERVAL_SECONDS
151 )
152 {
153 return Err(DataSourceValidationError::ProviderIntervalTooLow);
154 }
155
156 Ok(())
157 }
158}