use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TaskPushNotificationConfig {
pub id: String,
pub task_id: String,
pub url: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub token: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub authentication: Option<PushNotificationAuthenticationInfo>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub metadata: BTreeMap<String, serde_json::Value>,
}
impl TaskPushNotificationConfig {
#[must_use]
pub fn new(id: impl Into<String>, task_id: impl Into<String>, url: impl Into<String>) -> Self {
Self {
id: id.into(),
task_id: task_id.into(),
url: url.into(),
token: None,
authentication: None,
metadata: BTreeMap::new(),
}
}
#[must_use]
pub fn with_token(mut self, token: impl Into<String>) -> Self {
self.token = Some(token.into());
self
}
#[must_use]
pub fn with_authentication(
mut self,
authentication: PushNotificationAuthenticationInfo,
) -> Self {
self.authentication = Some(authentication);
self
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct PushNotificationAuthenticationInfo {
pub scheme: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub credentials: Option<String>,
}
impl PushNotificationAuthenticationInfo {
#[must_use]
pub fn new(scheme: impl Into<String>) -> Self {
Self {
scheme: scheme.into(),
credentials: None,
}
}
#[must_use]
pub fn with_credentials(mut self, credentials: impl Into<String>) -> Self {
self.credentials = Some(credentials.into());
self
}
}
#[cfg(test)]
mod tests {
use super::{PushNotificationAuthenticationInfo, TaskPushNotificationConfig};
type TestError = Box<dyn std::error::Error>;
#[test]
fn config_serialises_with_required_fields_only() -> Result<(), TestError> {
let config = TaskPushNotificationConfig::new("c-1", "t-1", "https://example.com/wh");
let value = serde_json::to_value(&config)?;
let obj = value
.as_object()
.ok_or("a TaskPushNotificationConfig 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_eq!(
obj.get("url").and_then(serde_json::Value::as_str),
Some("https://example.com/wh")
);
assert!(!obj.contains_key("token"));
assert!(!obj.contains_key("authentication"));
assert!(!obj.contains_key("metadata"));
Ok(())
}
#[test]
fn config_round_trips_with_token_and_authentication() -> Result<(), TestError> {
let config = TaskPushNotificationConfig::new("c-1", "t-1", "https://example.com/wh")
.with_token("auth-token")
.with_authentication(
PushNotificationAuthenticationInfo::new("Bearer").with_credentials("xyz"),
);
let json = serde_json::to_string(&config)?;
let parsed: TaskPushNotificationConfig = serde_json::from_str(&json)?;
assert_eq!(parsed, config);
Ok(())
}
#[test]
fn auth_info_serialises_with_scheme_only() -> Result<(), TestError> {
let info = PushNotificationAuthenticationInfo::new("Bearer");
let value = serde_json::to_value(&info)?;
let obj = value
.as_object()
.ok_or("a PushNotificationAuthenticationInfo must serialise to a JSON object")?;
assert_eq!(
obj.get("scheme").and_then(serde_json::Value::as_str),
Some("Bearer")
);
assert!(!obj.contains_key("credentials"));
Ok(())
}
#[test]
fn auth_info_round_trips_with_credentials() -> Result<(), TestError> {
let info =
PushNotificationAuthenticationInfo::new("Basic").with_credentials("dXNlcjpwYXNz");
let json = serde_json::to_string(&info)?;
let parsed: PushNotificationAuthenticationInfo = serde_json::from_str(&json)?;
assert_eq!(parsed, info);
Ok(())
}
}