diff --git a/.sqlx/query-18495d6198079fdb8e4806d8a59aa0a1abee44a8b568ce74fa275ab936e8362f.json b/.sqlx/query-18495d6198079fdb8e4806d8a59aa0a1abee44a8b568ce74fa275ab936e8362f.json new file mode 100644 index 0000000..1125f1f --- /dev/null +++ b/.sqlx/query-18495d6198079fdb8e4806d8a59aa0a1abee44a8b568ce74fa275ab936e8362f.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE\n survey_mcaptcha_upload_jobs\n SET\n job_state = (SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $1),\n scheduled_at = $2\n WHERE public_id = $3;", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Timestamptz", + "Text" + ] + }, + "nullable": [] + }, + "hash": "18495d6198079fdb8e4806d8a59aa0a1abee44a8b568ce74fa275ab936e8362f" +} diff --git a/.sqlx/query-1e41c42d89762ff4dc4b60a534a54db2741b325727c01852cbc68ea8442d15ef.json b/.sqlx/query-1e41c42d89762ff4dc4b60a534a54db2741b325727c01852cbc68ea8442d15ef.json new file mode 100644 index 0000000..a285802 --- /dev/null +++ b/.sqlx/query-1e41c42d89762ff4dc4b60a534a54db2741b325727c01852cbc68ea8442d15ef.json @@ -0,0 +1,64 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n survey_mcaptcha_upload_jobs.ID,\n survey_mcaptcha_upload_jobs.public_id,\n survey_mcaptcha_campaign.campaign_id,\n survey_mcaptcha_campaign.public_id as campaign_public_id,\n survey_mcaptcha_upload_job_states.name,\n survey_mcaptcha_upload_jobs.created_at,\n survey_mcaptcha_upload_jobs.scheduled_at,\n survey_mcaptcha_upload_jobs.finished_at\n\n FROM survey_mcaptcha_upload_jobs\n INNER JOIN\n survey_mcaptcha_upload_job_states\n ON\n survey_mcaptcha_upload_job_states.ID = survey_mcaptcha_upload_jobs.job_state\n INNER JOIN\n survey_mcaptcha_campaign\n ON\n survey_mcaptcha_campaign.ID = survey_mcaptcha_upload_jobs.campaign_id\n WHERE\n survey_mcaptcha_upload_job_states.name = $1;", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "public_id", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "campaign_id", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "campaign_public_id", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 6, + "name": "scheduled_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 7, + "name": "finished_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + true, + true + ] + }, + "hash": "1e41c42d89762ff4dc4b60a534a54db2741b325727c01852cbc68ea8442d15ef" +} diff --git a/.sqlx/query-4237df28c12e17ca68a1e1b33ae80ce5a4a8dff6d0795f277fb18b7b40dc69ef.json b/.sqlx/query-4237df28c12e17ca68a1e1b33ae80ce5a4a8dff6d0795f277fb18b7b40dc69ef.json deleted file mode 100644 index cf0ca95..0000000 --- a/.sqlx/query-4237df28c12e17ca68a1e1b33ae80ce5a4a8dff6d0795f277fb18b7b40dc69ef.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n campaign_id\n FROM\n survey_mcaptcha_campaign\n WHERE ID = (\n SELECT\n campaign_id\n FROM\n survey_mcaptcha_upload_jobs\n WHERE\n job_state = (SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $1)\n AND\n finished_at is NULL\n AND\n scheduled_at is NULL\n ORDER BY created_at ASC\n );", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "campaign_id", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "4237df28c12e17ca68a1e1b33ae80ce5a4a8dff6d0795f277fb18b7b40dc69ef" -} diff --git a/.sqlx/query-722f2d297a318f9804c1388d427d069a315b45c0c85c0b344d34cd8928b22c9c.json b/.sqlx/query-722f2d297a318f9804c1388d427d069a315b45c0c85c0b344d34cd8928b22c9c.json new file mode 100644 index 0000000..b7a9262 --- /dev/null +++ b/.sqlx/query-722f2d297a318f9804c1388d427d069a315b45c0c85c0b344d34cd8928b22c9c.json @@ -0,0 +1,64 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n survey_mcaptcha_upload_jobs.ID,\n survey_mcaptcha_upload_jobs.public_id,\n survey_mcaptcha_campaign.campaign_id,\n survey_mcaptcha_campaign.public_id as campaign_public_id,\n survey_mcaptcha_upload_job_states.name,\n survey_mcaptcha_upload_jobs.created_at,\n survey_mcaptcha_upload_jobs.scheduled_at,\n survey_mcaptcha_upload_jobs.finished_at\n\n FROM survey_mcaptcha_upload_jobs\n INNER JOIN\n survey_mcaptcha_upload_job_states\n ON\n survey_mcaptcha_upload_job_states.ID = survey_mcaptcha_upload_jobs.job_state\n INNER JOIN\n survey_mcaptcha_campaign\n ON\n survey_mcaptcha_campaign.ID = survey_mcaptcha_upload_jobs.campaign_id\n WHERE\n survey_mcaptcha_upload_jobs.public_id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "public_id", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "campaign_id", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "campaign_public_id", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 6, + "name": "scheduled_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 7, + "name": "finished_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + true, + true + ] + }, + "hash": "722f2d297a318f9804c1388d427d069a315b45c0c85c0b344d34cd8928b22c9c" +} diff --git a/.sqlx/query-8be51483900058d0bcc4c121440fe551bc57096826119ad914cc1ef2d9ccb79d.json b/.sqlx/query-8be51483900058d0bcc4c121440fe551bc57096826119ad914cc1ef2d9ccb79d.json new file mode 100644 index 0000000..3987ac3 --- /dev/null +++ b/.sqlx/query-8be51483900058d0bcc4c121440fe551bc57096826119ad914cc1ef2d9ccb79d.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n survey_mcaptcha_campaign.campaign_id,\n survey_mcaptcha_upload_jobs.public_id\n FROM\n survey_mcaptcha_campaign\n INNER JOIN\n survey_mcaptcha_upload_jobs\n ON\n survey_mcaptcha_upload_jobs.campaign_id = survey_mcaptcha_campaign.ID\n WHERE\n survey_mcaptcha_upload_jobs.job_state = (\n SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $1\n )\n AND\n survey_mcaptcha_upload_jobs.finished_at is NULL\n AND\n survey_mcaptcha_upload_jobs.scheduled_at is NULL\n ORDER BY created_at ASC;", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "campaign_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "public_id", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "8be51483900058d0bcc4c121440fe551bc57096826119ad914cc1ef2d9ccb79d" +} diff --git a/.sqlx/query-ca41f4e15fa5c5657a525ed9385a92214b644194443ae165957d9659d30dc3f9.json b/.sqlx/query-ca41f4e15fa5c5657a525ed9385a92214b644194443ae165957d9659d30dc3f9.json new file mode 100644 index 0000000..92e330d --- /dev/null +++ b/.sqlx/query-ca41f4e15fa5c5657a525ed9385a92214b644194443ae165957d9659d30dc3f9.json @@ -0,0 +1,65 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n survey_mcaptcha_upload_jobs.ID,\n survey_mcaptcha_upload_jobs.public_id,\n survey_mcaptcha_campaign.campaign_id,\n survey_mcaptcha_campaign.public_id as campaign_public_id,\n survey_mcaptcha_upload_job_states.name,\n survey_mcaptcha_upload_jobs.created_at,\n survey_mcaptcha_upload_jobs.scheduled_at,\n survey_mcaptcha_upload_jobs.finished_at\n\n FROM survey_mcaptcha_upload_jobs\n INNER JOIN\n survey_mcaptcha_upload_job_states\n ON\n survey_mcaptcha_upload_job_states.ID = survey_mcaptcha_upload_jobs.job_state\n INNER JOIN\n survey_mcaptcha_campaign\n ON\n survey_mcaptcha_campaign.ID = survey_mcaptcha_upload_jobs.campaign_id\n WHERE\n survey_mcaptcha_campaign.campaign_id = $1\n AND\n survey_mcaptcha_upload_job_states.name = $2;", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "public_id", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "campaign_id", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "campaign_public_id", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 6, + "name": "scheduled_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 7, + "name": "finished_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + true, + true + ] + }, + "hash": "ca41f4e15fa5c5657a525ed9385a92214b644194443ae165957d9659d30dc3f9" +} diff --git a/.sqlx/query-ebfc456dd76b3fb2e5484f935703ad6aa4712c782222f2015b92916827f81079.json b/.sqlx/query-ebfc456dd76b3fb2e5484f935703ad6aa4712c782222f2015b92916827f81079.json new file mode 100644 index 0000000..af67acf --- /dev/null +++ b/.sqlx/query-ebfc456dd76b3fb2e5484f935703ad6aa4712c782222f2015b92916827f81079.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO survey_mcaptcha_upload_jobs\n (campaign_id, job_state, created_at, public_id)\n VALUES (\n (SELECT ID FROM survey_mcaptcha_campaign WHERE campaign_id = $1),\n (SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $2),\n $3, $4)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Timestamptz", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "ebfc456dd76b3fb2e5484f935703ad6aa4712c782222f2015b92916827f81079" +} diff --git a/.sqlx/query-fade9f99846165c34486f6492ece38148bf0dd2d79e1a4f97b8cbf04015ceff0.json b/.sqlx/query-fade9f99846165c34486f6492ece38148bf0dd2d79e1a4f97b8cbf04015ceff0.json new file mode 100644 index 0000000..068f3db --- /dev/null +++ b/.sqlx/query-fade9f99846165c34486f6492ece38148bf0dd2d79e1a4f97b8cbf04015ceff0.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE\n survey_mcaptcha_upload_jobs\n SET\n job_state = (SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $1),\n finished_at = $2\n WHERE public_id = $3;", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Timestamptz", + "Text" + ] + }, + "nullable": [] + }, + "hash": "fade9f99846165c34486f6492ece38148bf0dd2d79e1a4f97b8cbf04015ceff0" +} diff --git a/src/api/v1/mcaptcha/db.rs b/src/api/v1/mcaptcha/db.rs index a6315bc..9de3643 100644 --- a/src/api/v1/mcaptcha/db.rs +++ b/src/api/v1/mcaptcha/db.rs @@ -15,12 +15,22 @@ * along with this program. If not, see . */ use url::Url; +use uuid::Uuid; use crate::api::v1::get_random; +use crate::db::{ + JobState, JOB_STATES, JOB_STATE_CREATE, JOB_STATE_FINISH, JOB_STATE_RUNNING, +}; use crate::errors::*; use crate::mcaptcha::PerformanceAnalytics; use crate::Data; +use sqlx::types::time::OffsetDateTime; + +fn now_unix_time_stamp() -> OffsetDateTime { + OffsetDateTime::now_utc() +} + impl Data { /// Check if an mCaptcha instance is registered on the database pub async fn mcaptcha_url_exists(&self, url: &str) -> ServiceResult { @@ -127,14 +137,14 @@ impl Data { .await?; Ok(()) } - /// Delete mCaptcha camapign from database + /// Delete mCaptcha campaign from database pub async fn mcaptcha_delete_mcaptcha_campaign( &self, - campaign_id: &uuid::Uuid, + campaign_id: &Uuid, secret: &str, ) -> ServiceResult<()> { let campaign_str = campaign_id.to_string(); - let res = sqlx::query!( + sqlx::query!( "DELETE FROM survey_mcaptcha_campaign WHERE @@ -160,7 +170,7 @@ impl Data { /// Check if an mCaptcha instance campaign is registered on DB pub async fn mcaptcha_campaign_is_registered( &self, - campaign_id: &uuid::Uuid, + campaign_id: &Uuid, secret: &str, ) -> ServiceResult { let campaign_str = campaign_id.to_string(); @@ -201,11 +211,11 @@ impl Data { /// Register an mCaptcha instance campaign on DB pub async fn mcaptcha_register_campaign( &self, - campaign_id: &uuid::Uuid, + campaign_id: &Uuid, secret: &str, ) -> ServiceResult<()> { let campaign_str = campaign_id.to_string(); - let public_id = uuid::Uuid::new_v4(); + let public_id = Uuid::new_v4(); sqlx::query!( "INSERT INTO @@ -223,9 +233,9 @@ impl Data { /// Register an mCaptcha instance campaign on DB pub async fn mcaptcha_get_campaign_public_id( &self, - campaign_id: &uuid::Uuid, + campaign_id: &Uuid, secret: &str, - ) -> ServiceResult { + ) -> ServiceResult { let campaign_str = campaign_id.to_string(); struct S { public_id: String, @@ -247,13 +257,13 @@ impl Data { .fetch_one(&self.db) .await?; - Ok(uuid::Uuid::parse_str(&res.public_id).unwrap()) + Ok(Uuid::parse_str(&res.public_id).unwrap()) } /// Get an mCaptcha instance campaign checkpoint pub async fn mcaptcha_get_checkpoint( &self, - campaign_id: &uuid::Uuid, + campaign_id: &Uuid, secret: &str, ) -> ServiceResult { let campaign_str = campaign_id.to_string(); @@ -286,7 +296,7 @@ impl Data { /// Set an mCaptcha instance campaign checkpoint pub async fn mcaptcha_set_checkpoint( &self, - campaign_id: &uuid::Uuid, + campaign_id: &Uuid, secret: &str, checkpoint: usize, ) -> ServiceResult<()> { @@ -316,7 +326,7 @@ impl Data { /// Store mCaptcha instance campaign analytics pub async fn mcaptcha_insert_analytics( &self, - campaign_id: &uuid::Uuid, + campaign_id: &Uuid, secret: &str, r: &PerformanceAnalytics, ) -> ServiceResult<()> { @@ -353,7 +363,7 @@ impl Data { /// fetch PoW analytics pub async fn mcaptcha_analytics_fetch( &self, - public_id: &uuid::Uuid, + public_id: &Uuid, limit: usize, offset: usize, ) -> ServiceResult> { @@ -402,12 +412,294 @@ impl Data { Ok(res) } + + pub async fn get_next_job_to_run(&self) -> ServiceResult { + let res = sqlx::query_as!( + InnerSchedulerJob, + "SELECT + survey_mcaptcha_campaign.campaign_id, + survey_mcaptcha_upload_jobs.public_id + FROM + survey_mcaptcha_campaign + INNER JOIN + survey_mcaptcha_upload_jobs + ON + survey_mcaptcha_upload_jobs.campaign_id = survey_mcaptcha_campaign.ID + WHERE + survey_mcaptcha_upload_jobs.job_state = ( + SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $1 + ) + AND + survey_mcaptcha_upload_jobs.finished_at is NULL + AND + survey_mcaptcha_upload_jobs.scheduled_at is NULL + ORDER BY created_at ASC;", + &JOB_STATE_CREATE.name + ) + .fetch_one(&self.db) + .await?; + Ok(res.into()) + } + + pub async fn add_job(&self, campaign_id: &Uuid) -> ServiceResult { + let now = now_unix_time_stamp(); + + if let Some(unfinished_job) = + self.get_unfinished_job_for_campaign(campaign_id).await? + { + return Ok(unfinished_job.public_job_id); + } + + let public_id = Uuid::new_v4(); + let public_id_str = public_id.to_string(); + + let campaign_str = campaign_id.to_string(); + sqlx::query!( + "INSERT INTO survey_mcaptcha_upload_jobs + (campaign_id, job_state, created_at, public_id) + VALUES ( + (SELECT ID FROM survey_mcaptcha_campaign WHERE campaign_id = $1), + (SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $2), + $3, $4)", + &campaign_str, + &JOB_STATE_CREATE.name, + now, + public_id_str + ) + .execute(&self.db) + .await?; + Ok(public_id) + } + + pub async fn get_unfinished_job_for_campaign( + &self, + campaign_id: &Uuid, + ) -> ServiceResult> { + let res = match sqlx::query_as!( + InnerJob, + " + SELECT + survey_mcaptcha_upload_jobs.ID, + survey_mcaptcha_upload_jobs.public_id, + survey_mcaptcha_campaign.campaign_id, + survey_mcaptcha_campaign.public_id as campaign_public_id, + survey_mcaptcha_upload_job_states.name, + survey_mcaptcha_upload_jobs.created_at, + survey_mcaptcha_upload_jobs.scheduled_at, + survey_mcaptcha_upload_jobs.finished_at + + FROM survey_mcaptcha_upload_jobs + INNER JOIN + survey_mcaptcha_upload_job_states + ON + survey_mcaptcha_upload_job_states.ID = survey_mcaptcha_upload_jobs.job_state + INNER JOIN + survey_mcaptcha_campaign + ON + survey_mcaptcha_campaign.ID = survey_mcaptcha_upload_jobs.campaign_id + WHERE + survey_mcaptcha_campaign.campaign_id = $1 + AND + survey_mcaptcha_upload_job_states.name = $2;", + &campaign_id.to_string(), + &JOB_STATE_CREATE.name + ) + .fetch_one(&self.db) + .await { + Ok(res) => Ok(Some(res.into())), + Err(sqlx::Error::RowNotFound) => Ok(None), + Err(e) => Err(e), + }?; + Ok(res) + } + + pub async fn get_job(&self, public_id: &uuid::Uuid) -> ServiceResult> { + let res = match sqlx::query_as!( + InnerJob, + " + SELECT + survey_mcaptcha_upload_jobs.ID, + survey_mcaptcha_upload_jobs.public_id, + survey_mcaptcha_campaign.campaign_id, + survey_mcaptcha_campaign.public_id as campaign_public_id, + survey_mcaptcha_upload_job_states.name, + survey_mcaptcha_upload_jobs.created_at, + survey_mcaptcha_upload_jobs.scheduled_at, + survey_mcaptcha_upload_jobs.finished_at + + FROM survey_mcaptcha_upload_jobs + INNER JOIN + survey_mcaptcha_upload_job_states + ON + survey_mcaptcha_upload_job_states.ID = survey_mcaptcha_upload_jobs.job_state + INNER JOIN + survey_mcaptcha_campaign + ON + survey_mcaptcha_campaign.ID = survey_mcaptcha_upload_jobs.campaign_id + WHERE + survey_mcaptcha_upload_jobs.public_id = $1", + &public_id.to_string() + ) + .fetch_one(&self.db) + .await { + Ok(res) => Ok(Some(res.into())), + Err(sqlx::Error::RowNotFound) => Ok(None), + Err(e) => Err(e), + }?; + + Ok(res.into()) + } + + pub async fn get_all_jobs_of_state( + &self, + state: &JobState, + ) -> ServiceResult> { + let mut res = sqlx::query_as!( + InnerJob, + " + SELECT + survey_mcaptcha_upload_jobs.ID, + survey_mcaptcha_upload_jobs.public_id, + survey_mcaptcha_campaign.campaign_id, + survey_mcaptcha_campaign.public_id as campaign_public_id, + survey_mcaptcha_upload_job_states.name, + survey_mcaptcha_upload_jobs.created_at, + survey_mcaptcha_upload_jobs.scheduled_at, + survey_mcaptcha_upload_jobs.finished_at + + FROM survey_mcaptcha_upload_jobs + INNER JOIN + survey_mcaptcha_upload_job_states + ON + survey_mcaptcha_upload_job_states.ID = survey_mcaptcha_upload_jobs.job_state + INNER JOIN + survey_mcaptcha_campaign + ON + survey_mcaptcha_campaign.ID = survey_mcaptcha_upload_jobs.campaign_id + WHERE + survey_mcaptcha_upload_job_states.name = $1;", + &state.name + ) + .fetch_all(&self.db) + .await?; + + let res = res.drain(0..).map(|r| r.into()).collect(); + + Ok(res) + } + + pub async fn mark_job_scheduled(&self, public_id: &Uuid) -> ServiceResult<()> { + let now = now_unix_time_stamp(); + sqlx::query!( + " + UPDATE + survey_mcaptcha_upload_jobs + SET + job_state = (SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $1), + scheduled_at = $2 + WHERE public_id = $3;", + &JOB_STATE_RUNNING.name, + now, + &public_id.to_string(), + ) + .execute(&self.db) + .await + ?; + + Ok(()) + } + + pub async fn mark_job_finished(&self, public_id: &Uuid) -> ServiceResult<()> { + let now = now_unix_time_stamp(); + sqlx::query!( + " + UPDATE + survey_mcaptcha_upload_jobs + SET + job_state = (SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $1), + finished_at = $2 + WHERE public_id = $3;", + &JOB_STATE_FINISH.name, + now, + &public_id.to_string(), + ) + .execute(&self.db) + .await + ?; + + Ok(()) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SchedulerJob { + pub campaign_id: Uuid, + pub public_job_id: Uuid, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +struct InnerSchedulerJob { + campaign_id: String, + public_id: String, +} +impl From for SchedulerJob { + fn from(j: InnerSchedulerJob) -> Self { + SchedulerJob { + campaign_id: Uuid::parse_str(&j.campaign_id).unwrap(), + public_job_id: Uuid::parse_str(&j.public_id).unwrap(), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Job { + pub state: JobState, + pub campaign_id: Uuid, + pub campaign_public_id: Uuid, + pub public_job_id: Uuid, + pub id: u32, + pub created_at: OffsetDateTime, + pub scheduled_at: Option, + pub finished_at: Option, +} + +struct InnerJob { + name: String, + campaign_id: String, + public_id: String, + campaign_public_id: String, + id: i32, + created_at: OffsetDateTime, + scheduled_at: Option, + finished_at: Option, +} + +impl From for Job { + fn from(j: InnerJob) -> Self { + Job { + state: (JOB_STATES) + .iter() + .find(|d| d.name == j.name) + .unwrap() + .to_owned() + .to_owned(), + id: j.id as u32, + created_at: j.created_at, + scheduled_at: j.scheduled_at, + finished_at: j.finished_at, + + campaign_id: Uuid::parse_str(&j.campaign_id).unwrap(), + campaign_public_id: Uuid::parse_str(&j.campaign_public_id).unwrap(), + public_job_id: Uuid::parse_str(&j.public_id).unwrap(), + } + } } #[cfg(test)] mod tests { use crate::{mcaptcha::PerformanceAnalytics, tests::*}; + use super::*; use url::Url; #[actix_rt::test] @@ -436,7 +728,7 @@ mod tests { url ); - let uuid = uuid::Uuid::new_v4(); + let uuid = Uuid::new_v4(); if data .mcaptcha_campaign_is_registered(&uuid, &secret) @@ -505,5 +797,61 @@ mod tests { .unwrap(), vec![] ); + + // job related stuff + + let job1_public_id = data.add_job(&uuid).await.unwrap(); + let job = data.get_job(&job1_public_id).await.unwrap().unwrap(); + assert_eq!(public_id, job.campaign_public_id); + assert_eq!( + data.get_unfinished_job_for_campaign(&uuid) + .await + .unwrap() + .unwrap(), + job + ); + let job2_public_id = data.add_job(&uuid).await.unwrap(); + let job2 = data.get_job(&job2_public_id).await.unwrap().unwrap(); + assert_eq!(job2, job); + + assert_eq!( + data.get_next_job_to_run().await.unwrap().public_job_id, + job.public_job_id + ); + + assert!(job.created_at < now_unix_time_stamp()); + assert!(job.scheduled_at.is_none()); + assert!(job.finished_at.is_none()); + assert_eq!( + data.get_all_jobs_of_state(&JOB_STATE_CREATE).await.unwrap(), + vec![job.clone()] + ); + + data.mark_job_scheduled(&job.public_job_id).await.unwrap(); + let job = data.get_job(&job.public_job_id).await.unwrap().unwrap(); + assert!(job.scheduled_at.is_some()); + assert_eq!( + data.get_all_jobs_of_state(&JOB_STATE_RUNNING) + .await + .unwrap(), + vec![job.clone()] + ); + + data.mark_job_finished(&job.public_job_id).await.unwrap(); + let job = data.get_job(&job.public_job_id).await.unwrap().unwrap(); + assert!(job.finished_at.is_some()); + assert_eq!( + data.get_all_jobs_of_state(&JOB_STATE_FINISH).await.unwrap(), + vec![job.clone()] + ); + + let job2_public_id = data.add_job(&uuid).await.unwrap(); + let job2 = data.get_job(&job2_public_id).await.unwrap().unwrap(); + assert_ne!(job2.public_job_id, job.public_job_id); + assert_eq!( + data.get_next_job_to_run().await.unwrap().public_job_id, + job2.public_job_id + ); + assert_eq!(public_id, job2.campaign_public_id); } } diff --git a/src/archive.rs b/src/archive.rs index 33c6103..6dc8c46 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -14,7 +14,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -use std::future::Future; use std::path::{Path, PathBuf}; use serde::{Deserialize, Serialize};