protocol_droid/domain/a2a/
push_notification_methods.rs1use std::collections::BTreeMap;
63
64use serde::{Deserialize, Serialize};
65
66use super::push_notification::TaskPushNotificationConfig;
67
68#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
75#[serde(transparent)]
76pub struct CreateTaskPushNotificationConfigRequest(pub TaskPushNotificationConfig);
77
78impl From<TaskPushNotificationConfig> for CreateTaskPushNotificationConfigRequest {
79 fn from(config: TaskPushNotificationConfig) -> Self {
80 Self(config)
81 }
82}
83
84impl std::ops::Deref for CreateTaskPushNotificationConfigRequest {
85 type Target = TaskPushNotificationConfig;
86
87 fn deref(&self) -> &Self::Target {
88 &self.0
89 }
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
97#[serde(transparent)]
98pub struct CreateTaskPushNotificationConfigResponse(pub TaskPushNotificationConfig);
99
100impl From<TaskPushNotificationConfig> for CreateTaskPushNotificationConfigResponse {
101 fn from(config: TaskPushNotificationConfig) -> Self {
102 Self(config)
103 }
104}
105
106impl std::ops::Deref for CreateTaskPushNotificationConfigResponse {
107 type Target = TaskPushNotificationConfig;
108
109 fn deref(&self) -> &Self::Target {
110 &self.0
111 }
112}
113
114#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
124#[serde(rename_all = "camelCase")]
125pub struct GetTaskPushNotificationConfigRequest {
126 pub id: String,
128 pub task_id: String,
130}
131
132impl GetTaskPushNotificationConfigRequest {
133 #[must_use]
136 pub fn new(id: impl Into<String>, task_id: impl Into<String>) -> Self {
137 Self {
138 id: id.into(),
139 task_id: task_id.into(),
140 }
141 }
142}
143
144#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
149#[serde(transparent)]
150pub struct GetTaskPushNotificationConfigResponse(pub TaskPushNotificationConfig);
151
152impl From<TaskPushNotificationConfig> for GetTaskPushNotificationConfigResponse {
153 fn from(config: TaskPushNotificationConfig) -> Self {
154 Self(config)
155 }
156}
157
158impl std::ops::Deref for GetTaskPushNotificationConfigResponse {
159 type Target = TaskPushNotificationConfig;
160
161 fn deref(&self) -> &Self::Target {
162 &self.0
163 }
164}
165
166#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
181#[serde(rename_all = "camelCase")]
182pub struct ListTaskPushNotificationConfigsRequest {
183 pub task_id: String,
185 #[serde(default, skip_serializing_if = "Option::is_none")]
189 pub page_size: Option<u32>,
190 #[serde(default, skip_serializing_if = "Option::is_none")]
196 pub page_token: Option<String>,
197}
198
199impl ListTaskPushNotificationConfigsRequest {
200 #[must_use]
206 pub fn new(task_id: impl Into<String>) -> Self {
207 Self {
208 task_id: task_id.into(),
209 page_size: None,
210 page_token: None,
211 }
212 }
213
214 #[must_use]
218 pub const fn with_page_size(mut self, page_size: u32) -> Self {
219 self.page_size = Some(page_size);
220 self
221 }
222
223 #[must_use]
228 pub fn with_page_token(mut self, page_token: impl Into<String>) -> Self {
229 self.page_token = Some(page_token.into());
230 self
231 }
232}
233
234#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
243#[serde(rename_all = "camelCase")]
244pub struct ListTaskPushNotificationConfigsResponse {
245 #[serde(default, skip_serializing_if = "Vec::is_empty")]
248 pub configs: Vec<TaskPushNotificationConfig>,
249 #[serde(default, skip_serializing_if = "Option::is_none")]
255 pub next_page_token: Option<String>,
256}
257
258impl ListTaskPushNotificationConfigsResponse {
259 #[must_use]
264 pub fn new(configs: impl IntoIterator<Item = TaskPushNotificationConfig>) -> Self {
265 Self {
266 configs: configs.into_iter().collect(),
267 next_page_token: None,
268 }
269 }
270
271 #[must_use]
273 pub fn with_next_page_token(mut self, next_page_token: impl Into<String>) -> Self {
274 self.next_page_token = Some(next_page_token.into());
275 self
276 }
277}
278
279#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
289#[serde(rename_all = "camelCase")]
290pub struct DeleteTaskPushNotificationConfigRequest {
291 pub id: String,
293 pub task_id: String,
295}
296
297impl DeleteTaskPushNotificationConfigRequest {
298 #[must_use]
301 pub fn new(id: impl Into<String>, task_id: impl Into<String>) -> Self {
302 Self {
303 id: id.into(),
304 task_id: task_id.into(),
305 }
306 }
307}
308
309#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
313pub struct DeleteTaskPushNotificationConfigResponse {
314 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
321 pub metadata: BTreeMap<String, serde_json::Value>,
322}
323
324#[cfg(test)]
325mod tests {
326 use super::{
327 CreateTaskPushNotificationConfigRequest, CreateTaskPushNotificationConfigResponse,
328 DeleteTaskPushNotificationConfigRequest, DeleteTaskPushNotificationConfigResponse,
329 GetTaskPushNotificationConfigRequest, GetTaskPushNotificationConfigResponse,
330 ListTaskPushNotificationConfigsRequest, ListTaskPushNotificationConfigsResponse,
331 };
332 use crate::domain::a2a::push_notification::TaskPushNotificationConfig;
333
334 type TestError = Box<dyn std::error::Error>;
335
336 fn sample_config() -> TaskPushNotificationConfig {
337 TaskPushNotificationConfig::new("c-1", "t-1", "https://example.com/wh")
338 }
339
340 #[test]
343 fn create_request_serialises_as_config_object() -> Result<(), TestError> {
344 let request = CreateTaskPushNotificationConfigRequest::from(sample_config());
345 let value = serde_json::to_value(&request)?;
346 let obj = value
347 .as_object()
348 .ok_or("a CreateTaskPushNotificationConfigRequest must serialise to a JSON object")?;
349 assert_eq!(
350 obj.get("id").and_then(serde_json::Value::as_str),
351 Some("c-1")
352 );
353 assert_eq!(
354 obj.get("taskId").and_then(serde_json::Value::as_str),
355 Some("t-1")
356 );
357 assert!(!obj.contains_key("config"));
360 Ok(())
361 }
362
363 #[test]
364 fn create_response_from_config_via_into_works() {
365 let config = sample_config();
366 let response: CreateTaskPushNotificationConfigResponse = config.clone().into();
367 assert_eq!(response.0, config);
368 }
369
370 #[test]
373 fn get_request_serialises_with_id_and_task_id() -> Result<(), TestError> {
374 let request = GetTaskPushNotificationConfigRequest::new("c-1", "t-1");
375 let value = serde_json::to_value(&request)?;
376 let obj = value
377 .as_object()
378 .ok_or("a GetTaskPushNotificationConfigRequest must serialise to a JSON object")?;
379 assert_eq!(
380 obj.get("id").and_then(serde_json::Value::as_str),
381 Some("c-1")
382 );
383 assert_eq!(
384 obj.get("taskId").and_then(serde_json::Value::as_str),
385 Some("t-1")
386 );
387 Ok(())
388 }
389
390 #[test]
391 fn get_response_serialises_as_config_object() -> Result<(), TestError> {
392 let response = GetTaskPushNotificationConfigResponse::from(sample_config());
393 let value = serde_json::to_value(&response)?;
394 let obj = value
395 .as_object()
396 .ok_or("a GetTaskPushNotificationConfigResponse must serialise to a JSON object")?;
397 assert_eq!(
398 obj.get("id").and_then(serde_json::Value::as_str),
399 Some("c-1")
400 );
401 Ok(())
402 }
403
404 #[test]
407 fn list_request_serialises_with_task_id_only() -> Result<(), TestError> {
408 let request = ListTaskPushNotificationConfigsRequest::new("t-1");
409 let value = serde_json::to_value(&request)?;
410 let obj = value
411 .as_object()
412 .ok_or("a ListTaskPushNotificationConfigsRequest must serialise to a JSON object")?;
413 assert_eq!(
414 obj.get("taskId").and_then(serde_json::Value::as_str),
415 Some("t-1")
416 );
417 assert!(!obj.contains_key("pageSize"));
418 assert!(!obj.contains_key("pageToken"));
419 Ok(())
420 }
421
422 #[test]
423 fn list_request_round_trips_with_pagination() -> Result<(), TestError> {
424 let request = ListTaskPushNotificationConfigsRequest::new("t-1")
425 .with_page_size(50)
426 .with_page_token("next-page");
427 let json = serde_json::to_string(&request)?;
428 let parsed: ListTaskPushNotificationConfigsRequest = serde_json::from_str(&json)?;
429 assert_eq!(parsed, request);
430 Ok(())
431 }
432
433 #[test]
434 fn list_response_with_no_next_page_token_omits_the_field() -> Result<(), TestError> {
435 let response = ListTaskPushNotificationConfigsResponse::new([sample_config()]);
439 let value = serde_json::to_value(&response)?;
440 let obj = value
441 .as_object()
442 .ok_or("a ListTaskPushNotificationConfigsResponse must serialise to a JSON object")?;
443 assert!(!obj.contains_key("nextPageToken"));
444 let configs = obj
445 .get("configs")
446 .and_then(serde_json::Value::as_array)
447 .ok_or("`configs` must be a JSON array")?;
448 assert_eq!(configs.len(), 1);
449 Ok(())
450 }
451
452 #[test]
453 fn list_response_round_trips_with_token_and_configs() -> Result<(), TestError> {
454 let response = ListTaskPushNotificationConfigsResponse::new([
455 sample_config(),
456 TaskPushNotificationConfig::new("c-2", "t-1", "https://example.com/wh2"),
457 ])
458 .with_next_page_token("next");
459 let json = serde_json::to_string(&response)?;
460 let parsed: ListTaskPushNotificationConfigsResponse = serde_json::from_str(&json)?;
461 assert_eq!(parsed, response);
462 Ok(())
463 }
464
465 #[test]
468 fn delete_request_serialises_with_id_and_task_id() -> Result<(), TestError> {
469 let request = DeleteTaskPushNotificationConfigRequest::new("c-1", "t-1");
470 let value = serde_json::to_value(&request)?;
471 let obj = value
472 .as_object()
473 .ok_or("a DeleteTaskPushNotificationConfigRequest must serialise to a JSON object")?;
474 assert_eq!(
475 obj.get("id").and_then(serde_json::Value::as_str),
476 Some("c-1")
477 );
478 assert_eq!(
479 obj.get("taskId").and_then(serde_json::Value::as_str),
480 Some("t-1")
481 );
482 Ok(())
483 }
484
485 #[test]
486 fn delete_response_with_no_metadata_is_an_empty_object() -> Result<(), TestError> {
487 let response = DeleteTaskPushNotificationConfigResponse::default();
494 let value = serde_json::to_value(&response)?;
495 let obj = value
496 .as_object()
497 .ok_or("a DeleteTaskPushNotificationConfigResponse must serialise to a JSON object")?;
498 assert!(obj.is_empty(), "expected `{{}}`, got {value}");
499 Ok(())
500 }
501}