#[cfg(doc)]
use crate::Client;
use crate::proto::{BatchId, Job};
use derive_builder::Builder;
mod cmd;
mod handle;
mod status;
pub use handle::BatchHandle;
pub use status::{BatchStatus, CallbackState};
pub(crate) use cmd::{CommitBatch, GetBatchStatus, OpenBatch};
#[derive(Builder, Default, Debug, Serialize)]
#[builder(
custom_constructor,
pattern = "owned",
setter(into),
build_fn(name = "try_build", private)
)]
pub struct Batch {
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(setter(skip))]
parent_bid: Option<BatchId>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(setter(custom), default = "None")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(setter(skip))]
pub(crate) success: Option<Job>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(setter(skip))]
pub(crate) complete: Option<Job>,
}
impl Default for BatchBuilder {
fn default() -> Self {
Self::new()
}
}
impl Batch {
pub fn builder() -> BatchBuilder {
BatchBuilder::new()
}
}
impl BatchBuilder {
fn build(self) -> Batch {
self.try_build().expect("There are no required fields.")
}
pub fn new() -> BatchBuilder {
Self::create_empty()
}
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(Some(description.into()));
self
}
pub fn with_success_callback(self, success_cb: Job) -> Batch {
let mut b = self.build();
b.success = Some(success_cb);
b
}
pub fn with_complete_callback(self, complete_cb: Job) -> Batch {
let mut b = self.build();
b.complete = Some(complete_cb);
b
}
pub fn with_callbacks(self, success_cb: Job, complete_cb: Job) -> Batch {
let mut b = self.build();
b.success = Some(success_cb);
b.complete = Some(complete_cb);
b
}
}
impl Clone for BatchBuilder {
fn clone(&self) -> Self {
BatchBuilder {
parent_bid: self.parent_bid,
description: self.description.clone(),
success: self.success,
complete: self.complete,
}
}
}
#[cfg(test)]
mod test {
use std::str::FromStr;
use chrono::{DateTime, Utc};
use crate::JobId;
use super::*;
#[test]
fn test_batch_creation() {
let b = BatchBuilder::new()
.description("Image processing batch")
.with_success_callback(Job::builder("thumbnail").build());
assert!(b.complete.is_none());
assert!(b.parent_bid.is_none());
assert!(b.success.is_some());
assert_eq!(b.description, Some("Image processing batch".into()));
let b = BatchBuilder::new()
.description("Image processing batch")
.with_complete_callback(Job::builder("thumbnail").build());
assert!(b.complete.is_some());
assert!(b.success.is_none());
let b = BatchBuilder::new().with_callbacks(
Job::builder("thumbnail").build(),
Job::builder("thumbnail").build(),
);
assert!(b.description.is_none());
assert!(b.complete.is_some());
assert!(b.success.is_some());
let b = BatchBuilder::new().description("Batch description");
let _batch_with_complete_cb = b.clone().with_complete_callback(Job::builder("jt").build());
let _batch_with_success_cb = b.with_success_callback(Job::builder("jt").build());
}
#[test]
fn test_batch_serialized_correctly() {
let prepare_test_job = |jobtype: String| {
let jid = JobId::new("LFluKy1Baak83p54");
let dt = "2023-12-22T07:00:52.546258624Z";
let created_at = DateTime::<Utc>::from_str(dt).unwrap();
Job::builder(jobtype)
.jid(jid)
.created_at(created_at)
.build()
};
let got = serde_json::to_string(
&BatchBuilder::new()
.description("Image processing workload")
.with_success_callback(prepare_test_job("thumbnail_clean_up".into())),
)
.unwrap();
let expected = if cfg!(feature = "ent") {
r#"{"description":"Image processing workload","success":{"jid":"LFluKy1Baak83p54","queue":"default","jobtype":"thumbnail_clean_up","args":[],"created_at":"2023-12-22T07:00:52.546258624Z","reserve_for":600,"retry":25,"priority":5,"backtrace":0,"custom":{"track":1}}}"#
} else {
r#"{"description":"Image processing workload","success":{"jid":"LFluKy1Baak83p54","queue":"default","jobtype":"thumbnail_clean_up","args":[],"created_at":"2023-12-22T07:00:52.546258624Z","reserve_for":600,"retry":25,"priority":5,"backtrace":0}}"#
};
assert_eq!(got, expected);
let got = serde_json::to_string(
&BatchBuilder::new().with_complete_callback(prepare_test_job("thumbnail_info".into())),
)
.unwrap();
let expected = if cfg!(feature = "ent") {
r#"{"complete":{"jid":"LFluKy1Baak83p54","queue":"default","jobtype":"thumbnail_info","args":[],"created_at":"2023-12-22T07:00:52.546258624Z","reserve_for":600,"retry":25,"priority":5,"backtrace":0,"custom":{"track":1}}}"#
} else {
r#"{"complete":{"jid":"LFluKy1Baak83p54","queue":"default","jobtype":"thumbnail_info","args":[],"created_at":"2023-12-22T07:00:52.546258624Z","reserve_for":600,"retry":25,"priority":5,"backtrace":0}}"#
};
assert_eq!(got, expected);
let got = serde_json::to_string(
&BatchBuilder::new()
.description("Image processing workload")
.with_callbacks(
prepare_test_job("thumbnail_clean_up".into()),
prepare_test_job("thumbnail_info".into()),
),
)
.unwrap();
let expected = if cfg!(feature = "ent") {
r#"{"description":"Image processing workload","success":{"jid":"LFluKy1Baak83p54","queue":"default","jobtype":"thumbnail_clean_up","args":[],"created_at":"2023-12-22T07:00:52.546258624Z","reserve_for":600,"retry":25,"priority":5,"backtrace":0,"custom":{"track":1}},"complete":{"jid":"LFluKy1Baak83p54","queue":"default","jobtype":"thumbnail_info","args":[],"created_at":"2023-12-22T07:00:52.546258624Z","reserve_for":600,"retry":25,"priority":5,"backtrace":0,"custom":{"track":1}}}"#
} else {
r#"{"description":"Image processing workload","success":{"jid":"LFluKy1Baak83p54","queue":"default","jobtype":"thumbnail_clean_up","args":[],"created_at":"2023-12-22T07:00:52.546258624Z","reserve_for":600,"retry":25,"priority":5,"backtrace":0},"complete":{"jid":"LFluKy1Baak83p54","queue":"default","jobtype":"thumbnail_info","args":[],"created_at":"2023-12-22T07:00:52.546258624Z","reserve_for":600,"retry":25,"priority":5,"backtrace":0}}"#
};
assert_eq!(got, expected);
}
}