Files
papercrate/backend/src/openapi.rs
T
2025-10-27 22:45:16 +01:00

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>,
}
}