diff --git a/meilisearch-error/src/lib.rs b/meilisearch-error/src/lib.rs index 1e71e1f78..4efba3cd9 100644 --- a/meilisearch-error/src/lib.rs +++ b/meilisearch-error/src/lib.rs @@ -84,6 +84,7 @@ pub enum Code { DumpAlreadyInProgress, DumpProcessFailed, + InvalidContentType, MissingContentType, MalformedPayload, } @@ -158,7 +159,8 @@ impl Code { ErrCode::internal("dump_process_failed", StatusCode::INTERNAL_SERVER_ERROR) } MissingContentType => ErrCode::invalid("missing_content_type", StatusCode::UNSUPPORTED_MEDIA_TYPE), - MalformedPayload => ErrCode::invalid("malformed_payload", StatusCode::BAD_REQUEST) + MalformedPayload => ErrCode::invalid("malformed_payload", StatusCode::BAD_REQUEST), + InvalidContentType => ErrCode::invalid("invalid_content_type", StatusCode::UNSUPPORTED_MEDIA_TYPE), } } diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index bb0b06e27..fb9847b87 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -13,12 +13,15 @@ use serde::{Deserialize, Serialize}; pub enum MeilisearchHttpError { #[error("A Content-Type header is missing. Accepted values for the Content-Type header are: \"application/json\", \"application/x-ndjson\", \"test/csv\"")] MissingContentType, + #[error("The Content-Type \"{0}\" is invalid. Accepted values for the Content-Type header are: \"application/json\", \"application/x-ndjson\", \"test/csv\"")] + InvalidContentType(String), } impl ErrorCode for MeilisearchHttpError { fn error_code(&self) -> Code { match self { MeilisearchHttpError::MissingContentType => Code::MissingContentType, + MeilisearchHttpError::InvalidContentType(_) => Code::InvalidContentType, } } } diff --git a/meilisearch-http/src/routes/indexes/documents.rs b/meilisearch-http/src/routes/indexes/documents.rs index ca17efad7..63318c676 100644 --- a/meilisearch-http/src/routes/indexes/documents.rs +++ b/meilisearch-http/src/routes/indexes/documents.rs @@ -1,6 +1,6 @@ use actix_web::error::PayloadError; use actix_web::web::Bytes; -use actix_web::{web, HttpResponse}; +use actix_web::{web, HttpRequest, HttpResponse}; use futures::{Stream, StreamExt}; use log::debug; use meilisearch_lib::index_controller::{DocumentAdditionFormat, Update}; @@ -18,29 +18,6 @@ use crate::routes::IndexParam; const DEFAULT_RETRIEVE_DOCUMENTS_OFFSET: usize = 0; const DEFAULT_RETRIEVE_DOCUMENTS_LIMIT: usize = 20; -macro_rules! guard_content_type { - ($fn_name:ident, $guard_value:literal) => { - fn $fn_name(head: &actix_web::dev::RequestHead) -> bool { - if let Some(content_type) = head.headers.get("Content-Type") { - content_type - .to_str() - .map(|v| v.contains($guard_value)) - .unwrap_or(false) - } else { - false - } - } - }; -} - -guard_content_type!(guard_ndjson, "application/x-ndjson"); -guard_content_type!(guard_csv, "text/csv"); -guard_content_type!(guard_json, "application/json"); - -fn empty_application_type(head: &actix_web::dev::RequestHead) -> bool { - head.headers.get("Content-Type").is_none() -} - /// This is required because Payload is not Sync nor Send fn payload_to_stream(mut payload: Payload) -> impl Stream> { let (snd, recv) = mpsc::channel(1); @@ -62,26 +39,8 @@ pub fn configure(cfg: &mut web::ServiceConfig) { cfg.service( web::resource("") .route(web::get().to(get_all_documents)) - // replace documents routes - .route( - web::post() - .guard(empty_application_type) - .to(missing_content_type_error), - ) - .route(web::post().guard(guard_json).to(add_documents_json)) - .route(web::post().guard(guard_ndjson).to(add_documents_ndjson)) - .route(web::post().guard(guard_csv).to(add_documents_csv)) - .route(web::post().to(HttpResponse::UnsupportedMediaType)) - // update documents routes - .route( - web::put() - .guard(empty_application_type) - .to(missing_content_type_error), - ) - .route(web::put().guard(guard_json).to(update_documents_json)) - .route(web::put().guard(guard_ndjson).to(update_documents_ndjson)) - .route(web::put().guard(guard_csv).to(update_documents_csv)) - .route(web::put().to(HttpResponse::UnsupportedMediaType)) + .route(web::post().to(add_documents)) + .route(web::put().to(update_documents)) .route(web::delete().to(clear_all_documents)), ) // this route needs to be before the /documents/{document_id} to match properly @@ -93,10 +52,6 @@ pub fn configure(cfg: &mut web::ServiceConfig) { ); } -async fn missing_content_type_error() -> Result { - Err(MeilisearchHttpError::MissingContentType.into()) -} - pub async fn get_document( meilisearch: GuardedData, path: web::Path, @@ -169,126 +124,69 @@ pub struct UpdateDocumentsQuery { primary_key: Option, } -pub async fn add_documents_json( +pub async fn add_documents( meilisearch: GuardedData, path: web::Path, params: web::Query, body: Payload, + req: HttpRequest, ) -> Result { + debug!("called with params: {:?}", params); document_addition( + req.headers().get("Content-type").map(|s| s.to_str().unwrap_or("unkown")), meilisearch, - path, - params, + path.into_inner().index_uid, + params.into_inner().primary_key, body, - DocumentAdditionFormat::Json, - IndexDocumentsMethod::ReplaceDocuments, - ) - .await + IndexDocumentsMethod::ReplaceDocuments) + .await } -pub async fn add_documents_ndjson( +pub async fn update_documents( meilisearch: GuardedData, path: web::Path, params: web::Query, body: Payload, + req: HttpRequest, ) -> Result { + debug!("called with params: {:?}", params); document_addition( + req.headers().get("Content-type").map(|s| s.to_str().unwrap_or("unkown")), meilisearch, - path, - params, + path.into_inner().index_uid, + params.into_inner().primary_key, body, - DocumentAdditionFormat::Ndjson, - IndexDocumentsMethod::ReplaceDocuments, - ) - .await + IndexDocumentsMethod::UpdateDocuments) + .await } -pub async fn add_documents_csv( - meilisearch: GuardedData, - path: web::Path, - params: web::Query, - body: Payload, -) -> Result { - document_addition( - meilisearch, - path, - params, - body, - DocumentAdditionFormat::Csv, - IndexDocumentsMethod::ReplaceDocuments, - ) - .await -} - -pub async fn update_documents_json( - meilisearch: GuardedData, - path: web::Path, - params: web::Query, - body: Payload, -) -> Result { - document_addition( - meilisearch, - path, - params, - body, - DocumentAdditionFormat::Json, - IndexDocumentsMethod::UpdateDocuments, - ) - .await -} - -pub async fn update_documents_ndjson( - meilisearch: GuardedData, - path: web::Path, - params: web::Query, - body: Payload, -) -> Result { - document_addition( - meilisearch, - path, - params, - body, - DocumentAdditionFormat::Ndjson, - IndexDocumentsMethod::UpdateDocuments, - ) - .await -} - -pub async fn update_documents_csv( - meilisearch: GuardedData, - path: web::Path, - params: web::Query, - body: Payload, -) -> Result { - document_addition( - meilisearch, - path, - params, - body, - DocumentAdditionFormat::Csv, - IndexDocumentsMethod::UpdateDocuments, - ) - .await -} /// Route used when the payload type is "application/json" /// Used to add or replace documents async fn document_addition( + content_type: Option<&str>, meilisearch: GuardedData, - path: web::Path, - params: web::Query, + index_uid: String, + primary_key: Option, body: Payload, - format: DocumentAdditionFormat, method: IndexDocumentsMethod, ) -> Result { - debug!("called with params: {:?}", params); + let format = match content_type { + Some("application/json") => DocumentAdditionFormat::Json, + Some("application/x-ndjson") => DocumentAdditionFormat::Ndjson, + Some("text/csv") => DocumentAdditionFormat::Csv, + Some(other) => return Err(MeilisearchHttpError::InvalidContentType(other.to_string()).into()), + None => return Err(MeilisearchHttpError::MissingContentType.into()), + }; + let update = Update::DocumentAddition { payload: Box::new(payload_to_stream(body)), - primary_key: params.primary_key.clone(), + primary_key, method, format, }; + let update_status = meilisearch - .register_update(path.into_inner().index_uid, update, true) + .register_update(index_uid, update, true) .await?; debug!("returns: {:?}", update_status); diff --git a/meilisearch-http/src/routes/mod.rs b/meilisearch-http/src/routes/mod.rs index 9c0e0bed1..382147f31 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -361,10 +361,8 @@ mod test { indexes::documents::clear_all_documents, indexes::documents::delete_documents, - indexes::documents::update_documents_json, - indexes::documents::update_documents_csv, - indexes::documents::add_documents_json, - indexes::documents::add_documents_csv, + indexes::documents::update_documents, + indexes::documents::add_documents, indexes::documents::delete_document, indexes::updates::get_all_updates_status,