diff --git a/Cargo.lock b/Cargo.lock index 073411f95..b15b69a6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2006,7 +2006,7 @@ dependencies = [ "base64", "enum-iterator", "hmac", - "meilisearch-error", + "meilisearch-types", "milli", "rand", "serde", @@ -2017,17 +2017,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "meilisearch-error" -version = "0.28.0" -dependencies = [ - "actix-web", - "proptest", - "proptest-derive", - "serde", - "serde_json", -] - [[package]] name = "meilisearch-http" version = "0.28.0" @@ -2061,8 +2050,8 @@ dependencies = [ "manifest-dir-macros", "maplit", "meilisearch-auth", - "meilisearch-error", "meilisearch-lib", + "meilisearch-types", "mime", "num_cpus", "obkv", @@ -2129,7 +2118,7 @@ dependencies = [ "lazy_static", "log", "meilisearch-auth", - "meilisearch-error", + "meilisearch-types", "milli", "mime", "mockall", @@ -2163,6 +2152,17 @@ dependencies = [ "whoami", ] +[[package]] +name = "meilisearch-types" +version = "0.28.0" +dependencies = [ + "actix-web", + "proptest", + "proptest-derive", + "serde", + "serde_json", +] + [[package]] name = "memchr" version = "2.5.0" diff --git a/Cargo.toml b/Cargo.toml index 33b961446..99ec43528 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ resolver = "2" members = [ "meilisearch-http", - "meilisearch-error", + "meilisearch-types", "meilisearch-lib", "meilisearch-auth", "permissive-json-pointer", diff --git a/meilisearch-auth/Cargo.toml b/meilisearch-auth/Cargo.toml index 2174b607c..8f00f3c99 100644 --- a/meilisearch-auth/Cargo.toml +++ b/meilisearch-auth/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" base64 = "0.13.0" enum-iterator = "0.7.0" hmac = "0.12.1" -meilisearch-error = { path = "../meilisearch-error" } +meilisearch-types = { path = "../meilisearch-types" } milli = { git = "https://github.com/meilisearch/milli.git", tag = "v0.29.2" } rand = "0.8.4" serde = { version = "1.0.136", features = ["derive"] } diff --git a/meilisearch-auth/src/error.rs b/meilisearch-auth/src/error.rs index 8be97daaf..bb96be789 100644 --- a/meilisearch-auth/src/error.rs +++ b/meilisearch-auth/src/error.rs @@ -1,7 +1,7 @@ use std::error::Error; -use meilisearch_error::ErrorCode; -use meilisearch_error::{internal_error, Code}; +use meilisearch_types::error::{Code, ErrorCode}; +use meilisearch_types::internal_error; use serde_json::Value; pub type Result = std::result::Result; diff --git a/meilisearch-auth/src/key.rs b/meilisearch-auth/src/key.rs index 0e336a7db..eb72aaa72 100644 --- a/meilisearch-auth/src/key.rs +++ b/meilisearch-auth/src/key.rs @@ -2,6 +2,8 @@ use crate::action::Action; use crate::error::{AuthControllerError, Result}; use crate::store::KeyId; +use meilisearch_types::index_uid::IndexUid; +use meilisearch_types::star_or::StarOr; use serde::{Deserialize, Serialize}; use serde_json::{from_value, Value}; use time::format_description::well_known::Rfc3339; @@ -17,7 +19,7 @@ pub struct Key { pub name: Option, pub uid: KeyId, pub actions: Vec, - pub indexes: Vec, + pub indexes: Vec>, #[serde(with = "time::serde::rfc3339::option")] pub expires_at: Option, #[serde(with = "time::serde::rfc3339")] @@ -136,7 +138,7 @@ impl Key { description: Some("Use it for anything that is not a search operation. Caution! Do not expose it on a public frontend".to_string()), uid, actions: vec![Action::All], - indexes: vec!["*".to_string()], + indexes: vec![StarOr::Star], expires_at: None, created_at: now, updated_at: now, @@ -151,7 +153,7 @@ impl Key { description: Some("Use it to search from the frontend".to_string()), uid, actions: vec![Action::Search], - indexes: vec!["*".to_string()], + indexes: vec![StarOr::Star], expires_at: None, created_at: now, updated_at: now, diff --git a/meilisearch-auth/src/lib.rs b/meilisearch-auth/src/lib.rs index e41fd92f4..81443348a 100644 --- a/meilisearch-auth/src/lib.rs +++ b/meilisearch-auth/src/lib.rs @@ -5,6 +5,7 @@ mod key; mod store; use std::collections::{HashMap, HashSet}; +use std::ops::Deref; use std::path::Path; use std::sync::Arc; @@ -16,6 +17,7 @@ use uuid::Uuid; pub use action::{actions, Action}; use error::{AuthControllerError, Result}; pub use key::Key; +use meilisearch_types::star_or::StarOr; use store::generate_key_as_base64; pub use store::open_auth_store_env; use store::HeedAuthStore; @@ -87,20 +89,22 @@ impl AuthController { .get_api_key(uid)? .ok_or_else(|| AuthControllerError::ApiKeyNotFound(uid.to_string()))?; - if !key.indexes.iter().any(|i| i.as_str() == "*") { + if !key.indexes.iter().any(|i| i == &StarOr::Star) { filters.search_rules = match search_rules { // Intersect search_rules with parent key authorized indexes. Some(search_rules) => SearchRules::Map( key.indexes .into_iter() .filter_map(|index| { - search_rules - .get_index_search_rules(&index) - .map(|index_search_rules| (index, Some(index_search_rules))) + search_rules.get_index_search_rules(index.deref()).map( + |index_search_rules| { + (String::from(index), Some(index_search_rules)) + }, + ) }) .collect(), ), - None => SearchRules::Set(key.indexes.into_iter().collect()), + None => SearchRules::Set(key.indexes.into_iter().map(String::from).collect()), }; } else if let Some(search_rules) = search_rules { filters.search_rules = search_rules; diff --git a/meilisearch-auth/src/store.rs b/meilisearch-auth/src/store.rs index d1af1b4ab..0355c4579 100644 --- a/meilisearch-auth/src/store.rs +++ b/meilisearch-auth/src/store.rs @@ -3,12 +3,14 @@ use std::cmp::Reverse; use std::convert::TryFrom; use std::convert::TryInto; use std::fs::create_dir_all; +use std::ops::Deref; use std::path::Path; use std::str; use std::sync::Arc; use enum_iterator::IntoEnumIterator; use hmac::{Hmac, Mac}; +use meilisearch_types::star_or::StarOr; use milli::heed::types::{ByteSlice, DecodeIgnore, SerdeJson}; use milli::heed::{Database, Env, EnvOpenOptions, RwTxn}; use sha2::{Digest, Sha256}; @@ -92,7 +94,7 @@ impl HeedAuthStore { key.actions.clone() }; - let no_index_restriction = key.indexes.contains(&"*".to_owned()); + let no_index_restriction = key.indexes.contains(&StarOr::Star); for action in actions { if no_index_restriction { // If there is no index restriction we put None. @@ -102,7 +104,7 @@ impl HeedAuthStore { for index in key.indexes.iter() { db.put( &mut wtxn, - &(&uid, &action, Some(index.as_bytes())), + &(&uid, &action, Some(index.deref().as_bytes())), &key.expires_at, )?; } diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index 53ff0b665..763337888 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -45,7 +45,7 @@ itertools = "0.10.3" jsonwebtoken = "8.0.1" log = "0.4.14" meilisearch-auth = { path = "../meilisearch-auth" } -meilisearch-error = { path = "../meilisearch-error" } +meilisearch-types = { path = "../meilisearch-types" } meilisearch-lib = { path = "../meilisearch-lib" } mime = "0.3.16" num_cpus = "1.13.1" diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index b2b6c1b3c..86b7c1964 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -1,6 +1,6 @@ use actix_web as aweb; use aweb::error::{JsonPayloadError, QueryPayloadError}; -use meilisearch_error::{Code, ErrorCode, ResponseError}; +use meilisearch_types::error::{Code, ErrorCode, ResponseError}; #[derive(Debug, thiserror::Error)] pub enum MeilisearchHttpError { diff --git a/meilisearch-http/src/extractors/authentication/error.rs b/meilisearch-http/src/extractors/authentication/error.rs index 6d362dcbf..bb78c53d0 100644 --- a/meilisearch-http/src/extractors/authentication/error.rs +++ b/meilisearch-http/src/extractors/authentication/error.rs @@ -1,4 +1,4 @@ -use meilisearch_error::{Code, ErrorCode}; +use meilisearch_types::error::{Code, ErrorCode}; #[derive(Debug, thiserror::Error)] pub enum AuthenticationError { diff --git a/meilisearch-http/src/extractors/authentication/mod.rs b/meilisearch-http/src/extractors/authentication/mod.rs index 7732bd7fa..22f080a6f 100644 --- a/meilisearch-http/src/extractors/authentication/mod.rs +++ b/meilisearch-http/src/extractors/authentication/mod.rs @@ -5,12 +5,11 @@ use std::ops::Deref; use std::pin::Pin; use actix_web::FromRequest; +use error::AuthenticationError; use futures::future::err; use futures::Future; -use meilisearch_error::{Code, ResponseError}; - -use error::AuthenticationError; use meilisearch_auth::{AuthController, AuthFilter}; +use meilisearch_types::error::{Code, ResponseError}; pub struct GuardedData { data: D, diff --git a/meilisearch-http/src/lib.rs b/meilisearch-http/src/lib.rs index 201013bc6..bfdb829d4 100644 --- a/meilisearch-http/src/lib.rs +++ b/meilisearch-http/src/lib.rs @@ -148,10 +148,10 @@ macro_rules! create_app { use actix_web::middleware::TrailingSlash; use actix_web::App; use actix_web::{middleware, web}; - use meilisearch_error::ResponseError; use meilisearch_http::error::MeilisearchHttpError; use meilisearch_http::routes; use meilisearch_http::{configure_data, dashboard}; + use meilisearch_types::error::ResponseError; App::new() .configure(|s| configure_data(s, $data.clone(), $auth.clone(), &$opt, $analytics)) diff --git a/meilisearch-http/src/routes/api_key.rs b/meilisearch-http/src/routes/api_key.rs index 87db3f890..7605fa644 100644 --- a/meilisearch-http/src/routes/api_key.rs +++ b/meilisearch-http/src/routes/api_key.rs @@ -7,7 +7,7 @@ use time::OffsetDateTime; use uuid::Uuid; use meilisearch_auth::{error::AuthControllerError, Action, AuthController, Key}; -use meilisearch_error::{Code, ResponseError}; +use meilisearch_types::error::{Code, ResponseError}; use crate::extractors::{ authentication::{policies::*, GuardedData}, @@ -151,7 +151,7 @@ impl KeyView { key: generated_key, uid: key.uid, actions: key.actions, - indexes: key.indexes, + indexes: key.indexes.into_iter().map(String::from).collect(), expires_at: key.expires_at, created_at: key.created_at, updated_at: key.updated_at, diff --git a/meilisearch-http/src/routes/dump.rs b/meilisearch-http/src/routes/dump.rs index 55469b0b4..4d9106ee0 100644 --- a/meilisearch-http/src/routes/dump.rs +++ b/meilisearch-http/src/routes/dump.rs @@ -1,7 +1,7 @@ use actix_web::{web, HttpRequest, HttpResponse}; use log::debug; -use meilisearch_error::ResponseError; use meilisearch_lib::MeiliSearch; +use meilisearch_types::error::ResponseError; use serde_json::json; use crate::analytics::Analytics; diff --git a/meilisearch-http/src/routes/indexes/documents.rs b/meilisearch-http/src/routes/indexes/documents.rs index b5f578a56..2becc6db1 100644 --- a/meilisearch-http/src/routes/indexes/documents.rs +++ b/meilisearch-http/src/routes/indexes/documents.rs @@ -6,10 +6,11 @@ use actix_web::{web, HttpRequest, HttpResponse}; use bstr::ByteSlice; use futures::{Stream, StreamExt}; use log::debug; -use meilisearch_error::ResponseError; use meilisearch_lib::index_controller::{DocumentAdditionFormat, Update}; use meilisearch_lib::milli::update::IndexDocumentsMethod; use meilisearch_lib::MeiliSearch; +use meilisearch_types::error::ResponseError; +use meilisearch_types::star_or::StarOr; use mime::Mime; use once_cell::sync::Lazy; use serde::Deserialize; @@ -22,7 +23,7 @@ use crate::error::MeilisearchHttpError; use crate::extractors::authentication::{policies::*, GuardedData}; use crate::extractors::payload::Payload; use crate::extractors::sequential_extractor::SeqHandler; -use crate::routes::{fold_star_or, PaginationView, StarOr}; +use crate::routes::{fold_star_or, PaginationView}; use crate::task::SummarizedTaskView; static ACCEPTED_CONTENT_TYPE: Lazy> = Lazy::new(|| { diff --git a/meilisearch-http/src/routes/indexes/mod.rs b/meilisearch-http/src/routes/indexes/mod.rs index f5ebf83a9..ed6196ebd 100644 --- a/meilisearch-http/src/routes/indexes/mod.rs +++ b/meilisearch-http/src/routes/indexes/mod.rs @@ -1,8 +1,8 @@ use actix_web::{web, HttpRequest, HttpResponse}; use log::debug; -use meilisearch_error::ResponseError; use meilisearch_lib::index_controller::Update; use meilisearch_lib::MeiliSearch; +use meilisearch_types::error::ResponseError; use serde::{Deserialize, Serialize}; use serde_json::json; use time::OffsetDateTime; diff --git a/meilisearch-http/src/routes/indexes/search.rs b/meilisearch-http/src/routes/indexes/search.rs index 4eaa65b9d..3f8fecd5c 100644 --- a/meilisearch-http/src/routes/indexes/search.rs +++ b/meilisearch-http/src/routes/indexes/search.rs @@ -1,12 +1,12 @@ use actix_web::{web, HttpRequest, HttpResponse}; use log::debug; use meilisearch_auth::IndexSearchRules; -use meilisearch_error::ResponseError; use meilisearch_lib::index::{ SearchQuery, DEFAULT_CROP_LENGTH, DEFAULT_CROP_MARKER, DEFAULT_HIGHLIGHT_POST_TAG, DEFAULT_HIGHLIGHT_PRE_TAG, DEFAULT_SEARCH_LIMIT, }; use meilisearch_lib::MeiliSearch; +use meilisearch_types::error::ResponseError; use serde::Deserialize; use serde_cs::vec::CS; use serde_json::Value; diff --git a/meilisearch-http/src/routes/indexes/settings.rs b/meilisearch-http/src/routes/indexes/settings.rs index 2d0b47121..962fe7d82 100644 --- a/meilisearch-http/src/routes/indexes/settings.rs +++ b/meilisearch-http/src/routes/indexes/settings.rs @@ -1,10 +1,10 @@ use log::debug; use actix_web::{web, HttpRequest, HttpResponse}; -use meilisearch_error::ResponseError; use meilisearch_lib::index::{Settings, Unchecked}; use meilisearch_lib::index_controller::Update; use meilisearch_lib::MeiliSearch; +use meilisearch_types::error::ResponseError; use serde_json::json; use crate::analytics::Analytics; @@ -21,7 +21,7 @@ macro_rules! make_setting_route { use meilisearch_lib::milli::update::Setting; use meilisearch_lib::{index::Settings, index_controller::Update, MeiliSearch}; - use meilisearch_error::ResponseError; + use meilisearch_types::error::ResponseError; use $crate::analytics::Analytics; use $crate::extractors::authentication::{policies::*, GuardedData}; use $crate::extractors::sequential_extractor::SeqHandler; diff --git a/meilisearch-http/src/routes/mod.rs b/meilisearch-http/src/routes/mod.rs index a438d12d7..f61854c48 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -1,14 +1,13 @@ -use std::str::FromStr; - use actix_web::{web, HttpResponse}; use log::debug; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; -use meilisearch_error::ResponseError; use meilisearch_lib::index::{Settings, Unchecked}; use meilisearch_lib::MeiliSearch; +use meilisearch_types::error::ResponseError; +use meilisearch_types::star_or::StarOr; use crate::extractors::authentication::{policies::*, GuardedData}; @@ -27,26 +26,6 @@ pub fn configure(cfg: &mut web::ServiceConfig) { .service(web::scope("/indexes").configure(indexes::configure)); } -/// A type that tries to match either a star (*) or -/// any other thing that implements `FromStr`. -#[derive(Debug)] -pub enum StarOr { - Star, - Other(T), -} - -impl FromStr for StarOr { - type Err = T::Err; - - fn from_str(s: &str) -> Result { - if s.trim() == "*" { - Ok(StarOr::Star) - } else { - T::from_str(s).map(StarOr::Other) - } - } -} - /// Extracts the raw values from the `StarOr` types and /// return None if a `StarOr::Star` is encountered. pub fn fold_star_or(content: impl IntoIterator>) -> Option diff --git a/meilisearch-http/src/routes/tasks.rs b/meilisearch-http/src/routes/tasks.rs index 14716ff6b..fed7fa634 100644 --- a/meilisearch-http/src/routes/tasks.rs +++ b/meilisearch-http/src/routes/tasks.rs @@ -1,8 +1,10 @@ use actix_web::{web, HttpRequest, HttpResponse}; -use meilisearch_error::ResponseError; use meilisearch_lib::tasks::task::{TaskContent, TaskEvent, TaskId}; use meilisearch_lib::tasks::TaskFilter; -use meilisearch_lib::{IndexUid, MeiliSearch}; +use meilisearch_lib::MeiliSearch; +use meilisearch_types::error::ResponseError; +use meilisearch_types::index_uid::IndexUid; +use meilisearch_types::star_or::StarOr; use serde::Deserialize; use serde_cs::vec::CS; use serde_json::json; @@ -12,7 +14,7 @@ use crate::extractors::authentication::{policies::*, GuardedData}; use crate::extractors::sequential_extractor::SeqHandler; use crate::task::{TaskListView, TaskStatus, TaskType, TaskView}; -use super::{fold_star_or, StarOr}; +use super::fold_star_or; const DEFAULT_LIMIT: fn() -> usize = || 20; diff --git a/meilisearch-http/src/task.rs b/meilisearch-http/src/task.rs index 1a1d4e4a8..06bba1f76 100644 --- a/meilisearch-http/src/task.rs +++ b/meilisearch-http/src/task.rs @@ -3,12 +3,12 @@ use std::fmt::{self, Write}; use std::str::FromStr; use std::write; -use meilisearch_error::ResponseError; use meilisearch_lib::index::{Settings, Unchecked}; use meilisearch_lib::tasks::batch::BatchId; use meilisearch_lib::tasks::task::{ DocumentDeletion, Task, TaskContent, TaskEvent, TaskId, TaskResult, }; +use meilisearch_types::error::ResponseError; use serde::{Deserialize, Serialize, Serializer}; use time::{Duration, OffsetDateTime}; diff --git a/meilisearch-http/tests/auth/api_keys.rs b/meilisearch-http/tests/auth/api_keys.rs index 28be81c91..9dcbd9b55 100644 --- a/meilisearch-http/tests/auth/api_keys.rs +++ b/meilisearch-http/tests/auth/api_keys.rs @@ -358,6 +358,32 @@ async fn error_add_api_key_invalid_parameters_indexes() { assert_eq!(response, expected_response); } +#[actix_rt::test] +async fn error_add_api_key_invalid_index_uids() { + let mut server = Server::new_auth().await; + server.use_api_key("MASTER_KEY"); + + let content = json!({ + "description": Value::Null, + "indexes": ["invalid index # / \\name with spaces"], + "actions": [ + "documents.add" + ], + "expiresAt": "2050-11-13T00:00:00" + }); + let (response, code) = server.add_api_key(content).await; + + let expected_response = json!({ + "message": r#"`indexes` field value `["invalid index # / \\name with spaces"]` is invalid. It should be an array of string representing index names."#, + "code": "invalid_api_key_indexes", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#invalid_api_key_indexes" + }); + + assert_eq!(response, expected_response); + assert_eq!(code, 400); +} + #[actix_rt::test] async fn error_add_api_key_invalid_parameters_actions() { let mut server = Server::new_auth().await; diff --git a/meilisearch-http/tests/documents/add_documents.rs b/meilisearch-http/tests/documents/add_documents.rs index d6235c8b7..c3baf0cb0 100644 --- a/meilisearch-http/tests/documents/add_documents.rs +++ b/meilisearch-http/tests/documents/add_documents.rs @@ -638,7 +638,7 @@ async fn error_document_add_create_index_bad_uid() { let (response, code) = index.add_documents(json!([{"id": 1}]), None).await; let expected_response = json!({ - "message": "`883 fj!` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).", + "message": "invalid index uid `883 fj!`, the uid must be an integer or a string containing only alphanumeric characters a-z A-Z 0-9, hyphens - and underscores _.", "code": "invalid_index_uid", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_index_uid" @@ -655,7 +655,7 @@ async fn error_document_update_create_index_bad_uid() { let (response, code) = index.update_documents(json!([{"id": 1}]), None).await; let expected_response = json!({ - "message": "`883 fj!` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).", + "message": "invalid index uid `883 fj!`, the uid must be an integer or a string containing only alphanumeric characters a-z A-Z 0-9, hyphens - and underscores _.", "code": "invalid_index_uid", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_index_uid" diff --git a/meilisearch-http/tests/index/create_index.rs b/meilisearch-http/tests/index/create_index.rs index 0e134600e..a1c508e1f 100644 --- a/meilisearch-http/tests/index/create_index.rs +++ b/meilisearch-http/tests/index/create_index.rs @@ -102,7 +102,7 @@ async fn error_create_with_invalid_index_uid() { let (response, code) = index.create(None).await; let expected_response = json!({ - "message": "`test test#!` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).", + "message": "invalid index uid `test test#!`, the uid must be an integer or a string containing only alphanumeric characters a-z A-Z 0-9, hyphens - and underscores _.", "code": "invalid_index_uid", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_index_uid" diff --git a/meilisearch-http/tests/settings/get_settings.rs b/meilisearch-http/tests/settings/get_settings.rs index 1cc60d652..0862b15c5 100644 --- a/meilisearch-http/tests/settings/get_settings.rs +++ b/meilisearch-http/tests/settings/get_settings.rs @@ -197,7 +197,7 @@ async fn error_update_setting_unexisting_index_invalid_uid() { assert_eq!(code, 400); let expected = json!({ - "message": "`test##! ` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).", + "message": "invalid index uid `test##! `, the uid must be an integer or a string containing only alphanumeric characters a-z A-Z 0-9, hyphens - and underscores _.", "code": "invalid_index_uid", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_index_uid"}); diff --git a/meilisearch-lib/Cargo.toml b/meilisearch-lib/Cargo.toml index 425f931b1..fa4e6587a 100644 --- a/meilisearch-lib/Cargo.toml +++ b/meilisearch-lib/Cargo.toml @@ -29,7 +29,7 @@ itertools = "0.10.3" lazy_static = "1.4.0" log = "0.4.14" meilisearch-auth = { path = "../meilisearch-auth" } -meilisearch-error = { path = "../meilisearch-error" } +meilisearch-types = { path = "../meilisearch-types" } milli = { git = "https://github.com/meilisearch/milli.git", tag = "v0.29.2" } mime = "0.3.16" num_cpus = "1.13.1" @@ -59,7 +59,7 @@ whoami = { version = "1.2.1", optional = true } [dev-dependencies] actix-rt = "2.7.0" -meilisearch-error = { path = "../meilisearch-error", features = ["test-traits"] } +meilisearch-types = { path = "../meilisearch-types", features = ["test-traits"] } mockall = "0.11.0" nelson = { git = "https://github.com/meilisearch/nelson.git", rev = "675f13885548fb415ead8fbb447e9e6d9314000a"} paste = "1.0.6" diff --git a/meilisearch-lib/src/document_formats.rs b/meilisearch-lib/src/document_formats.rs index 93c47afe8..de3d7f5d5 100644 --- a/meilisearch-lib/src/document_formats.rs +++ b/meilisearch-lib/src/document_formats.rs @@ -2,7 +2,8 @@ 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}; +use meilisearch_types::error::{Code, ErrorCode}; +use meilisearch_types::internal_error; use milli::documents::DocumentBatchBuilder; type Result = std::result::Result; diff --git a/meilisearch-lib/src/dump/compat/v2.rs b/meilisearch-lib/src/dump/compat/v2.rs index a30e24794..364d894c4 100644 --- a/meilisearch-lib/src/dump/compat/v2.rs +++ b/meilisearch-lib/src/dump/compat/v2.rs @@ -1,5 +1,5 @@ use anyhow::bail; -use meilisearch_error::Code; +use meilisearch_types::error::Code; use milli::update::IndexDocumentsMethod; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; diff --git a/meilisearch-lib/src/dump/compat/v3.rs b/meilisearch-lib/src/dump/compat/v3.rs index 2044e3b60..61e31eccd 100644 --- a/meilisearch-lib/src/dump/compat/v3.rs +++ b/meilisearch-lib/src/dump/compat/v3.rs @@ -1,4 +1,5 @@ -use meilisearch_error::{Code, ResponseError}; +use meilisearch_types::error::{Code, ResponseError}; +use meilisearch_types::index_uid::IndexUid; use milli::update::IndexDocumentsMethod; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; @@ -6,7 +7,6 @@ use uuid::Uuid; use super::v4::{Task, TaskContent, TaskEvent}; use crate::index::{Settings, Unchecked}; -use crate::index_resolver::IndexUid; use crate::tasks::task::{DocumentDeletion, TaskId, TaskResult}; use super::v2; diff --git a/meilisearch-lib/src/dump/compat/v4.rs b/meilisearch-lib/src/dump/compat/v4.rs index 867bc7b63..c412e7f17 100644 --- a/meilisearch-lib/src/dump/compat/v4.rs +++ b/meilisearch-lib/src/dump/compat/v4.rs @@ -1,4 +1,5 @@ -use meilisearch_error::ResponseError; +use meilisearch_types::error::ResponseError; +use meilisearch_types::index_uid::IndexUid; use milli::update::IndexDocumentsMethod; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; @@ -9,7 +10,6 @@ use crate::tasks::batch::BatchId; use crate::tasks::task::{ DocumentDeletion, TaskContent as NewTaskContent, TaskEvent as NewTaskEvent, TaskId, TaskResult, }; -use crate::IndexUid; #[derive(Debug, Serialize, Deserialize)] pub struct Task { diff --git a/meilisearch-lib/src/dump/error.rs b/meilisearch-lib/src/dump/error.rs index da9010347..3f6e2aae5 100644 --- a/meilisearch-lib/src/dump/error.rs +++ b/meilisearch-lib/src/dump/error.rs @@ -1,5 +1,6 @@ use meilisearch_auth::error::AuthControllerError; -use meilisearch_error::{internal_error, Code, ErrorCode}; +use meilisearch_types::error::{Code, ErrorCode}; +use meilisearch_types::internal_error; use crate::{index_resolver::error::IndexResolverError, tasks::error::TaskError}; diff --git a/meilisearch-lib/src/error.rs b/meilisearch-lib/src/error.rs index c3e7b8313..83e9263b4 100644 --- a/meilisearch-lib/src/error.rs +++ b/meilisearch-lib/src/error.rs @@ -1,7 +1,7 @@ use std::error::Error; use std::fmt; -use meilisearch_error::{Code, ErrorCode}; +use meilisearch_types::error::{Code, ErrorCode}; use milli::UserError; #[derive(Debug)] diff --git a/meilisearch-lib/src/index/error.rs b/meilisearch-lib/src/index/error.rs index 89a12a41f..e31fcc4a0 100644 --- a/meilisearch-lib/src/index/error.rs +++ b/meilisearch-lib/src/index/error.rs @@ -1,6 +1,7 @@ use std::error::Error; -use meilisearch_error::{internal_error, Code, ErrorCode}; +use meilisearch_types::error::{Code, ErrorCode}; +use meilisearch_types::internal_error; use serde_json::Value; use crate::{error::MilliError, update_file_store}; diff --git a/meilisearch-lib/src/index_controller/error.rs b/meilisearch-lib/src/index_controller/error.rs index 529887b6a..ab2dd142d 100644 --- a/meilisearch-lib/src/index_controller/error.rs +++ b/meilisearch-lib/src/index_controller/error.rs @@ -1,7 +1,8 @@ use std::error::Error; -use meilisearch_error::Code; -use meilisearch_error::{internal_error, ErrorCode}; +use meilisearch_types::error::{Code, ErrorCode}; +use meilisearch_types::index_uid::IndexUidFormatError; +use meilisearch_types::internal_error; use tokio::task::JoinError; use super::DocumentAdditionFormat; @@ -63,3 +64,9 @@ impl ErrorCode for IndexControllerError { } } } + +impl From for IndexControllerError { + fn from(err: IndexUidFormatError) -> Self { + IndexResolverError::from(err).into() + } +} diff --git a/meilisearch-lib/src/index_controller/mod.rs b/meilisearch-lib/src/index_controller/mod.rs index 7d8b51fa8..88782c5ea 100644 --- a/meilisearch-lib/src/index_controller/mod.rs +++ b/meilisearch-lib/src/index_controller/mod.rs @@ -11,6 +11,7 @@ use actix_web::error::PayloadError; use bytes::Bytes; use futures::Stream; use futures::StreamExt; +use meilisearch_types::index_uid::IndexUid; use milli::update::IndexDocumentsMethod; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; @@ -37,7 +38,6 @@ use error::Result; use self::error::IndexControllerError; use crate::index_resolver::index_store::{IndexStore, MapIndexStore}; use crate::index_resolver::meta_store::{HeedMetaStore, IndexMetaStore}; -pub use crate::index_resolver::IndexUid; use crate::index_resolver::{create_index_resolver, IndexResolver}; use crate::update_file_store::UpdateFileStore; diff --git a/meilisearch-lib/src/index_controller/updates/error.rs b/meilisearch-lib/src/index_controller/updates/error.rs index 434783041..7ecaa45c5 100644 --- a/meilisearch-lib/src/index_controller/updates/error.rs +++ b/meilisearch-lib/src/index_controller/updates/error.rs @@ -1,7 +1,7 @@ use std::error::Error; use std::fmt; -use meilisearch_error::{internal_error, Code, ErrorCode}; +use meilisearch_types::{internal_error, Code, ErrorCode}; use crate::{ document_formats::DocumentFormatError, diff --git a/meilisearch-lib/src/index_resolver/error.rs b/meilisearch-lib/src/index_resolver/error.rs index 610ec6c7c..d973d2229 100644 --- a/meilisearch-lib/src/index_resolver/error.rs +++ b/meilisearch-lib/src/index_resolver/error.rs @@ -1,6 +1,8 @@ use std::fmt; -use meilisearch_error::{internal_error, Code, ErrorCode}; +use meilisearch_types::error::{Code, ErrorCode}; +use meilisearch_types::index_uid::IndexUidFormatError; +use meilisearch_types::internal_error; use tokio::sync::mpsc::error::SendError as MpscSendError; use tokio::sync::oneshot::error::RecvError as OneshotRecvError; use uuid::Uuid; @@ -25,8 +27,8 @@ pub enum IndexResolverError { UuidAlreadyExists(Uuid), #[error("{0}")] Milli(#[from] milli::Error), - #[error("`{0}` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).")] - BadlyFormatted(String), + #[error("{0}")] + BadlyFormatted(#[from] IndexUidFormatError), } impl From> for IndexResolverError diff --git a/meilisearch-lib/src/index_resolver/mod.rs b/meilisearch-lib/src/index_resolver/mod.rs index 316528647..abbfdfd4c 100644 --- a/meilisearch-lib/src/index_resolver/mod.rs +++ b/meilisearch-lib/src/index_resolver/mod.rs @@ -2,20 +2,17 @@ pub mod error; pub mod index_store; pub mod meta_store; -use std::convert::{TryFrom, TryInto}; -use std::error::Error; -use std::fmt; +use std::convert::TryFrom; use std::path::Path; -use std::str::FromStr; use std::sync::Arc; use error::{IndexResolverError, Result}; use index_store::{IndexStore, MapIndexStore}; -use meilisearch_error::ResponseError; +use meilisearch_types::error::ResponseError; +use meilisearch_types::index_uid::IndexUid; use meta_store::{HeedMetaStore, IndexMetaStore}; use milli::heed::Env; use milli::update::{DocumentDeletionResult, IndexerConfig}; -use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use tokio::task::spawn_blocking; use uuid::Uuid; @@ -35,12 +32,6 @@ pub use real::IndexResolver; #[cfg(test)] pub use test::MockIndexResolver as IndexResolver; -/// An index uid is composed of only ascii alphanumeric characters, - and _, between 1 and 400 -/// bytes long -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -#[cfg_attr(test, derive(proptest_derive::Arbitrary))] -pub struct IndexUid(#[cfg_attr(test, proptest(regex("[a-zA-Z0-9_-]{1,400}")))] String); - pub fn create_index_resolver( path: impl AsRef, index_size: usize, @@ -53,81 +44,6 @@ pub fn create_index_resolver( Ok(IndexResolver::new(uuid_store, index_store, file_store)) } -impl IndexUid { - pub fn new_unchecked(s: impl AsRef) -> Self { - Self(s.as_ref().to_string()) - } - - pub fn into_inner(self) -> String { - self.0 - } - - /// Return a reference over the inner str. - pub fn as_str(&self) -> &str { - &self.0 - } -} - -impl std::ops::Deref for IndexUid { - type Target = str; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl TryInto for String { - type Error = IndexUidFormatError; - - fn try_into(self) -> std::result::Result { - IndexUid::from_str(&self) - } -} - -#[derive(Debug)] -pub struct IndexUidFormatError { - invalid_uid: String, -} - -impl fmt::Display for IndexUidFormatError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "invalid index uid `{}`, the uid must be an integer \ - or a string containing only alphanumeric characters \ - a-z A-Z 0-9, hyphens - and underscores _.", - self.invalid_uid, - ) - } -} - -impl Error for IndexUidFormatError {} - -impl From for IndexResolverError { - fn from(error: IndexUidFormatError) -> Self { - Self::BadlyFormatted(error.invalid_uid) - } -} - -impl FromStr for IndexUid { - type Err = IndexUidFormatError; - - fn from_str(uid: &str) -> std::result::Result { - if !uid - .chars() - .all(|x| x.is_ascii_alphanumeric() || x == '-' || x == '_') - || uid.is_empty() - || uid.len() > 400 - { - Err(IndexUidFormatError { - invalid_uid: uid.to_string(), - }) - } else { - Ok(IndexUid(uid.to_string())) - } - } -} - mod real { use super::*; diff --git a/meilisearch-lib/src/lib.rs b/meilisearch-lib/src/lib.rs index 52da63027..3d3d5e860 100644 --- a/meilisearch-lib/src/lib.rs +++ b/meilisearch-lib/src/lib.rs @@ -13,7 +13,7 @@ mod update_file_store; use std::path::Path; -pub use index_controller::{IndexUid, MeiliSearch}; +pub use index_controller::MeiliSearch; pub use milli; pub use milli::heed; diff --git a/meilisearch-lib/src/tasks/error.rs b/meilisearch-lib/src/tasks/error.rs index d849b4c10..75fd7a591 100644 --- a/meilisearch-lib/src/tasks/error.rs +++ b/meilisearch-lib/src/tasks/error.rs @@ -1,4 +1,5 @@ -use meilisearch_error::{internal_error, Code, ErrorCode}; +use meilisearch_types::error::{Code, ErrorCode}; +use meilisearch_types::internal_error; use tokio::task::JoinError; use crate::update_file_store::UpdateFileStoreError; diff --git a/meilisearch-lib/src/tasks/handlers/index_resolver_handler.rs b/meilisearch-lib/src/tasks/handlers/index_resolver_handler.rs index 0975ba912..22c57e2fd 100644 --- a/meilisearch-lib/src/tasks/handlers/index_resolver_handler.rs +++ b/meilisearch-lib/src/tasks/handlers/index_resolver_handler.rs @@ -55,9 +55,9 @@ mod test { task::{Task, TaskContent}, }; use crate::update_file_store::{Result as FileStoreResult, UpdateFileStore}; - use crate::IndexUid; use super::*; + use meilisearch_types::index_uid::IndexUid; use milli::update::IndexDocumentsMethod; use nelson::Mocker; use proptest::prelude::*; diff --git a/meilisearch-lib/src/tasks/scheduler.rs b/meilisearch-lib/src/tasks/scheduler.rs index 76294b6e7..8ce14fe8c 100644 --- a/meilisearch-lib/src/tasks/scheduler.rs +++ b/meilisearch-lib/src/tasks/scheduler.rs @@ -534,10 +534,11 @@ fn make_batch(tasks: &mut TaskQueue, config: &SchedulerConfig) -> Processing { #[cfg(test)] mod test { + use meilisearch_types::index_uid::IndexUid; use milli::update::IndexDocumentsMethod; use uuid::Uuid; - use crate::{index_resolver::IndexUid, tasks::task::TaskContent}; + use crate::tasks::task::TaskContent; use super::*; diff --git a/meilisearch-lib/src/tasks/task.rs b/meilisearch-lib/src/tasks/task.rs index cf9ab0520..bd5579151 100644 --- a/meilisearch-lib/src/tasks/task.rs +++ b/meilisearch-lib/src/tasks/task.rs @@ -1,4 +1,5 @@ -use meilisearch_error::ResponseError; +use meilisearch_types::error::ResponseError; +use meilisearch_types::index_uid::IndexUid; use milli::update::{DocumentAdditionResult, IndexDocumentsMethod}; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; @@ -6,7 +7,6 @@ use uuid::Uuid; use super::batch::BatchId; use crate::index::{Settings, Unchecked}; -use crate::index_resolver::IndexUid; pub type TaskId = u32; diff --git a/meilisearch-lib/src/tasks/task_store/mod.rs b/meilisearch-lib/src/tasks/task_store/mod.rs index 33f4bfb50..e2b01afb8 100644 --- a/meilisearch-lib/src/tasks/task_store/mod.rs +++ b/meilisearch-lib/src/tasks/task_store/mod.rs @@ -267,13 +267,11 @@ impl TaskStore { #[cfg(test)] pub mod test { - use crate::{ - tasks::{scheduler::Processing, task_store::store::test::tmp_env}, - IndexUid, - }; + use crate::tasks::{scheduler::Processing, task_store::store::test::tmp_env}; use super::*; + use meilisearch_types::index_uid::IndexUid; use nelson::Mocker; use proptest::{ strategy::Strategy, diff --git a/meilisearch-lib/src/tasks/task_store/store.rs b/meilisearch-lib/src/tasks/task_store/store.rs index f044bd077..9dfe61c55 100644 --- a/meilisearch-lib/src/tasks/task_store/store.rs +++ b/meilisearch-lib/src/tasks/task_store/store.rs @@ -179,11 +179,11 @@ impl Store { #[cfg(test)] pub mod test { use itertools::Itertools; + use meilisearch_types::index_uid::IndexUid; use milli::heed::EnvOpenOptions; use nelson::Mocker; use tempfile::TempDir; - use crate::index_resolver::IndexUid; use crate::tasks::task::TaskContent; use super::*; diff --git a/meilisearch-error/Cargo.toml b/meilisearch-types/Cargo.toml similarity index 93% rename from meilisearch-error/Cargo.toml rename to meilisearch-types/Cargo.toml index 8d2203144..6949722e7 100644 --- a/meilisearch-error/Cargo.toml +++ b/meilisearch-types/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "meilisearch-error" +name = "meilisearch-types" version = "0.28.0" authors = ["marin "] edition = "2021" diff --git a/meilisearch-error/src/lib.rs b/meilisearch-types/src/error.rs similarity index 100% rename from meilisearch-error/src/lib.rs rename to meilisearch-types/src/error.rs diff --git a/meilisearch-types/src/index_uid.rs b/meilisearch-types/src/index_uid.rs new file mode 100644 index 000000000..de453572b --- /dev/null +++ b/meilisearch-types/src/index_uid.rs @@ -0,0 +1,85 @@ +use serde::{Deserialize, Serialize}; +use std::error::Error; +use std::fmt; +use std::str::FromStr; + +/// An index uid is composed of only ascii alphanumeric characters, - and _, between 1 and 400 +/// bytes long +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[cfg_attr(feature = "test-traits", derive(proptest_derive::Arbitrary))] +pub struct IndexUid( + #[cfg_attr(feature = "test-traits", proptest(regex("[a-zA-Z0-9_-]{1,400}")))] String, +); + +impl IndexUid { + pub fn new_unchecked(s: impl AsRef) -> Self { + Self(s.as_ref().to_string()) + } + + pub fn into_inner(self) -> String { + self.0 + } + + /// Return a reference over the inner str. + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl std::ops::Deref for IndexUid { + type Target = str; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl TryFrom for IndexUid { + type Error = IndexUidFormatError; + + fn try_from(uid: String) -> Result { + if !uid + .chars() + .all(|x| x.is_ascii_alphanumeric() || x == '-' || x == '_') + || uid.is_empty() + || uid.len() > 400 + { + Err(IndexUidFormatError { invalid_uid: uid }) + } else { + Ok(IndexUid(uid)) + } + } +} + +impl FromStr for IndexUid { + type Err = IndexUidFormatError; + + fn from_str(uid: &str) -> Result { + uid.to_string().try_into() + } +} + +impl From for String { + fn from(uid: IndexUid) -> Self { + uid.into_inner() + } +} + +#[derive(Debug)] +pub struct IndexUidFormatError { + pub invalid_uid: String, +} + +impl fmt::Display for IndexUidFormatError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "invalid index uid `{}`, the uid must be an integer \ + or a string containing only alphanumeric characters \ + a-z A-Z 0-9, hyphens - and underscores _.", + self.invalid_uid, + ) + } +} + +impl Error for IndexUidFormatError {} diff --git a/meilisearch-types/src/lib.rs b/meilisearch-types/src/lib.rs new file mode 100644 index 000000000..2d685c2dc --- /dev/null +++ b/meilisearch-types/src/lib.rs @@ -0,0 +1,3 @@ +pub mod error; +pub mod index_uid; +pub mod star_or; diff --git a/meilisearch-types/src/star_or.rs b/meilisearch-types/src/star_or.rs new file mode 100644 index 000000000..02c9c3524 --- /dev/null +++ b/meilisearch-types/src/star_or.rs @@ -0,0 +1,138 @@ +use serde::de::Visitor; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt::{Display, Formatter}; +use std::marker::PhantomData; +use std::ops::Deref; +use std::str::FromStr; + +/// A type that tries to match either a star (*) or +/// any other thing that implements `FromStr`. +#[derive(Debug)] +pub enum StarOr { + Star, + Other(T), +} + +impl FromStr for StarOr { + type Err = T::Err; + + fn from_str(s: &str) -> Result { + if s.trim() == "*" { + Ok(StarOr::Star) + } else { + T::from_str(s).map(StarOr::Other) + } + } +} + +impl> Deref for StarOr { + type Target = str; + + fn deref(&self) -> &Self::Target { + match self { + Self::Star => "*", + Self::Other(t) => t.deref(), + } + } +} + +impl> From> for String { + fn from(s: StarOr) -> Self { + match s { + StarOr::Star => "*".to_string(), + StarOr::Other(t) => t.into(), + } + } +} + +impl PartialEq for StarOr { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Star, Self::Star) => true, + (Self::Other(left), Self::Other(right)) if left.eq(right) => true, + _ => false, + } + } +} + +impl Eq for StarOr {} + +impl<'de, T, E> Deserialize<'de> for StarOr +where + T: FromStr, + E: Display, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + /// Serde can't differentiate between `StarOr::Star` and `StarOr::Other` without a tag. + /// Simply using `#[serde(untagged)]` + `#[serde(rename="*")]` will lead to attempting to + /// deserialize everything as a `StarOr::Other`, including "*". + /// [`#[serde(other)]`](https://serde.rs/variant-attrs.html#other) might have helped but is + /// not supported on untagged enums. + struct StarOrVisitor(PhantomData); + + impl<'de, T, FE> Visitor<'de> for StarOrVisitor + where + T: FromStr, + FE: Display, + { + type Value = StarOr; + + fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { + formatter.write_str("a string") + } + + fn visit_str(self, v: &str) -> Result + where + SE: serde::de::Error, + { + match v { + "*" => Ok(StarOr::Star), + v => { + let other = FromStr::from_str(v).map_err(|e: T::Err| { + SE::custom(format!("Invalid `other` value: {}", e)) + })?; + Ok(StarOr::Other(other)) + } + } + } + } + + deserializer.deserialize_str(StarOrVisitor(PhantomData)) + } +} + +impl Serialize for StarOr +where + T: Deref, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + StarOr::Star => serializer.serialize_str("*"), + StarOr::Other(other) => serializer.serialize_str(other.deref()), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::{json, Value}; + + #[test] + fn star_or_serde_roundtrip() { + fn roundtrip(content: Value, expected: StarOr) { + let deserialized: StarOr = serde_json::from_value(content.clone()).unwrap(); + assert_eq!(deserialized, expected); + assert_eq!(content, serde_json::to_value(deserialized).unwrap()); + } + + roundtrip(json!("products"), StarOr::Other("products".to_string())); + roundtrip(json!("*"), StarOr::Star); + } +} diff --git a/permissive-json-pointer/src/lib.rs b/permissive-json-pointer/src/lib.rs index 56382beae..8f97ab2de 100644 --- a/permissive-json-pointer/src/lib.rs +++ b/permissive-json-pointer/src/lib.rs @@ -206,7 +206,7 @@ fn create_value(value: &Document, mut selectors: HashSet<&str>) -> Document { new_value } -fn create_array(array: &Vec, selectors: &HashSet<&str>) -> Vec { +fn create_array(array: &[Value], selectors: &HashSet<&str>) -> Vec { let mut res = Vec::new(); for value in array {