crate::create_kind!(TaskKind, "tasks");
pub type TaskEntity = super::Entity<u64, TaskKind>;
#[cfg_attr(feature = "facet", derive(facet::Facet))]
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TaskDocument {
pub id: u64,
#[serde(rename = "type")]
pub kind: TaskKind,
pub attributes: TaskAttributes,
}
#[cfg_attr(feature = "facet", derive(facet::Facet))]
#[cfg_attr(feature = "facet", repr(C))]
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TaskAttributes {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub after: Option<chrono::DateTime<chrono::Utc>>,
pub status: TaskStatus,
#[serde(flatten)]
pub payload: TaskPayload,
pub retries: u8,
pub max_retries: u8,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
}
#[cfg_attr(feature = "facet", derive(facet::Facet))]
#[cfg_attr(feature = "facet", repr(C))]
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum TaskStatus {
Pending,
Running,
Completed,
Failed { message: Option<String> },
}
#[cfg_attr(feature = "facet", derive(facet::Facet))]
#[cfg_attr(feature = "facet", repr(C))]
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "targetKind", rename_all = "camelCase")]
pub enum TaskPayload {
Podcast(super::podcast_task::PodcastTask),
Noop,
}
#[cfg(test)]
mod tests {
use chrono::DateTime;
use crate::entity::task::{TaskAttributes, TaskDocument};
#[test]
fn should_serialize_task_document_pending_synchronize() {
let task = TaskDocument {
id: 1,
kind: Default::default(),
attributes: TaskAttributes {
after: None,
status: super::TaskStatus::Pending,
payload: super::TaskPayload::Podcast(
crate::entity::podcast_task::PodcastTask::Synchronize { podcast_id: 42 },
),
retries: 0,
max_retries: 10,
created_at: DateTime::from_timestamp(0, 0).unwrap(),
updated_at: DateTime::from_timestamp(0, 0).unwrap(),
},
};
let output = serde_json::to_string(&task).unwrap();
assert_eq!(
output,
r#"{"id":1,"type":"tasks","attributes":{"status":{"type":"pending"},"targetKind":"podcast","method":"synchronize","targetId":42,"retries":0,"maxRetries":10,"createdAt":"1970-01-01T00:00:00Z","updatedAt":"1970-01-01T00:00:00Z"}}"#
);
}
#[test]
fn should_serialize_task_document_failed_synchronize() {
let task = TaskDocument {
id: 1,
kind: Default::default(),
attributes: TaskAttributes {
after: None,
status: super::TaskStatus::Failed {
message: Some("Oops".to_string()),
},
payload: super::TaskPayload::Podcast(
crate::entity::podcast_task::PodcastTask::SynchronizeAll,
),
retries: 0,
max_retries: 10,
created_at: DateTime::from_timestamp(0, 0).unwrap(),
updated_at: DateTime::from_timestamp(0, 0).unwrap(),
},
};
let output = serde_json::to_string(&task).unwrap();
assert_eq!(
output,
r#"{"id":1,"type":"tasks","attributes":{"status":{"type":"failed","message":"Oops"},"targetKind":"podcast","method":"synchronizeAll","retries":0,"maxRetries":10,"createdAt":"1970-01-01T00:00:00Z","updatedAt":"1970-01-01T00:00:00Z"}}"#
);
}
#[test]
fn should_serialize_task_document_running_noop() {
let task = TaskDocument {
id: 1,
kind: Default::default(),
attributes: TaskAttributes {
after: None,
status: super::TaskStatus::Running,
payload: super::TaskPayload::Noop,
retries: 0,
max_retries: 10,
created_at: DateTime::from_timestamp(0, 0).unwrap(),
updated_at: DateTime::from_timestamp(0, 0).unwrap(),
},
};
let output = serde_json::to_string(&task).unwrap();
assert_eq!(
output,
r#"{"id":1,"type":"tasks","attributes":{"status":{"type":"running"},"targetKind":"noop","retries":0,"maxRetries":10,"createdAt":"1970-01-01T00:00:00Z","updatedAt":"1970-01-01T00:00:00Z"}}"#
);
}
}