use crate::ops::cloudscheduler::CloudschedulerOps;
use crate::types::cloudscheduler::*;
use crate::{GcpHttpClient, Result};
pub struct SchedulerClient<'a> {
ops: CloudschedulerOps<'a>,
}
impl<'a> SchedulerClient<'a> {
pub(crate) fn new(client: &'a GcpHttpClient) -> Self {
Self {
ops: CloudschedulerOps::new(client),
}
}
pub async fn create_job(&self, project: &str, location: &str, job: &Job) -> Result<Job> {
let parent = format!("projects/{}/locations/{}", project, location);
self.ops.create_job(&parent, job).await
}
pub async fn get_job(&self, project: &str, location: &str, job_id: &str) -> Result<Job> {
let name = format!(
"projects/{}/locations/{}/jobs/{}",
project, location, job_id
);
self.ops.get_job(&name).await
}
pub async fn list_jobs(&self, project: &str, location: &str) -> Result<Vec<Job>> {
let parent = format!("projects/{}/locations/{}", project, location);
let response = self.ops.list_jobs(&parent).await?;
Ok(response.jobs)
}
pub async fn update_job(
&self,
project: &str,
location: &str,
job_id: &str,
job: &Job,
update_mask: &str,
) -> Result<Job> {
let name = format!(
"projects/{}/locations/{}/jobs/{}",
project, location, job_id
);
self.ops.update_job(&name, update_mask, job).await
}
pub async fn delete_job(&self, project: &str, location: &str, job_id: &str) -> Result<()> {
let name = format!(
"projects/{}/locations/{}/jobs/{}",
project, location, job_id
);
self.ops.delete_job(&name).await?;
Ok(())
}
pub async fn pause_job(&self, project: &str, location: &str, job_id: &str) -> Result<Job> {
let name = format!(
"projects/{}/locations/{}/jobs/{}",
project, location, job_id
);
self.ops.pause_job(&name, &PauseJobRequest {}).await
}
pub async fn resume_job(&self, project: &str, location: &str, job_id: &str) -> Result<Job> {
let name = format!(
"projects/{}/locations/{}/jobs/{}",
project, location, job_id
);
self.ops.resume_job(&name, &ResumeJobRequest {}).await
}
pub async fn run_job(&self, project: &str, location: &str, job_id: &str) -> Result<Job> {
let name = format!(
"projects/{}/locations/{}/jobs/{}",
project, location, job_id
);
self.ops.run_job(&name, &RunJobRequest {}).await
}
}
#[cfg(test)]
mod tests {
use serde_json::json;
#[tokio::test]
async fn test_job_crud_lifecycle() {
let mut mock = crate::MockClient::new();
mock.expect_post("/v1/projects/test-project/locations/us-central1/jobs")
.returning_json(json!({
"name": "projects/test-project/locations/us-central1/jobs/test-job",
"description": "Test job",
"schedule": "0 9 * * 1",
"timeZone": "America/New_York"
}))
.times(1);
mock.expect_get("/v1/projects/test-project/locations/us-central1/jobs/test-job")
.returning_json(json!({
"name": "projects/test-project/locations/us-central1/jobs/test-job",
"description": "Test job",
"schedule": "0 9 * * 1"
}))
.times(1);
mock.expect_get("/v1/projects/test-project/locations/us-central1/jobs")
.returning_json(json!({
"jobs": [{
"name": "projects/test-project/locations/us-central1/jobs/test-job",
"description": "Test job"
}]
}))
.times(1);
mock.expect_delete("/v1/projects/test-project/locations/us-central1/jobs/test-job")
.returning_json(json!({}))
.times(1);
let client = crate::GcpHttpClient::from_mock(mock);
let scheduler = client.scheduler();
let job = crate::types::cloudscheduler::Job {
name: "projects/test-project/locations/us-central1/jobs/test-job".to_string(),
description: Some("Test job".to_string()),
schedule: Some("0 9 * * 1".to_string()),
time_zone: Some("America/New_York".to_string()),
..Default::default()
};
let created = scheduler
.create_job("test-project", "us-central1", &job)
.await
.unwrap();
assert_eq!(
created.name,
"projects/test-project/locations/us-central1/jobs/test-job"
);
assert_eq!(created.description, Some("Test job".to_string()));
let fetched = scheduler
.get_job("test-project", "us-central1", "test-job")
.await
.unwrap();
assert_eq!(fetched.schedule, Some("0 9 * * 1".to_string()));
let jobs = scheduler
.list_jobs("test-project", "us-central1")
.await
.unwrap();
assert_eq!(jobs.len(), 1);
assert_eq!(
jobs[0].name,
"projects/test-project/locations/us-central1/jobs/test-job"
);
scheduler
.delete_job("test-project", "us-central1", "test-job")
.await
.unwrap();
}
#[tokio::test]
async fn test_update_job_with_mask() {
let mut mock = crate::MockClient::new();
mock.expect_patch(
"/v1/projects/test-project/locations/us-central1/jobs/test-job?updateMask=description",
)
.returning_json(json!({
"name": "projects/test-project/locations/us-central1/jobs/test-job",
"description": "Updated description",
"schedule": "0 9 * * 1"
}))
.times(1);
let client = crate::GcpHttpClient::from_mock(mock);
let scheduler = client.scheduler();
let job = crate::types::cloudscheduler::Job {
name: "projects/test-project/locations/us-central1/jobs/test-job".to_string(),
description: Some("Updated description".to_string()),
..Default::default()
};
let updated = scheduler
.update_job(
"test-project",
"us-central1",
"test-job",
&job,
"description",
)
.await
.unwrap();
assert_eq!(updated.description, Some("Updated description".to_string()));
}
#[tokio::test]
async fn test_list_jobs_empty() {
let mut mock = crate::MockClient::new();
mock.expect_get("/v1/projects/test-project/locations/us-central1/jobs")
.returning_json(json!({ "jobs": [] }))
.times(1);
let client = crate::GcpHttpClient::from_mock(mock);
let jobs = client
.scheduler()
.list_jobs("test-project", "us-central1")
.await
.unwrap();
assert!(jobs.is_empty());
}
#[tokio::test]
async fn test_pause_resume_job() {
let mut mock = crate::MockClient::new();
mock.expect_post("/v1/projects/test-project/locations/us-central1/jobs/test-job:pause")
.returning_json(json!({
"name": "projects/test-project/locations/us-central1/jobs/test-job",
"state": "PAUSED"
}))
.times(1);
mock.expect_post("/v1/projects/test-project/locations/us-central1/jobs/test-job:resume")
.returning_json(json!({
"name": "projects/test-project/locations/us-central1/jobs/test-job",
"state": "ENABLED"
}))
.times(1);
let client = crate::GcpHttpClient::from_mock(mock);
let scheduler = client.scheduler();
let paused = scheduler
.pause_job("test-project", "us-central1", "test-job")
.await
.unwrap();
assert_eq!(
paused.state,
Some(crate::types::cloudscheduler::JobState::Paused)
);
let resumed = scheduler
.resume_job("test-project", "us-central1", "test-job")
.await
.unwrap();
assert_eq!(
resumed.state,
Some(crate::types::cloudscheduler::JobState::Enabled)
);
}
#[tokio::test]
async fn test_run_job() {
let mut mock = crate::MockClient::new();
mock.expect_post("/v1/projects/test-project/locations/us-central1/jobs/test-job:run")
.returning_json(json!({
"name": "projects/test-project/locations/us-central1/jobs/test-job",
"state": "ENABLED",
"lastAttemptTime": "2026-02-09T10:00:00Z"
}))
.times(1);
let client = crate::GcpHttpClient::from_mock(mock);
let result = client
.scheduler()
.run_job("test-project", "us-central1", "test-job")
.await
.unwrap();
assert_eq!(
result.last_attempt_time,
Some("2026-02-09T10:00:00Z".to_string())
);
}
}