use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use super::push_notification::TaskPushNotificationConfig;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct CreateTaskPushNotificationConfigRequest(pub TaskPushNotificationConfig);
impl From<TaskPushNotificationConfig> for CreateTaskPushNotificationConfigRequest {
fn from(config: TaskPushNotificationConfig) -> Self {
Self(config)
}
}
impl std::ops::Deref for CreateTaskPushNotificationConfigRequest {
type Target = TaskPushNotificationConfig;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct CreateTaskPushNotificationConfigResponse(pub TaskPushNotificationConfig);
impl From<TaskPushNotificationConfig> for CreateTaskPushNotificationConfigResponse {
fn from(config: TaskPushNotificationConfig) -> Self {
Self(config)
}
}
impl std::ops::Deref for CreateTaskPushNotificationConfigResponse {
type Target = TaskPushNotificationConfig;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetTaskPushNotificationConfigRequest {
pub id: String,
pub task_id: String,
}
impl GetTaskPushNotificationConfigRequest {
#[must_use]
pub fn new(id: impl Into<String>, task_id: impl Into<String>) -> Self {
Self {
id: id.into(),
task_id: task_id.into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct GetTaskPushNotificationConfigResponse(pub TaskPushNotificationConfig);
impl From<TaskPushNotificationConfig> for GetTaskPushNotificationConfigResponse {
fn from(config: TaskPushNotificationConfig) -> Self {
Self(config)
}
}
impl std::ops::Deref for GetTaskPushNotificationConfigResponse {
type Target = TaskPushNotificationConfig;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListTaskPushNotificationConfigsRequest {
pub task_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub page_size: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub page_token: Option<String>,
}
impl ListTaskPushNotificationConfigsRequest {
#[must_use]
pub fn new(task_id: impl Into<String>) -> Self {
Self {
task_id: task_id.into(),
page_size: None,
page_token: None,
}
}
#[must_use]
pub const fn with_page_size(mut self, page_size: u32) -> Self {
self.page_size = Some(page_size);
self
}
#[must_use]
pub fn with_page_token(mut self, page_token: impl Into<String>) -> Self {
self.page_token = Some(page_token.into());
self
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListTaskPushNotificationConfigsResponse {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub configs: Vec<TaskPushNotificationConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_page_token: Option<String>,
}
impl ListTaskPushNotificationConfigsResponse {
#[must_use]
pub fn new(configs: impl IntoIterator<Item = TaskPushNotificationConfig>) -> Self {
Self {
configs: configs.into_iter().collect(),
next_page_token: None,
}
}
#[must_use]
pub fn with_next_page_token(mut self, next_page_token: impl Into<String>) -> Self {
self.next_page_token = Some(next_page_token.into());
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DeleteTaskPushNotificationConfigRequest {
pub id: String,
pub task_id: String,
}
impl DeleteTaskPushNotificationConfigRequest {
#[must_use]
pub fn new(id: impl Into<String>, task_id: impl Into<String>) -> Self {
Self {
id: id.into(),
task_id: task_id.into(),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct DeleteTaskPushNotificationConfigResponse {
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub metadata: BTreeMap<String, serde_json::Value>,
}
#[cfg(test)]
mod tests {
use super::{
CreateTaskPushNotificationConfigRequest, CreateTaskPushNotificationConfigResponse,
DeleteTaskPushNotificationConfigRequest, DeleteTaskPushNotificationConfigResponse,
GetTaskPushNotificationConfigRequest, GetTaskPushNotificationConfigResponse,
ListTaskPushNotificationConfigsRequest, ListTaskPushNotificationConfigsResponse,
};
use crate::domain::a2a::push_notification::TaskPushNotificationConfig;
type TestError = Box<dyn std::error::Error>;
fn sample_config() -> TaskPushNotificationConfig {
TaskPushNotificationConfig::new("c-1", "t-1", "https://example.com/wh")
}
#[test]
fn create_request_serialises_as_config_object() -> Result<(), TestError> {
let request = CreateTaskPushNotificationConfigRequest::from(sample_config());
let value = serde_json::to_value(&request)?;
let obj = value
.as_object()
.ok_or("a CreateTaskPushNotificationConfigRequest must serialise to a JSON object")?;
assert_eq!(
obj.get("id").and_then(serde_json::Value::as_str),
Some("c-1")
);
assert_eq!(
obj.get("taskId").and_then(serde_json::Value::as_str),
Some("t-1")
);
assert!(!obj.contains_key("config"));
Ok(())
}
#[test]
fn create_response_from_config_via_into_works() {
let config = sample_config();
let response: CreateTaskPushNotificationConfigResponse = config.clone().into();
assert_eq!(response.0, config);
}
#[test]
fn get_request_serialises_with_id_and_task_id() -> Result<(), TestError> {
let request = GetTaskPushNotificationConfigRequest::new("c-1", "t-1");
let value = serde_json::to_value(&request)?;
let obj = value
.as_object()
.ok_or("a GetTaskPushNotificationConfigRequest must serialise to a JSON object")?;
assert_eq!(
obj.get("id").and_then(serde_json::Value::as_str),
Some("c-1")
);
assert_eq!(
obj.get("taskId").and_then(serde_json::Value::as_str),
Some("t-1")
);
Ok(())
}
#[test]
fn get_response_serialises_as_config_object() -> Result<(), TestError> {
let response = GetTaskPushNotificationConfigResponse::from(sample_config());
let value = serde_json::to_value(&response)?;
let obj = value
.as_object()
.ok_or("a GetTaskPushNotificationConfigResponse must serialise to a JSON object")?;
assert_eq!(
obj.get("id").and_then(serde_json::Value::as_str),
Some("c-1")
);
Ok(())
}
#[test]
fn list_request_serialises_with_task_id_only() -> Result<(), TestError> {
let request = ListTaskPushNotificationConfigsRequest::new("t-1");
let value = serde_json::to_value(&request)?;
let obj = value
.as_object()
.ok_or("a ListTaskPushNotificationConfigsRequest must serialise to a JSON object")?;
assert_eq!(
obj.get("taskId").and_then(serde_json::Value::as_str),
Some("t-1")
);
assert!(!obj.contains_key("pageSize"));
assert!(!obj.contains_key("pageToken"));
Ok(())
}
#[test]
fn list_request_round_trips_with_pagination() -> Result<(), TestError> {
let request = ListTaskPushNotificationConfigsRequest::new("t-1")
.with_page_size(50)
.with_page_token("next-page");
let json = serde_json::to_string(&request)?;
let parsed: ListTaskPushNotificationConfigsRequest = serde_json::from_str(&json)?;
assert_eq!(parsed, request);
Ok(())
}
#[test]
fn list_response_with_no_next_page_token_omits_the_field() -> Result<(), TestError> {
let response = ListTaskPushNotificationConfigsResponse::new([sample_config()]);
let value = serde_json::to_value(&response)?;
let obj = value
.as_object()
.ok_or("a ListTaskPushNotificationConfigsResponse must serialise to a JSON object")?;
assert!(!obj.contains_key("nextPageToken"));
let configs = obj
.get("configs")
.and_then(serde_json::Value::as_array)
.ok_or("`configs` must be a JSON array")?;
assert_eq!(configs.len(), 1);
Ok(())
}
#[test]
fn list_response_round_trips_with_token_and_configs() -> Result<(), TestError> {
let response = ListTaskPushNotificationConfigsResponse::new([
sample_config(),
TaskPushNotificationConfig::new("c-2", "t-1", "https://example.com/wh2"),
])
.with_next_page_token("next");
let json = serde_json::to_string(&response)?;
let parsed: ListTaskPushNotificationConfigsResponse = serde_json::from_str(&json)?;
assert_eq!(parsed, response);
Ok(())
}
#[test]
fn delete_request_serialises_with_id_and_task_id() -> Result<(), TestError> {
let request = DeleteTaskPushNotificationConfigRequest::new("c-1", "t-1");
let value = serde_json::to_value(&request)?;
let obj = value
.as_object()
.ok_or("a DeleteTaskPushNotificationConfigRequest must serialise to a JSON object")?;
assert_eq!(
obj.get("id").and_then(serde_json::Value::as_str),
Some("c-1")
);
assert_eq!(
obj.get("taskId").and_then(serde_json::Value::as_str),
Some("t-1")
);
Ok(())
}
#[test]
fn delete_response_with_no_metadata_is_an_empty_object() -> Result<(), TestError> {
let response = DeleteTaskPushNotificationConfigResponse::default();
let value = serde_json::to_value(&response)?;
let obj = value
.as_object()
.ok_or("a DeleteTaskPushNotificationConfigResponse must serialise to a JSON object")?;
assert!(obj.is_empty(), "expected `{{}}`, got {value}");
Ok(())
}
}