systemprompt_agent/repository/content/
push_notification.rs1use chrono::Utc;
11use sqlx::PgPool;
12use std::sync::Arc;
13use systemprompt_database::DbPool;
14use systemprompt_identifiers::{ConfigId, TaskId};
15use systemprompt_traits::RepositoryError;
16
17use crate::models::a2a::protocol::PushNotificationConfig;
18use crate::models::database_rows::PushNotificationConfigRow;
19
20#[derive(Clone)]
21pub struct PushNotificationConfigRepository {
22 pool: Arc<PgPool>,
23 write_pool: Arc<PgPool>,
24}
25
26impl std::fmt::Debug for PushNotificationConfigRepository {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 f.debug_struct("PushNotificationConfigRepository")
29 .field("pool", &"<PgPool>")
30 .finish()
31 }
32}
33
34impl PushNotificationConfigRepository {
35 pub fn new(db: &DbPool) -> Result<Self, crate::error::AgentError> {
36 let pool = db
37 .pool_arc()
38 .map_err(|e| crate::error::AgentError::Init(e.to_string()))?;
39 let write_pool = db
40 .write_pool_arc()
41 .map_err(|e| crate::error::AgentError::Init(e.to_string()))?;
42 Ok(Self { pool, write_pool })
43 }
44
45 pub async fn add_config(
46 &self,
47 task_id: &TaskId,
48 config: &PushNotificationConfig,
49 ) -> Result<String, RepositoryError> {
50 let config_id = uuid::Uuid::new_v4().to_string();
51 let headers_json = config
52 .headers
53 .as_ref()
54 .map(serde_json::to_value)
55 .transpose()?;
56 let auth_json = config
57 .authentication
58 .as_ref()
59 .map(serde_json::to_value)
60 .transpose()?;
61 let now = Utc::now();
62 let task_id_str = task_id.as_str();
63
64 sqlx::query!(
65 r#"INSERT INTO task_push_notification_configs
66 (id, task_id, url, endpoint, token, headers, authentication, created_at, updated_at)
67 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)"#,
68 config_id,
69 task_id_str,
70 config.url,
71 config.endpoint,
72 config.token,
73 headers_json,
74 auth_json,
75 now,
76 now
77 )
78 .execute(&*self.write_pool)
79 .await
80 .map_err(RepositoryError::database)?;
81
82 Ok(config_id)
83 }
84
85 pub async fn get_config(
86 &self,
87 task_id: &TaskId,
88 config_id: &ConfigId,
89 ) -> Result<Option<PushNotificationConfig>, RepositoryError> {
90 let task_id_str = task_id.as_str();
91 let config_id_str = config_id.as_str();
92 let row = sqlx::query_as!(
93 PushNotificationConfigRow,
94 r#"SELECT
95 id,
96 task_id,
97 url,
98 endpoint,
99 token,
100 headers,
101 authentication,
102 created_at,
103 updated_at
104 FROM task_push_notification_configs
105 WHERE task_id = $1 AND id = $2"#,
106 task_id_str,
107 config_id_str
108 )
109 .fetch_optional(&*self.pool)
110 .await
111 .map_err(RepositoryError::database)?;
112
113 row.map(|r| Self::row_to_config(&r)).transpose()
114 }
115
116 pub async fn list_configs(
117 &self,
118 task_id: &TaskId,
119 ) -> Result<Vec<PushNotificationConfig>, RepositoryError> {
120 let task_id_str = task_id.as_str();
121 let rows: Vec<PushNotificationConfigRow> = sqlx::query_as!(
122 PushNotificationConfigRow,
123 r#"SELECT
124 id,
125 task_id,
126 url,
127 endpoint,
128 token,
129 headers,
130 authentication,
131 created_at,
132 updated_at
133 FROM task_push_notification_configs
134 WHERE task_id = $1"#,
135 task_id_str
136 )
137 .fetch_all(&*self.pool)
138 .await
139 .map_err(RepositoryError::database)?;
140
141 rows.iter()
142 .map(Self::row_to_config)
143 .collect::<Result<Vec<_>, RepositoryError>>()
144 }
145
146 pub async fn delete_config(
147 &self,
148 task_id: &TaskId,
149 config_id: &ConfigId,
150 ) -> Result<bool, RepositoryError> {
151 let task_id_str = task_id.as_str();
152 let config_id_str = config_id.as_str();
153 let result = sqlx::query!(
154 "DELETE FROM task_push_notification_configs WHERE task_id = $1 AND id = $2",
155 task_id_str,
156 config_id_str
157 )
158 .execute(&*self.write_pool)
159 .await
160 .map_err(RepositoryError::database)?;
161
162 Ok(result.rows_affected() > 0)
163 }
164
165 pub async fn delete_all_for_task(&self, task_id: &TaskId) -> Result<u64, RepositoryError> {
166 let task_id_str = task_id.as_str();
167 let result = sqlx::query!(
168 "DELETE FROM task_push_notification_configs WHERE task_id = $1",
169 task_id_str
170 )
171 .execute(&*self.write_pool)
172 .await
173 .map_err(RepositoryError::database)?;
174
175 Ok(result.rows_affected())
176 }
177
178 fn row_to_config(
179 row: &PushNotificationConfigRow,
180 ) -> Result<PushNotificationConfig, RepositoryError> {
181 let headers = row
182 .headers
183 .as_ref()
184 .map(|v| serde_json::from_value(v.clone()))
185 .transpose()?;
186 let authentication = row
187 .authentication
188 .as_ref()
189 .map(|v| serde_json::from_value(v.clone()))
190 .transpose()?;
191
192 Ok(PushNotificationConfig {
193 url: row.url.clone(),
194 endpoint: row.endpoint.clone(),
195 token: row.token.clone(),
196 headers,
197 authentication,
198 })
199 }
200}