Implements the get and delete tasks route

This commit is contained in:
Tamo 2024-08-08 19:14:19 +02:00
parent f00a285a6d
commit 742d0ee531
No known key found for this signature in database
GPG key ID: 20CD8020AFA88D69
25 changed files with 1787 additions and 85 deletions

View file

@ -1,20 +1,38 @@
use std::collections::BTreeMap;
use actix_web::web::Data;
use actix_web::{web, HttpRequest, HttpResponse};
use index_scheduler::IndexScheduler;
use meilisearch_auth::AuthController;
use meilisearch_types::error::{Code, ResponseError};
use meilisearch_types::settings::{Settings, Unchecked};
use meilisearch_types::tasks::{Kind, Status, Task, TaskId};
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use tracing::debug;
use crate::extractors::authentication::policies::*;
use crate::extractors::authentication::GuardedData;
use crate::search_queue::SearchQueue;
use crate::Opt;
use actix_web::web::Data;
use actix_web::{web, HttpRequest, HttpResponse};
use index_scheduler::IndexScheduler;
use meilisearch_auth::AuthController;
use meilisearch_types::error::{Code, ErrorType, ResponseError};
use meilisearch_types::index_uid::IndexUid;
use meilisearch_types::keys::CreateApiKey;
use meilisearch_types::settings::{
Checked, FacetingSettings, MinWordSizeTyposSetting, PaginationSettings, Settings, TypoSettings,
Unchecked,
};
use meilisearch_types::task_view::{DetailsView, TaskView};
use meilisearch_types::tasks::{Kind, Status, Task, TaskId};
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use tracing::debug;
use utoipa::{OpenApi, ToSchema};
use utoipa_rapidoc::RapiDoc;
use utoipa_redoc::{Redoc, Servable};
use utoipa_scalar::{Scalar, Servable as ScalarServable};
use self::api_key::KeyView;
use self::indexes::documents::BrowseQuery;
use self::indexes::{IndexCreateRequest, IndexStats, UpdateIndexRequest};
use self::logs::GetLogs;
use self::logs::LogMode;
use self::logs::UpdateStderrLogs;
use self::open_api_utils::OpenApiAuth;
use self::tasks::AllTasks;
const PAGINATION_DEFAULT_LIMIT: usize = 20;
@ -27,24 +45,50 @@ mod logs;
mod metrics;
mod multi_search;
mod multi_search_analytics;
mod open_api_utils;
mod snapshot;
mod swap_indexes;
pub mod tasks;
#[derive(OpenApi)]
#[openapi(
nest(
(path = "/tasks", api = tasks::TaskApi),
(path = "/indexes", api = indexes::IndexesApi),
(path = "/snapshots", api = snapshot::SnapshotApi),
(path = "/dumps", api = dump::DumpApi),
(path = "/keys", api = api_key::ApiKeyApi),
(path = "/metrics", api = metrics::MetricApi),
(path = "/logs", api = logs::LogsApi),
),
paths(get_health, get_version, get_stats),
tags(
(name = "Stats", description = "Stats gives extended information and metrics about indexes and the Meilisearch database."),
),
modifiers(&OpenApiAuth),
components(schemas(BrowseQuery, UpdateIndexRequest, IndexUid, IndexCreateRequest, KeyView, Action, CreateApiKey, UpdateStderrLogs, LogMode, GetLogs, IndexStats, Stats, HealthStatus, HealthResponse, VersionResponse, Code, ErrorType, AllTasks, TaskView, Status, DetailsView, ResponseError, Settings<Unchecked>, Settings<Checked>, TypoSettings, MinWordSizeTyposSetting, FacetingSettings, PaginationSettings, SummarizedTaskView, Kind))
)]
pub struct MeilisearchApi;
pub fn configure(cfg: &mut web::ServiceConfig) {
cfg.service(web::scope("/tasks").configure(tasks::configure))
.service(web::scope("/batches").configure(batches::configure))
.service(web::resource("/health").route(web::get().to(get_health)))
.service(web::scope("/logs").configure(logs::configure))
.service(web::scope("/keys").configure(api_key::configure))
.service(web::scope("/dumps").configure(dump::configure))
.service(web::scope("/snapshots").configure(snapshot::configure))
.service(web::resource("/stats").route(web::get().to(get_stats)))
.service(web::resource("/version").route(web::get().to(get_version)))
.service(web::scope("/indexes").configure(indexes::configure))
.service(web::scope("/multi-search").configure(multi_search::configure))
.service(web::scope("/swap-indexes").configure(swap_indexes::configure))
.service(web::scope("/metrics").configure(metrics::configure))
let openapi = MeilisearchApi::openapi();
cfg.service(web::scope("/tasks").configure(tasks::configure)) // done
.service(web::scope("/batches").configure(batches::configure)) // TODO
.service(Scalar::with_url("/scalar", openapi.clone())) // done
.service(RapiDoc::with_openapi("/api-docs/openapi.json", openapi.clone()).path("/rapidoc")) // done
.service(Redoc::with_url("/redoc", openapi)) // done
.service(web::resource("/health").route(web::get().to(get_health))) // done
.service(web::scope("/logs").configure(logs::configure)) // done
.service(web::scope("/keys").configure(api_key::configure)) // done
.service(web::scope("/dumps").configure(dump::configure)) // done
.service(web::scope("/snapshots").configure(snapshot::configure)) // done
.service(web::resource("/stats").route(web::get().to(get_stats))) // done
.service(web::resource("/version").route(web::get().to(get_version))) // done
.service(web::scope("/indexes").configure(indexes::configure)) // WIP
.service(web::scope("/multi-search").configure(multi_search::configure)) // TODO
.service(web::scope("/swap-indexes").configure(swap_indexes::configure)) // TODO
.service(web::scope("/metrics").configure(metrics::configure)) // done
.service(web::scope("/experimental-features").configure(features::configure));
}
@ -98,14 +142,20 @@ pub fn is_dry_run(req: &HttpRequest, opt: &Opt) -> Result<bool, ResponseError> {
.map_or(false, |s| s.to_lowercase() == "true"))
}
#[derive(Debug, Serialize)]
#[derive(Debug, Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct SummarizedTaskView {
/// The task unique identifier.
#[schema(value_type = u32)]
task_uid: TaskId,
/// The index affected by this task. May be `null` if the task is not linked to any index.
index_uid: Option<String>,
/// The status of the task.
status: Status,
/// The type of the task.
#[serde(rename = "type")]
kind: Kind,
/// The date on which the task was enqueued.
#[serde(serialize_with = "time::serde::rfc3339::serialize")]
enqueued_at: OffsetDateTime,
}
@ -128,6 +178,7 @@ pub struct Pagination {
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PaginationView<T> {
pub results: Vec<T>,
pub offset: usize,
@ -283,17 +334,56 @@ pub async fn running() -> HttpResponse {
HttpResponse::Ok().json(serde_json::json!({ "status": "Meilisearch is running" }))
}
#[derive(Serialize, Debug)]
#[derive(Serialize, Debug, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct Stats {
/// The size of the database, in bytes.
pub database_size: u64,
#[serde(skip)]
pub used_database_size: u64,
/// The date of the last update in the RFC 3339 formats. Can be `null` if no update has ever been processed.
#[serde(serialize_with = "time::serde::rfc3339::option::serialize")]
pub last_update: Option<OffsetDateTime>,
/// The stats of every individual index your API key lets you access.
#[schema(value_type = HashMap<String, indexes::IndexStats>)]
pub indexes: BTreeMap<String, indexes::IndexStats>,
}
/// Get stats of all indexes.
///
/// Get stats of all indexes.
#[utoipa::path(
get,
path = "/stats",
tag = "Stats",
security(("Bearer" = ["stats.get", "stats.*", "*"])),
responses(
(status = 200, description = "The stats of the instance", body = Stats, content_type = "application/json", example = json!(
{
"databaseSize": 567,
"lastUpdate": "2019-11-20T09:40:33.711324Z",
"indexes": {
"movies": {
"numberOfDocuments": 10,
"isIndexing": true,
"fieldDistribution": {
"genre": 10,
"author": 9
}
}
}
}
)),
(status = 401, description = "The authorization header is missing", body = ResponseError, content_type = "application/json", example = json!(
{
"message": "The Authorization header is missing. It must use the bearer authorization method.",
"code": "missing_authorization_header",
"type": "auth",
"link": "https://docs.meilisearch.com/errors#missing_authorization_header"
}
)),
)
)]
async fn get_stats(
index_scheduler: GuardedData<ActionPolicy<{ actions::STATS_GET }>, Data<IndexScheduler>>,
auth_controller: GuardedData<ActionPolicy<{ actions::STATS_GET }>, Data<AuthController>>,
@ -343,14 +433,43 @@ pub fn create_all_stats(
Ok(stats)
}
#[derive(Serialize)]
#[derive(Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
struct VersionResponse {
/// The commit used to compile this build of Meilisearch.
commit_sha: String,
/// The date of this build.
commit_date: String,
/// The version of Meilisearch.
pkg_version: String,
}
/// Get version
///
/// Current version of Meilisearch.
#[utoipa::path(
get,
path = "/version",
tag = "Version",
security(("Bearer" = ["version", "*"])),
responses(
(status = 200, description = "Instance is healthy", body = VersionResponse, content_type = "application/json", example = json!(
{
"commitSha": "b46889b5f0f2f8b91438a08a358ba8f05fc09fc1",
"commitDate": "2021-07-08",
"pkgVersion": "0.23.0"
}
)),
(status = 401, description = "The authorization header is missing", body = ResponseError, content_type = "application/json", example = json!(
{
"message": "The Authorization header is missing. It must use the bearer authorization method.",
"code": "missing_authorization_header",
"type": "auth",
"link": "https://docs.meilisearch.com/errors#missing_authorization_header"
}
)),
)
)]
async fn get_version(
_index_scheduler: GuardedData<ActionPolicy<{ actions::VERSION }>, Data<IndexScheduler>>,
) -> HttpResponse {
@ -370,6 +489,35 @@ async fn get_version(
})
}
#[derive(Default, Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
struct HealthResponse {
/// The status of the instance.
status: HealthStatus,
}
#[derive(Default, Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
enum HealthStatus {
#[default]
Available,
}
/// Get Health
///
/// The health check endpoint enables you to periodically test the health of your Meilisearch instance.
#[utoipa::path(
get,
path = "/health",
tag = "Health",
responses(
(status = 200, description = "Instance is healthy", body = HealthResponse, content_type = "application/json", example = json!(
{
"status": "available"
}
)),
)
)]
pub async fn get_health(
index_scheduler: Data<IndexScheduler>,
auth_controller: Data<AuthController>,
@ -379,5 +527,5 @@ pub async fn get_health(
index_scheduler.health().unwrap();
auth_controller.health().unwrap();
Ok(HttpResponse::Ok().json(serde_json::json!({ "status": "available" })))
Ok(HttpResponse::Ok().json(&HealthResponse::default()))
}