use std::time::Duration; use actix_web::rt::time::Instant; use actix_web::{delete, get, patch, post, put, web, HttpResponse, Responder}; use actix_web_validator::{Json, Path, Query}; use collection::operations::cluster_ops::ClusterOperations; use collection::operations::verification::new_unchecked_verification_pass; use serde::Deserialize; use storage::content_manager::collection_meta_ops::{ ChangeAliasesOperation, CollectionMetaOperations, CreateCollection, CreateCollectionOperation, DeleteCollectionOperation, UpdateCollection, UpdateCollectionOperation, }; use storage::dispatcher::Dispatcher; use validator::Validate; use super::CollectionPath; use crate::actix::api::StrictCollectionPath; use crate::actix::auth::ActixAccess; use crate::actix::helpers::{self, process_response}; use crate::common::collections::*; #[derive(Debug, Deserialize, Validate)] pub struct WaitTimeout { #[validate(range(min = 1))] timeout: Option, } impl WaitTimeout { pub fn timeout(&self) -> Option { self.timeout.map(Duration::from_secs) } } #[get("/collections")] async fn get_collections( dispatcher: web::Data, ActixAccess(access): ActixAccess, ) -> HttpResponse { // No request to verify let pass = new_unchecked_verification_pass(); helpers::time(do_list_collections(dispatcher.toc(&access, &pass), access)).await } #[get("/aliases")] async fn get_aliases( dispatcher: web::Data, ActixAccess(access): ActixAccess, ) -> HttpResponse { // No request to verify let pass = new_unchecked_verification_pass(); helpers::time(do_list_aliases(dispatcher.toc(&access, &pass), access)).await } #[get("/collections/{name}")] async fn get_collection( dispatcher: web::Data, collection: Path, ActixAccess(access): ActixAccess, ) -> HttpResponse { // No request to verify let pass = new_unchecked_verification_pass(); helpers::time(do_get_collection( dispatcher.toc(&access, &pass), access, &collection.name, None, )) .await } #[get("/collections/{name}/exists")] async fn get_collection_existence( dispatcher: web::Data, collection: Path, ActixAccess(access): ActixAccess, ) -> HttpResponse { // No request to verify let pass = new_unchecked_verification_pass(); helpers::time(do_collection_exists( dispatcher.toc(&access, &pass), access, &collection.name, )) .await } #[get("/collections/{name}/aliases")] async fn get_collection_aliases( dispatcher: web::Data, collection: Path, ActixAccess(access): ActixAccess, ) -> HttpResponse { // No request to verify let pass = new_unchecked_verification_pass(); helpers::time(do_list_collection_aliases( dispatcher.toc(&access, &pass), access, &collection.name, )) .await } #[put("/collections/{name}")] async fn create_collection( dispatcher: web::Data, collection: Path, operation: Json, Query(query): Query, ActixAccess(access): ActixAccess, ) -> HttpResponse { helpers::time(dispatcher.submit_collection_meta_op( CollectionMetaOperations::CreateCollection(CreateCollectionOperation::new( collection.name.clone(), operation.into_inner(), )), access, query.timeout(), )) .await } #[patch("/collections/{name}")] async fn update_collection( dispatcher: web::Data, collection: Path, operation: Json, Query(query): Query, ActixAccess(access): ActixAccess, ) -> impl Responder { let timing = Instant::now(); let name = collection.name.clone(); let response = dispatcher .submit_collection_meta_op( CollectionMetaOperations::UpdateCollection(UpdateCollectionOperation::new( name, operation.into_inner(), )), access, query.timeout(), ) .await; process_response(response, timing, None) } #[delete("/collections/{name}")] async fn delete_collection( dispatcher: web::Data, collection: Path, Query(query): Query, ActixAccess(access): ActixAccess, ) -> impl Responder { let timing = Instant::now(); let response = dispatcher .submit_collection_meta_op( CollectionMetaOperations::DeleteCollection(DeleteCollectionOperation( collection.name.clone(), )), access, query.timeout(), ) .await; process_response(response, timing, None) } #[post("/collections/aliases")] async fn update_aliases( dispatcher: web::Data, operation: Json, Query(query): Query, ActixAccess(access): ActixAccess, ) -> impl Responder { let timing = Instant::now(); let response = dispatcher .submit_collection_meta_op( CollectionMetaOperations::ChangeAliases(operation.0), access, query.timeout(), ) .await; process_response(response, timing, None) } #[get("/collections/{name}/cluster")] async fn get_cluster_info( dispatcher: web::Data, collection: Path, ActixAccess(access): ActixAccess, ) -> impl Responder { // No request to verify let pass = new_unchecked_verification_pass(); helpers::time(do_get_collection_cluster( dispatcher.toc(&access, &pass), access, &collection.name, )) .await } #[post("/collections/{name}/cluster")] async fn update_collection_cluster( dispatcher: web::Data, collection: Path, operation: Json, Query(query): Query, ActixAccess(access): ActixAccess, ) -> impl Responder { let timing = Instant::now(); let wait_timeout = query.timeout(); let response = do_update_collection_cluster( &dispatcher.into_inner(), collection.name.clone(), operation.0, access, wait_timeout, ) .await; process_response(response, timing, None) } // Configure services pub fn config_collections_api(cfg: &mut web::ServiceConfig) { // Ordering of services is important for correct path pattern matching // See: cfg.service(update_aliases) .service(get_collections) .service(get_collection) .service(get_collection_existence) .service(create_collection) .service(update_collection) .service(delete_collection) .service(get_aliases) .service(get_collection_aliases) .service(get_cluster_info) .service(update_collection_cluster); } #[cfg(test)] mod tests { use actix_web::web::Query; use super::WaitTimeout; #[test] fn timeout_is_deserialized() { let timeout: WaitTimeout = Query::from_query("").unwrap().0; assert!(timeout.timeout.is_none()); let timeout: WaitTimeout = Query::from_query("timeout=10").unwrap().0; assert_eq!(timeout.timeout, Some(10)) } }