1000 lines
30 KiB
Rust
1000 lines
30 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
use serde_json::Value;
|
|
use utoipa::{IntoParams, OpenApi, ToSchema};
|
|
use uuid::Uuid;
|
|
|
|
#[derive(OpenApi)]
|
|
#[openapi(
|
|
paths(
|
|
doc::health_check,
|
|
doc::login,
|
|
doc::refresh,
|
|
doc::logout,
|
|
doc::me,
|
|
doc::select_tenant,
|
|
doc::list_documents,
|
|
doc::check_document,
|
|
doc::upload_document,
|
|
doc::get_document,
|
|
doc::update_document,
|
|
doc::delete_document,
|
|
doc::download_document,
|
|
doc::download_with_token,
|
|
doc::move_document,
|
|
doc::assign_tags,
|
|
doc::remove_tag,
|
|
doc::bulk_move_documents,
|
|
doc::bulk_update_tags,
|
|
doc::bulk_assign_correspondents,
|
|
doc::assign_correspondents,
|
|
doc::remove_correspondent,
|
|
doc::reanalyze_selected_documents,
|
|
doc::list_document_assets,
|
|
doc::request_document_assets,
|
|
doc::get_document_asset,
|
|
doc::create_folder,
|
|
doc::ensure_folder_path,
|
|
doc::get_folder,
|
|
doc::list_folder_contents,
|
|
doc::delete_folder,
|
|
doc::update_folder,
|
|
doc::list_tags,
|
|
doc::create_tag,
|
|
doc::update_tag,
|
|
doc::delete_tag,
|
|
doc::list_correspondents,
|
|
doc::create_correspondent,
|
|
doc::update_correspondent,
|
|
doc::delete_correspondent,
|
|
),
|
|
components(
|
|
schemas(
|
|
schemas::LoginRequest,
|
|
schemas::AccessTokenResponse,
|
|
schemas::TenantSummary,
|
|
schemas::TenantSelectionResponse,
|
|
schemas::TenantSelectionRequest,
|
|
schemas::LoginResponseVariants,
|
|
schemas::DocumentResponse,
|
|
schemas::DocumentDetailResponse,
|
|
schemas::DocumentVersion,
|
|
schemas::DocumentAssetSummary,
|
|
schemas::DocumentAssetDetail,
|
|
schemas::DocumentAssetObject,
|
|
schemas::DocumentCorrespondent,
|
|
schemas::DocumentTag,
|
|
schemas::DocumentDownloadResponse,
|
|
schemas::UpdateDocumentRequest,
|
|
schemas::BulkMoveDocumentsRequest,
|
|
schemas::BulkMoveDocumentsResponse,
|
|
schemas::AssignTagsRequest,
|
|
schemas::MoveDocumentRequest,
|
|
schemas::BulkTagRequest,
|
|
schemas::BulkTagResponse,
|
|
schemas::CorrespondentAssignment,
|
|
schemas::BulkTagAction,
|
|
schemas::BulkCorrespondentsRequest,
|
|
schemas::BulkCorrespondentsResponse,
|
|
schemas::BulkCorrespondentAction,
|
|
schemas::AssignCorrespondentsRequest,
|
|
schemas::RemoveCorrespondentParams,
|
|
schemas::ReanalyzeRequest,
|
|
schemas::ReanalyzeResponse,
|
|
schemas::DocumentAssetRequestParams,
|
|
schemas::AssetObjectsQuery,
|
|
schemas::DocumentCheckQuery,
|
|
schemas::DocumentCheckResponse,
|
|
schemas::UploadDocumentForm,
|
|
schemas::CreateFolderRequest,
|
|
schemas::EnsureFolderPathRequest,
|
|
schemas::FolderResponse,
|
|
schemas::FolderInfo,
|
|
schemas::FolderContentsResponse,
|
|
schemas::FolderDocumentSummary,
|
|
schemas::UpdateFolderRequest,
|
|
schemas::FolderContentsParams,
|
|
schemas::TagCatalogEntry,
|
|
schemas::CreateTagRequest,
|
|
schemas::UpdateTagRequest,
|
|
schemas::CorrespondentCatalogEntry,
|
|
schemas::CreateCorrespondentRequest,
|
|
schemas::UpdateCorrespondentRequest,
|
|
)
|
|
),
|
|
tags(
|
|
(name = "Health", description = "Service health"),
|
|
(name = "Auth", description = "Authentication"),
|
|
(name = "Documents", description = "Document management"),
|
|
(name = "Assets", description = "Document assets"),
|
|
(name = "Folders", description = "Folder management"),
|
|
(name = "Tags", description = "Tag catalog"),
|
|
(name = "Correspondents", description = "Correspondent catalog")
|
|
)
|
|
)]
|
|
pub struct ApiDoc;
|
|
|
|
#[allow(dead_code)]
|
|
mod doc {
|
|
use super::schemas::*;
|
|
use uuid::Uuid;
|
|
|
|
#[allow(dead_code)]
|
|
fn __keep_uuid_import() {
|
|
let _ = Uuid::nil();
|
|
}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/api/health",
|
|
responses((status = 200, description = "Service is healthy")),
|
|
tag = "Health"
|
|
)]
|
|
pub(super) fn health_check() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/auth/login",
|
|
request_body = LoginRequest,
|
|
responses(
|
|
(status = 200, description = "Login succeeded", body = LoginResponseVariants),
|
|
(status = 401, description = "Invalid credentials")
|
|
),
|
|
tag = "Auth"
|
|
)]
|
|
pub(super) fn login() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/auth/refresh",
|
|
responses(
|
|
(status = 200, description = "Refreshed access token", body = AccessTokenResponse),
|
|
(status = 401, description = "Missing or invalid refresh token")
|
|
),
|
|
tag = "Auth"
|
|
)]
|
|
pub(super) fn refresh() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/auth/logout",
|
|
responses((status = 204, description = "Session revoked")),
|
|
tag = "Auth"
|
|
)]
|
|
pub(super) fn logout() {}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/api/auth/me",
|
|
responses((status = 200, description = "Authenticated principal", body = AccessTokenResponse)),
|
|
tag = "Auth"
|
|
)]
|
|
pub(super) fn me() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/auth/select-tenant",
|
|
request_body = TenantSelectionRequest,
|
|
responses((status = 200, description = "Tenant selected", body = AccessTokenResponse)),
|
|
tag = "Auth"
|
|
)]
|
|
pub(super) fn select_tenant() {}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/api/documents",
|
|
params(DocumentListQuery),
|
|
responses((status = 200, description = "List documents", body = [DocumentResponse])),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn list_documents() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/documents",
|
|
request_body = UploadDocumentForm,
|
|
responses(
|
|
(status = 201, description = "Document created", body = DocumentDetailResponse),
|
|
(status = 200, description = "Existing document reused", body = DocumentDetailResponse),
|
|
(status = 204, description = "Upload skipped because the document already exists")
|
|
),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn upload_document() {}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/api/documents/check",
|
|
params(DocumentCheckQuery),
|
|
responses((status = 200, description = "Checksum lookup", body = DocumentCheckResponse)),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn check_document() {}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/api/documents/{id}",
|
|
params(("id" = Uuid, Path, description = "Document ID")),
|
|
responses((status = 200, description = "Document detail", body = DocumentDetailResponse)),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn get_document() {}
|
|
|
|
#[utoipa::path(
|
|
patch,
|
|
path = "/api/documents/{id}",
|
|
params(("id" = Uuid, Path, description = "Document ID")),
|
|
request_body = UpdateDocumentRequest,
|
|
responses((status = 200, description = "Updated document", body = DocumentDetailResponse)),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn update_document() {}
|
|
|
|
#[utoipa::path(
|
|
delete,
|
|
path = "/api/documents/{id}",
|
|
params(("id" = Uuid, Path, description = "Document ID")),
|
|
responses((status = 204, description = "Document deleted")),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn delete_document() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/documents/{id}/restore",
|
|
params(("id" = Uuid, Path, description = "Document ID")),
|
|
request_body = RestoreDocumentRequest,
|
|
responses((status = 204, description = "Document restored")),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn restore_document() {}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/api/documents/{id}/download",
|
|
params(("id" = Uuid, Path, description = "Document ID")),
|
|
responses((status = 200, description = "Download metadata", body = DocumentDownloadResponse)),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn download_document() {}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/download/{token}",
|
|
params(("token" = String, Path, description = "Download token")),
|
|
responses((status = 302, description = "Redirect to pre-signed URL")),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn download_with_token() {}
|
|
|
|
#[utoipa::path(
|
|
patch,
|
|
path = "/api/documents/{id}/folder",
|
|
params(("id" = Uuid, Path, description = "Document ID")),
|
|
request_body = MoveDocumentRequest,
|
|
responses((status = 204, description = "Document moved")),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn move_document() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/documents/{id}/tags",
|
|
params(("id" = Uuid, Path, description = "Document ID")),
|
|
request_body = AssignTagsRequest,
|
|
responses((status = 204, description = "Tags assigned")),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn assign_tags() {}
|
|
|
|
#[utoipa::path(
|
|
delete,
|
|
path = "/api/documents/{id}/tags/{tag_id}",
|
|
params(
|
|
("id" = Uuid, Path, description = "Document ID"),
|
|
("tag_id" = Uuid, Path, description = "Tag ID")
|
|
),
|
|
responses((status = 204, description = "Tag removed")),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn remove_tag() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/documents/bulk/move",
|
|
request_body = BulkMoveDocumentsRequest,
|
|
responses((status = 200, description = "Bulk move outcome", body = BulkMoveDocumentsResponse)),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn bulk_move_documents() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/documents/bulk/tags",
|
|
request_body = BulkTagRequest,
|
|
responses((status = 200, description = "Bulk tag outcome", body = BulkTagResponse)),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn bulk_update_tags() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/documents/bulk/correspondents",
|
|
request_body = BulkCorrespondentsRequest,
|
|
responses((status = 200, description = "Bulk correspondents outcome", body = BulkCorrespondentsResponse)),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn bulk_assign_correspondents() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/documents/{id}/correspondents",
|
|
params(("id" = Uuid, Path, description = "Document ID")),
|
|
request_body = AssignCorrespondentsRequest,
|
|
responses((status = 204, description = "Correspondents assigned")),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn assign_correspondents() {}
|
|
|
|
#[utoipa::path(
|
|
delete,
|
|
path = "/api/documents/{id}/correspondents/{correspondent_id}",
|
|
params(
|
|
("id" = Uuid, Path, description = "Document ID"),
|
|
("correspondent_id" = Uuid, Path, description = "Correspondent ID"),
|
|
RemoveCorrespondentParams
|
|
),
|
|
responses((status = 204, description = "Correspondent removed")),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn remove_correspondent() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/documents/bulk/reanalyze",
|
|
request_body = ReanalyzeRequest,
|
|
responses((status = 200, description = "Reanalyze queued", body = ReanalyzeResponse)),
|
|
tag = "Documents"
|
|
)]
|
|
pub(super) fn reanalyze_selected_documents() {}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/api/documents/{id}/assets",
|
|
params(("id" = Uuid, Path, description = "Document ID")),
|
|
responses((status = 200, description = "Document assets", body = [DocumentAssetSummary])),
|
|
tag = "Assets"
|
|
)]
|
|
pub(super) fn list_document_assets() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/documents/{id}/assets",
|
|
params(
|
|
("id" = Uuid, Path, description = "Document ID"),
|
|
DocumentAssetRequestParams
|
|
),
|
|
responses((status = 202, description = "Asset generation requested")),
|
|
tag = "Assets"
|
|
)]
|
|
pub(super) fn request_document_assets() {}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/api/assets/{asset_id}",
|
|
params(
|
|
("asset_id" = Uuid, Path, description = "Asset ID"),
|
|
AssetObjectsQuery
|
|
),
|
|
responses((status = 200, description = "Asset detail", body = DocumentAssetDetail)),
|
|
tag = "Assets"
|
|
)]
|
|
pub(super) fn get_document_asset() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/folders",
|
|
request_body = CreateFolderRequest,
|
|
responses((status = 200, description = "Folder created", body = FolderResponse)),
|
|
tag = "Folders"
|
|
)]
|
|
pub(super) fn create_folder() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/folders/path",
|
|
request_body = EnsureFolderPathRequest,
|
|
responses((status = 200, description = "Folder path ensured", body = FolderResponse)),
|
|
tag = "Folders"
|
|
)]
|
|
pub(super) fn ensure_folder_path() {}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/api/folders/{id}",
|
|
params(("id" = Uuid, Path, description = "Folder ID")),
|
|
responses((status = 200, description = "Folder detail", body = FolderResponse)),
|
|
tag = "Folders"
|
|
)]
|
|
pub(super) fn get_folder() {}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/api/folders/{id}/contents",
|
|
params(
|
|
("id" = Uuid, Path, description = "Folder ID"),
|
|
FolderContentsParams
|
|
),
|
|
responses((status = 200, description = "Folder contents", body = FolderContentsResponse)),
|
|
tag = "Folders"
|
|
)]
|
|
pub(super) fn list_folder_contents() {}
|
|
|
|
#[utoipa::path(
|
|
delete,
|
|
path = "/api/folders/{id}",
|
|
params(("id" = Uuid, Path, description = "Folder ID")),
|
|
responses((status = 204, description = "Folder deleted")),
|
|
tag = "Folders"
|
|
)]
|
|
pub(super) fn delete_folder() {}
|
|
|
|
#[utoipa::path(
|
|
patch,
|
|
path = "/api/folders/{id}",
|
|
params(("id" = Uuid, Path, description = "Folder ID")),
|
|
request_body = UpdateFolderRequest,
|
|
responses((status = 204, description = "Folder updated")),
|
|
tag = "Folders"
|
|
)]
|
|
pub(super) fn update_folder() {}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/api/tags",
|
|
responses((status = 200, description = "Tags", body = [TagCatalogEntry])),
|
|
tag = "Tags"
|
|
)]
|
|
pub(super) fn list_tags() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/tags",
|
|
request_body = CreateTagRequest,
|
|
responses((status = 200, description = "Tag created", body = TagCatalogEntry)),
|
|
tag = "Tags"
|
|
)]
|
|
pub(super) fn create_tag() {}
|
|
|
|
#[utoipa::path(
|
|
patch,
|
|
path = "/api/tags/{id}",
|
|
params(("id" = Uuid, Path, description = "Tag ID")),
|
|
request_body = UpdateTagRequest,
|
|
responses((status = 200, description = "Tag updated", body = TagCatalogEntry)),
|
|
tag = "Tags"
|
|
)]
|
|
pub(super) fn update_tag() {}
|
|
|
|
#[utoipa::path(
|
|
delete,
|
|
path = "/api/tags/{id}",
|
|
params(("id" = Uuid, Path, description = "Tag ID")),
|
|
responses((status = 204, description = "Tag deleted")),
|
|
tag = "Tags"
|
|
)]
|
|
pub(super) fn delete_tag() {}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/api/correspondents",
|
|
responses((status = 200, description = "Correspondents", body = [CorrespondentCatalogEntry])),
|
|
tag = "Correspondents"
|
|
)]
|
|
pub(super) fn list_correspondents() {}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/correspondents",
|
|
request_body = CreateCorrespondentRequest,
|
|
responses((status = 200, description = "Correspondent created", body = CorrespondentCatalogEntry)),
|
|
tag = "Correspondents"
|
|
)]
|
|
pub(super) fn create_correspondent() {}
|
|
|
|
#[utoipa::path(
|
|
patch,
|
|
path = "/api/correspondents/{id}",
|
|
params(("id" = Uuid, Path, description = "Correspondent ID")),
|
|
request_body = UpdateCorrespondentRequest,
|
|
responses((status = 200, description = "Correspondent updated", body = CorrespondentCatalogEntry)),
|
|
tag = "Correspondents"
|
|
)]
|
|
pub(super) fn update_correspondent() {}
|
|
|
|
#[utoipa::path(
|
|
delete,
|
|
path = "/api/correspondents/{id}",
|
|
params(("id" = Uuid, Path, description = "Correspondent ID")),
|
|
responses((status = 204, description = "Correspondent deleted")),
|
|
tag = "Correspondents"
|
|
)]
|
|
pub(super) fn delete_correspondent() {}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::ApiDoc;
|
|
use utoipa::OpenApi;
|
|
|
|
#[test]
|
|
fn openapi_serializes() {
|
|
let spec = ApiDoc::openapi();
|
|
let _ = serde_json::to_string(&spec).expect("serialize openapi");
|
|
}
|
|
}
|
|
|
|
pub mod schemas {
|
|
use super::*;
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct LoginRequest {
|
|
pub username: String,
|
|
pub password: String,
|
|
#[schema(nullable)]
|
|
pub preferred_tenant_slug: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct AccessTokenResponse {
|
|
pub access_token: String,
|
|
pub token_type: String,
|
|
pub expires_in: i64,
|
|
pub tenant: TenantSnippet,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct TenantSummary {
|
|
pub tenant_id: Uuid,
|
|
pub slug: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct TenantSnippet {
|
|
pub id: Uuid,
|
|
pub slug: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct TenantSelectionResponse {
|
|
pub access_token: String,
|
|
pub tenants: Vec<TenantSummary>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct TenantSelectionRequest {
|
|
pub tenant_id: Uuid,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
#[serde(untagged)]
|
|
pub enum LoginResponseVariants {
|
|
Token(AccessTokenResponse),
|
|
Selection(TenantSelectionResponse),
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, IntoParams, ToSchema)]
|
|
#[into_params(parameter_in = Query)]
|
|
pub struct DocumentListQuery {
|
|
pub folder_id: Option<Uuid>,
|
|
#[schema(nullable)]
|
|
pub include_descendants: Option<bool>,
|
|
pub query: Option<String>,
|
|
pub tags: Option<String>,
|
|
pub correspondents: Option<String>,
|
|
#[serde(default = "default_document_status_filter")]
|
|
#[schema(default = "active")]
|
|
pub status: DocumentStatusFilter,
|
|
}
|
|
|
|
fn default_document_status_filter() -> DocumentStatusFilter {
|
|
DocumentStatusFilter::Active
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum DocumentStatusFilter {
|
|
Active,
|
|
Deleted,
|
|
All,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct DocumentTag {
|
|
pub id: Uuid,
|
|
pub label: String,
|
|
#[schema(nullable)]
|
|
pub color: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct DocumentAssetObject {
|
|
pub id: Uuid,
|
|
pub ordinal: i32,
|
|
pub metadata: Value,
|
|
#[schema(nullable)]
|
|
pub url: Option<String>,
|
|
#[schema(nullable)]
|
|
pub expires_at: Option<i64>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct DocumentAssetSummary {
|
|
pub id: Uuid,
|
|
pub asset_type: String,
|
|
pub mime_type: String,
|
|
pub metadata: Value,
|
|
#[schema(nullable)]
|
|
pub cardinality: Option<i32>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct DocumentAssetDetail {
|
|
pub id: Uuid,
|
|
pub asset_type: String,
|
|
pub mime_type: String,
|
|
pub metadata: Value,
|
|
pub created_at: String,
|
|
#[schema(nullable)]
|
|
pub cardinality: Option<i32>,
|
|
pub objects: Vec<DocumentAssetObject>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct DocumentVersion {
|
|
pub id: Uuid,
|
|
pub version_number: i32,
|
|
pub checksum: String,
|
|
pub size_bytes: i64,
|
|
pub created_at: String,
|
|
pub metadata: Value,
|
|
#[schema(nullable)]
|
|
pub assets: Option<Vec<DocumentAssetSummary>>,
|
|
pub download_path: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct DocumentCorrespondent {
|
|
pub id: Uuid,
|
|
pub name: String,
|
|
pub role: String,
|
|
pub metadata: Value,
|
|
pub assigned_at: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct DocumentResponse {
|
|
pub id: Uuid,
|
|
pub filename: String,
|
|
pub title: String,
|
|
pub original_name: String,
|
|
#[schema(nullable)]
|
|
pub content_type: Option<String>,
|
|
#[schema(nullable)]
|
|
pub folder_id: Option<Uuid>,
|
|
pub uploaded_at: String,
|
|
pub updated_at: String,
|
|
#[schema(nullable)]
|
|
pub deleted_at: Option<String>,
|
|
#[schema(nullable)]
|
|
pub issued_at: Option<String>,
|
|
pub metadata: Value,
|
|
pub tags: Vec<DocumentTag>,
|
|
#[schema(nullable)]
|
|
pub correspondents: Option<Vec<DocumentCorrespondent>>,
|
|
#[schema(nullable)]
|
|
pub current_version: Option<DocumentVersion>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct DocumentDetailResponse {
|
|
pub document: DocumentResponse,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct DocumentDownloadResponse {
|
|
pub url: String,
|
|
pub expires_in: u64,
|
|
pub filename: String,
|
|
#[schema(nullable)]
|
|
pub content_type: Option<String>,
|
|
pub size_bytes: i64,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct DocumentMetadataUpdate {
|
|
pub value: Value,
|
|
#[serde(default)]
|
|
#[schema(default = false)]
|
|
pub replace: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct RestoreDocumentRequest {
|
|
#[schema(nullable)]
|
|
pub folder_id: Option<Uuid>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct UpdateDocumentRequest {
|
|
#[schema(nullable)]
|
|
pub title: Option<String>,
|
|
#[schema(nullable, value_type = Option<String>)]
|
|
pub issued_at: Option<Value>,
|
|
#[schema(nullable)]
|
|
pub metadata: Option<DocumentMetadataUpdate>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct BulkMoveDocumentsRequest {
|
|
pub document_ids: Vec<Uuid>,
|
|
#[schema(nullable)]
|
|
pub folder_id: Option<Uuid>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct BulkMoveDocumentsResponse {
|
|
pub updated: usize,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct AssignTagsRequest {
|
|
pub tag_ids: Vec<Uuid>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct MoveDocumentRequest {
|
|
#[schema(nullable)]
|
|
pub folder_id: Option<Uuid>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum BulkTagAction {
|
|
Add,
|
|
Remove,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct BulkTagRequest {
|
|
pub document_ids: Vec<Uuid>,
|
|
pub tag_ids: Vec<Uuid>,
|
|
pub action: BulkTagAction,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct BulkTagResponse {
|
|
pub added: usize,
|
|
pub removed: usize,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct CorrespondentAssignment {
|
|
pub correspondent_id: Uuid,
|
|
pub role: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct AssignCorrespondentsRequest {
|
|
pub assignments: Vec<CorrespondentAssignment>,
|
|
#[serde(default)]
|
|
pub replace: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum BulkCorrespondentAction {
|
|
Add,
|
|
Remove,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct BulkCorrespondentsRequest {
|
|
pub document_ids: Vec<Uuid>,
|
|
pub assignments: Vec<CorrespondentAssignment>,
|
|
#[serde(default = "default_bulk_correspondent_action")]
|
|
pub action: BulkCorrespondentAction,
|
|
}
|
|
|
|
fn default_bulk_correspondent_action() -> BulkCorrespondentAction {
|
|
BulkCorrespondentAction::Add
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct BulkCorrespondentsResponse {
|
|
pub assigned: usize,
|
|
pub removed: usize,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, IntoParams, ToSchema)]
|
|
#[into_params(parameter_in = Query)]
|
|
pub struct RemoveCorrespondentParams {
|
|
pub role: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct ReanalyzeRequest {
|
|
pub document_ids: Vec<Uuid>,
|
|
#[serde(default)]
|
|
pub force: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct ReanalyzeResponse {
|
|
pub queued: usize,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, IntoParams, ToSchema)]
|
|
#[into_params(parameter_in = Query)]
|
|
pub struct DocumentAssetRequestParams {
|
|
#[serde(default)]
|
|
pub force: Option<bool>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, IntoParams, ToSchema)]
|
|
#[into_params(parameter_in = Query)]
|
|
pub struct AssetObjectsQuery {
|
|
#[serde(default)]
|
|
pub start: Option<i32>,
|
|
#[serde(default)]
|
|
pub limit: Option<i32>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, IntoParams, ToSchema)]
|
|
#[into_params(parameter_in = Query)]
|
|
pub struct DocumentCheckQuery {
|
|
pub checksum: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct DocumentCheckResponse {
|
|
pub exists: bool,
|
|
#[schema(nullable)]
|
|
pub document_id: Option<Uuid>,
|
|
#[schema(nullable)]
|
|
pub title: Option<String>,
|
|
#[schema(nullable)]
|
|
pub filename: Option<String>,
|
|
#[schema(nullable)]
|
|
pub version_id: Option<Uuid>,
|
|
#[schema(nullable)]
|
|
pub version_number: Option<i32>,
|
|
#[schema(nullable)]
|
|
pub uploaded_at: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct UploadDocumentForm {
|
|
#[schema(value_type = String, format = Binary)]
|
|
pub file: String,
|
|
#[schema(nullable)]
|
|
pub folder_id: Option<Uuid>,
|
|
#[schema(nullable)]
|
|
pub metadata: Option<Value>,
|
|
#[schema(nullable)]
|
|
pub title: Option<String>,
|
|
#[schema(nullable, value_type = Vec<Uuid>)]
|
|
pub tag_ids: Option<Vec<Uuid>>,
|
|
#[schema(nullable, value_type = Vec<CorrespondentAssignment>)]
|
|
pub correspondents: Option<Vec<CorrespondentAssignment>>,
|
|
#[schema(nullable, example = "2024-01-01T00:00:00Z")]
|
|
pub issued_at: Option<String>,
|
|
#[schema(nullable)]
|
|
pub skip_existing: Option<bool>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct CreateFolderRequest {
|
|
pub name: String,
|
|
#[schema(nullable)]
|
|
pub parent_id: Option<Uuid>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct EnsureFolderPathRequest {
|
|
#[schema(nullable)]
|
|
pub parent_id: Option<Uuid>,
|
|
pub segments: Vec<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct FolderInfo {
|
|
pub id: Uuid,
|
|
pub name: String,
|
|
#[schema(nullable)]
|
|
pub parent_id: Option<Uuid>,
|
|
pub created_at: String,
|
|
pub updated_at: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct FolderResponse {
|
|
pub folder: FolderInfo,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, IntoParams, ToSchema)]
|
|
#[into_params(parameter_in = Query)]
|
|
pub struct FolderContentsParams {
|
|
#[serde(default)]
|
|
pub include_documents: Option<bool>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct FolderDocumentSummary {
|
|
pub id: Uuid,
|
|
pub title: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct FolderContentsResponse {
|
|
#[schema(nullable)]
|
|
pub folder: Option<FolderInfo>,
|
|
pub subfolders: Vec<FolderInfo>,
|
|
pub documents: Vec<FolderDocumentSummary>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct UpdateFolderRequest {
|
|
#[schema(nullable)]
|
|
pub parent_id: Option<Option<Uuid>>,
|
|
#[schema(nullable)]
|
|
pub name: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct TagCatalogEntry {
|
|
pub id: Uuid,
|
|
pub label: String,
|
|
#[schema(nullable)]
|
|
pub color: Option<String>,
|
|
pub usage_count: i64,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct CreateTagRequest {
|
|
pub label: String,
|
|
#[schema(nullable)]
|
|
pub color: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct UpdateTagRequest {
|
|
#[schema(nullable)]
|
|
pub label: Option<Option<String>>,
|
|
#[schema(nullable)]
|
|
pub color: Option<Option<String>>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct CorrespondentCatalogEntry {
|
|
pub id: Uuid,
|
|
pub name: String,
|
|
pub metadata: Value,
|
|
pub role_counts: Value,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct CreateCorrespondentRequest {
|
|
pub name: String,
|
|
#[schema(default, value_type = Object)]
|
|
pub metadata: Value,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, ToSchema)]
|
|
pub struct UpdateCorrespondentRequest {
|
|
#[schema(nullable)]
|
|
pub name: Option<String>,
|
|
#[schema(nullable)]
|
|
pub metadata: Option<Value>,
|
|
}
|
|
}
|