use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::{postgres::PgQueryResult, prelude::FromRow, types::Json};
use uuid::Uuid;
use super::{document_box::DocumentBoxScopeRaw, file::FileId, folder::FolderId, user::UserId};
use crate::{DbErr, DbExecutor, DbResult};
pub type PresignedUploadTaskId = Uuid;
#[derive(Debug, Clone, FromRow, Serialize)]
pub struct PresignedUploadTask {
pub id: PresignedUploadTaskId,
#[sqlx(json)]
pub status: PresignedTaskStatus,
pub name: String,
pub mime: String,
pub size: i32,
pub document_box: DocumentBoxScopeRaw,
pub folder_id: FolderId,
pub file_key: String,
pub created_at: DateTime<Utc>,
pub expires_at: DateTime<Utc>,
pub created_by: Option<UserId>,
pub parent_id: Option<FileId>,
pub processing_config: Option<Json<serde_json::Value>>,
}
impl Eq for PresignedUploadTask {}
impl PartialEq for PresignedUploadTask {
fn eq(&self, other: &Self) -> bool {
self.id.eq(&other.id)
&& self.status.eq(&other.status)
&& self.name.eq(&other.name)
&& self.mime.eq(&other.mime)
&& self.size.eq(&other.size)
&& self.document_box.eq(&other.document_box)
&& self.folder_id.eq(&other.folder_id)
&& self.file_key.eq(&other.file_key)
&& self
.created_at
.timestamp_millis()
.eq(&other.created_at.timestamp_millis())
&& self
.expires_at
.timestamp_millis()
.eq(&other.expires_at.timestamp_millis())
&& self.created_by.eq(&self.created_by)
&& self.parent_id.eq(&other.parent_id)
&& self.processing_config.eq(&other.processing_config)
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(tag = "status")]
pub enum PresignedTaskStatus {
Pending,
Completed { file_id: FileId },
Failed { error: String },
}
#[derive(Default)]
pub struct CreatePresignedUploadTask {
pub name: String,
pub mime: String,
pub document_box: DocumentBoxScopeRaw,
pub folder_id: FolderId,
pub size: i32,
pub file_key: String,
pub created_by: Option<UserId>,
pub expires_at: DateTime<Utc>,
pub parent_id: Option<FileId>,
pub processing_config: Option<serde_json::Value>,
}
impl PresignedUploadTask {
pub async fn create(
db: impl DbExecutor<'_>,
create: CreatePresignedUploadTask,
) -> DbResult<PresignedUploadTask> {
let id = Uuid::new_v4();
let created_at = Utc::now();
let task = PresignedUploadTask {
id,
status: PresignedTaskStatus::Pending,
name: create.name,
mime: create.mime,
size: create.size,
document_box: create.document_box,
folder_id: create.folder_id,
file_key: create.file_key,
created_at,
expires_at: create.expires_at,
created_by: create.created_by,
parent_id: create.parent_id,
processing_config: create.processing_config.map(Json),
};
let status_json =
serde_json::to_value(&task.status).map_err(|err| DbErr::Encode(Box::new(err)))?;
let processing_config_json = task.processing_config.clone();
sqlx::query(
r#"
INSERT INTO "docbox_presigned_upload_tasks" (
"id",
"status",
"name",
"mime",
"size",
"document_box",
"folder_id",
"file_key",
"created_at",
"expires_at",
"created_by",
"parent_id",
"processing_config"
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
"#,
)
.bind(task.id)
.bind(status_json)
.bind(task.name.as_str())
.bind(task.mime.clone())
.bind(task.size)
.bind(task.document_box.as_str())
.bind(task.folder_id)
.bind(task.file_key.as_str())
.bind(task.created_at)
.bind(task.expires_at)
.bind(task.created_by.clone())
.bind(task.parent_id)
.bind(processing_config_json)
.execute(db)
.await?;
Ok(task)
}
pub async fn set_status(
&mut self,
db: impl DbExecutor<'_>,
status: PresignedTaskStatus,
) -> DbResult<()> {
let status_json =
serde_json::to_value(&status).map_err(|err| DbErr::Encode(Box::new(err)))?;
sqlx::query(r#"UPDATE "docbox_presigned_upload_tasks" SET "status" = $1 WHERE "id" = $2"#)
.bind(status_json)
.bind(self.id)
.execute(db)
.await?;
self.status = status;
Ok(())
}
pub async fn find(
db: impl DbExecutor<'_>,
scope: &DocumentBoxScopeRaw,
task_id: PresignedUploadTaskId,
) -> DbResult<Option<PresignedUploadTask>> {
sqlx::query_as(
r#"SELECT * FROM "docbox_presigned_upload_tasks"
WHERE "id" = $1 AND "document_box" = $2"#,
)
.bind(task_id)
.bind(scope)
.fetch_optional(db)
.await
}
pub async fn find_expired(
db: impl DbExecutor<'_>,
current_date: DateTime<Utc>,
) -> DbResult<Vec<PresignedUploadTask>> {
sqlx::query_as(r#"SELECT * FROM "docbox_presigned_upload_tasks" WHERE "expires_at" < $1"#)
.bind(current_date)
.fetch_all(db)
.await
}
pub async fn find_by_file_key(
db: impl DbExecutor<'_>,
file_key: &str,
) -> DbResult<Option<PresignedUploadTask>> {
sqlx::query_as(r#"SELECT * FROM "docbox_presigned_upload_tasks" WHERE "file_key" = $1"#)
.bind(file_key)
.fetch_optional(db)
.await
}
pub async fn delete(
db: impl DbExecutor<'_>,
task_id: PresignedUploadTaskId,
) -> DbResult<PgQueryResult> {
sqlx::query(r#"DELETE FROM "docbox_presigned_upload_tasks" WHERE "id" = $1"#)
.bind(task_id)
.execute(db)
.await
}
}