diff --git a/meilisearch-http/tests/documents/add_documents.rs b/meilisearch-http/tests/documents/add_documents.rs index c684458c5..8b91997d4 100644 --- a/meilisearch-http/tests/documents/add_documents.rs +++ b/meilisearch-http/tests/documents/add_documents.rs @@ -212,7 +212,7 @@ async fn error_add_malformed_csv_documents() { assert_eq!( response["message"], json!( - r#"The `csv` payload provided is malformed. `CSV error: record 1 (line: 2, byte: 12): found record with 3 fields, but the previous record has 2 fields`."# + r#"The `csv` payload provided is malformed: `CSV error: record 1 (line: 2, byte: 12): found record with 3 fields, but the previous record has 2 fields`."# ) ); assert_eq!(response["code"], json!("malformed_payload")); @@ -236,7 +236,7 @@ async fn error_add_malformed_csv_documents() { assert_eq!( response["message"], json!( - r#"The `csv` payload provided is malformed. `CSV error: record 1 (line: 2, byte: 12): found record with 3 fields, but the previous record has 2 fields`."# + r#"The `csv` payload provided is malformed: `CSV error: record 1 (line: 2, byte: 12): found record with 3 fields, but the previous record has 2 fields`."# ) ); assert_eq!(response["code"], json!("malformed_payload")); @@ -307,6 +307,58 @@ async fn error_add_malformed_json_documents() { response["link"], json!("https://docs.meilisearch.com/errors#malformed_payload") ); + + // truncate + + // length = 100 + let long = "0123456789".repeat(10); + + let document = format!("\"{}\"", long); + let req = test::TestRequest::put() + .uri("/indexes/dog/documents") + .set_payload(document) + .insert_header(("content-type", "application/json")) + .to_request(); + let res = test::call_service(&app, req).await; + let body = test::read_body(res).await; + let response: Value = serde_json::from_slice(&body).unwrap_or_default(); + assert_eq!(status_code, 400); + assert_eq!( + response["message"], + json!( + r#"The `json` payload provided is malformed. `Couldn't serialize document value: invalid type: string "0123456789012345678901234567...890123456789", expected a documents, or a sequence of documents. at line 1 column 102`."# + ) + ); + assert_eq!(response["code"], json!("malformed_payload")); + assert_eq!(response["type"], json!("invalid_request")); + assert_eq!( + response["link"], + json!("https://docs.meilisearch.com/errors#malformed_payload") + ); + + // add one more char to the long string to test if the truncating works. + let document = format!("\"{}m\"", long); + let req = test::TestRequest::put() + .uri("/indexes/dog/documents") + .set_payload(document) + .insert_header(("content-type", "application/json")) + .to_request(); + let res = test::call_service(&app, req).await; + let body = test::read_body(res).await; + let response: Value = serde_json::from_slice(&body).unwrap_or_default(); + assert_eq!(status_code, 400); + assert_eq!( + response["message"], + json!( + r#"The `json` payload provided is malformed. `Couldn't serialize document value: invalid type: string "0123456789012345678901234567...90123456789m", expected a documents, or a sequence of documents. at line 1 column 103`."# + ) + ); + assert_eq!(response["code"], json!("malformed_payload")); + assert_eq!(response["type"], json!("invalid_request")); + assert_eq!( + response["link"], + json!("https://docs.meilisearch.com/errors#malformed_payload") + ); } #[actix_rt::test] diff --git a/meilisearch-lib/src/document_formats.rs b/meilisearch-lib/src/document_formats.rs index cfd73038e..93c47afe8 100644 --- a/meilisearch-lib/src/document_formats.rs +++ b/meilisearch-lib/src/document_formats.rs @@ -1,4 +1,5 @@ -use std::fmt; +use std::borrow::Borrow; +use std::fmt::{self, Debug, Display}; use std::io::{self, BufRead, BufReader, BufWriter, Cursor, Read, Seek, Write}; use meilisearch_error::{internal_error, Code, ErrorCode}; @@ -23,17 +24,40 @@ impl fmt::Display for PayloadType { } } -#[derive(thiserror::Error, Debug)] +#[derive(Debug)] pub enum DocumentFormatError { - #[error("An internal error has occurred. `{0}`.")] Internal(Box), - #[error("The `{1}` payload provided is malformed. `{0}`.")] - MalformedPayload( - Box, - PayloadType, - ), + MalformedPayload(Box, PayloadType), } +impl Display for DocumentFormatError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Internal(e) => write!(f, "An internal error has occurred: `{}`.", e), + Self::MalformedPayload(me, b) => match me.borrow() { + milli::documents::Error::JsonError(se) => { + // https://github.com/meilisearch/meilisearch/issues/2107 + // The user input maybe insanely long. We need to truncate it. + let mut serde_msg = se.to_string(); + let ellipsis = "..."; + if serde_msg.len() > 100 + ellipsis.len() { + serde_msg.replace_range(50..serde_msg.len() - 85, ellipsis); + } + + write!( + f, + "The `{}` payload provided is malformed. `Couldn't serialize document value: {}`.", + b, serde_msg + ) + } + _ => write!(f, "The `{}` payload provided is malformed: `{}`.", b, me), + }, + } + } +} + +impl std::error::Error for DocumentFormatError {} + impl From<(PayloadType, milli::documents::Error)> for DocumentFormatError { fn from((ty, error): (PayloadType, milli::documents::Error)) -> Self { match error {