mirror of
https://github.com/meilisearch/MeiliSearch
synced 2024-11-29 16:24:26 +01:00
Merge #2414
2414: Improve index uid validation upon API key creation r=Kerollmops a=pierre-l - ~Use an IndexUid newtype to enforce stronger constraints~ - ~`cargo update -p vergen`~ (`rustup update` was the proper fix for this) - Add a new `meilisearch_types` crate - Move `meilisearch_error` to `meilisearch_types::error` - Move `meilisearch_lib::index_resolver::IndexUid` to `meilisearch_types::index_uid` - Add a new `InvalidIndexUid` error in `meilisearch_types::index_uid` - Move `meilisearch_http::routes::StarOr` to `meilisearch_types::star_or` - Use the `IndexUid` and `StarOr` in `meilisearch_auth::Key` Fixes #2158 Co-authored-by: pierre-l <pierre.larger@gmail.com>
This commit is contained in:
commit
de356061db
28
Cargo.lock
generated
28
Cargo.lock
generated
@ -2006,7 +2006,7 @@ dependencies = [
|
|||||||
"base64",
|
"base64",
|
||||||
"enum-iterator",
|
"enum-iterator",
|
||||||
"hmac",
|
"hmac",
|
||||||
"meilisearch-error",
|
"meilisearch-types",
|
||||||
"milli",
|
"milli",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
@ -2017,17 +2017,6 @@ dependencies = [
|
|||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "meilisearch-error"
|
|
||||||
version = "0.28.0"
|
|
||||||
dependencies = [
|
|
||||||
"actix-web",
|
|
||||||
"proptest",
|
|
||||||
"proptest-derive",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meilisearch-http"
|
name = "meilisearch-http"
|
||||||
version = "0.28.0"
|
version = "0.28.0"
|
||||||
@ -2061,8 +2050,8 @@ dependencies = [
|
|||||||
"manifest-dir-macros",
|
"manifest-dir-macros",
|
||||||
"maplit",
|
"maplit",
|
||||||
"meilisearch-auth",
|
"meilisearch-auth",
|
||||||
"meilisearch-error",
|
|
||||||
"meilisearch-lib",
|
"meilisearch-lib",
|
||||||
|
"meilisearch-types",
|
||||||
"mime",
|
"mime",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"obkv",
|
"obkv",
|
||||||
@ -2129,7 +2118,7 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"meilisearch-auth",
|
"meilisearch-auth",
|
||||||
"meilisearch-error",
|
"meilisearch-types",
|
||||||
"milli",
|
"milli",
|
||||||
"mime",
|
"mime",
|
||||||
"mockall",
|
"mockall",
|
||||||
@ -2163,6 +2152,17 @@ dependencies = [
|
|||||||
"whoami",
|
"whoami",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "meilisearch-types"
|
||||||
|
version = "0.28.0"
|
||||||
|
dependencies = [
|
||||||
|
"actix-web",
|
||||||
|
"proptest",
|
||||||
|
"proptest-derive",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"meilisearch-http",
|
"meilisearch-http",
|
||||||
"meilisearch-error",
|
"meilisearch-types",
|
||||||
"meilisearch-lib",
|
"meilisearch-lib",
|
||||||
"meilisearch-auth",
|
"meilisearch-auth",
|
||||||
"permissive-json-pointer",
|
"permissive-json-pointer",
|
||||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
|||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
enum-iterator = "0.7.0"
|
enum-iterator = "0.7.0"
|
||||||
hmac = "0.12.1"
|
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" }
|
milli = { git = "https://github.com/meilisearch/milli.git", tag = "v0.29.2" }
|
||||||
rand = "0.8.4"
|
rand = "0.8.4"
|
||||||
serde = { version = "1.0.136", features = ["derive"] }
|
serde = { version = "1.0.136", features = ["derive"] }
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use meilisearch_error::ErrorCode;
|
use meilisearch_types::error::{Code, ErrorCode};
|
||||||
use meilisearch_error::{internal_error, Code};
|
use meilisearch_types::internal_error;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, AuthControllerError>;
|
pub type Result<T> = std::result::Result<T, AuthControllerError>;
|
||||||
|
@ -2,6 +2,8 @@ use crate::action::Action;
|
|||||||
use crate::error::{AuthControllerError, Result};
|
use crate::error::{AuthControllerError, Result};
|
||||||
use crate::store::KeyId;
|
use crate::store::KeyId;
|
||||||
|
|
||||||
|
use meilisearch_types::index_uid::IndexUid;
|
||||||
|
use meilisearch_types::star_or::StarOr;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{from_value, Value};
|
use serde_json::{from_value, Value};
|
||||||
use time::format_description::well_known::Rfc3339;
|
use time::format_description::well_known::Rfc3339;
|
||||||
@ -17,7 +19,7 @@ pub struct Key {
|
|||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub uid: KeyId,
|
pub uid: KeyId,
|
||||||
pub actions: Vec<Action>,
|
pub actions: Vec<Action>,
|
||||||
pub indexes: Vec<String>,
|
pub indexes: Vec<StarOr<IndexUid>>,
|
||||||
#[serde(with = "time::serde::rfc3339::option")]
|
#[serde(with = "time::serde::rfc3339::option")]
|
||||||
pub expires_at: Option<OffsetDateTime>,
|
pub expires_at: Option<OffsetDateTime>,
|
||||||
#[serde(with = "time::serde::rfc3339")]
|
#[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()),
|
description: Some("Use it for anything that is not a search operation. Caution! Do not expose it on a public frontend".to_string()),
|
||||||
uid,
|
uid,
|
||||||
actions: vec![Action::All],
|
actions: vec![Action::All],
|
||||||
indexes: vec!["*".to_string()],
|
indexes: vec![StarOr::Star],
|
||||||
expires_at: None,
|
expires_at: None,
|
||||||
created_at: now,
|
created_at: now,
|
||||||
updated_at: now,
|
updated_at: now,
|
||||||
@ -151,7 +153,7 @@ impl Key {
|
|||||||
description: Some("Use it to search from the frontend".to_string()),
|
description: Some("Use it to search from the frontend".to_string()),
|
||||||
uid,
|
uid,
|
||||||
actions: vec![Action::Search],
|
actions: vec![Action::Search],
|
||||||
indexes: vec!["*".to_string()],
|
indexes: vec![StarOr::Star],
|
||||||
expires_at: None,
|
expires_at: None,
|
||||||
created_at: now,
|
created_at: now,
|
||||||
updated_at: now,
|
updated_at: now,
|
||||||
|
@ -5,6 +5,7 @@ mod key;
|
|||||||
mod store;
|
mod store;
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::ops::Deref;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -16,6 +17,7 @@ use uuid::Uuid;
|
|||||||
pub use action::{actions, Action};
|
pub use action::{actions, Action};
|
||||||
use error::{AuthControllerError, Result};
|
use error::{AuthControllerError, Result};
|
||||||
pub use key::Key;
|
pub use key::Key;
|
||||||
|
use meilisearch_types::star_or::StarOr;
|
||||||
use store::generate_key_as_base64;
|
use store::generate_key_as_base64;
|
||||||
pub use store::open_auth_store_env;
|
pub use store::open_auth_store_env;
|
||||||
use store::HeedAuthStore;
|
use store::HeedAuthStore;
|
||||||
@ -87,20 +89,22 @@ impl AuthController {
|
|||||||
.get_api_key(uid)?
|
.get_api_key(uid)?
|
||||||
.ok_or_else(|| AuthControllerError::ApiKeyNotFound(uid.to_string()))?;
|
.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 {
|
filters.search_rules = match search_rules {
|
||||||
// Intersect search_rules with parent key authorized indexes.
|
// Intersect search_rules with parent key authorized indexes.
|
||||||
Some(search_rules) => SearchRules::Map(
|
Some(search_rules) => SearchRules::Map(
|
||||||
key.indexes
|
key.indexes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|index| {
|
.filter_map(|index| {
|
||||||
search_rules
|
search_rules.get_index_search_rules(index.deref()).map(
|
||||||
.get_index_search_rules(&index)
|
|index_search_rules| {
|
||||||
.map(|index_search_rules| (index, Some(index_search_rules)))
|
(String::from(index), Some(index_search_rules))
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.collect(),
|
.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 {
|
} else if let Some(search_rules) = search_rules {
|
||||||
filters.search_rules = search_rules;
|
filters.search_rules = search_rules;
|
||||||
|
@ -3,12 +3,14 @@ use std::cmp::Reverse;
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::fs::create_dir_all;
|
use std::fs::create_dir_all;
|
||||||
|
use std::ops::Deref;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use enum_iterator::IntoEnumIterator;
|
use enum_iterator::IntoEnumIterator;
|
||||||
use hmac::{Hmac, Mac};
|
use hmac::{Hmac, Mac};
|
||||||
|
use meilisearch_types::star_or::StarOr;
|
||||||
use milli::heed::types::{ByteSlice, DecodeIgnore, SerdeJson};
|
use milli::heed::types::{ByteSlice, DecodeIgnore, SerdeJson};
|
||||||
use milli::heed::{Database, Env, EnvOpenOptions, RwTxn};
|
use milli::heed::{Database, Env, EnvOpenOptions, RwTxn};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
@ -92,7 +94,7 @@ impl HeedAuthStore {
|
|||||||
key.actions.clone()
|
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 {
|
for action in actions {
|
||||||
if no_index_restriction {
|
if no_index_restriction {
|
||||||
// If there is no index restriction we put None.
|
// If there is no index restriction we put None.
|
||||||
@ -102,7 +104,7 @@ impl HeedAuthStore {
|
|||||||
for index in key.indexes.iter() {
|
for index in key.indexes.iter() {
|
||||||
db.put(
|
db.put(
|
||||||
&mut wtxn,
|
&mut wtxn,
|
||||||
&(&uid, &action, Some(index.as_bytes())),
|
&(&uid, &action, Some(index.deref().as_bytes())),
|
||||||
&key.expires_at,
|
&key.expires_at,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ itertools = "0.10.3"
|
|||||||
jsonwebtoken = "8.0.1"
|
jsonwebtoken = "8.0.1"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
meilisearch-auth = { path = "../meilisearch-auth" }
|
meilisearch-auth = { path = "../meilisearch-auth" }
|
||||||
meilisearch-error = { path = "../meilisearch-error" }
|
meilisearch-types = { path = "../meilisearch-types" }
|
||||||
meilisearch-lib = { path = "../meilisearch-lib" }
|
meilisearch-lib = { path = "../meilisearch-lib" }
|
||||||
mime = "0.3.16"
|
mime = "0.3.16"
|
||||||
num_cpus = "1.13.1"
|
num_cpus = "1.13.1"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use actix_web as aweb;
|
use actix_web as aweb;
|
||||||
use aweb::error::{JsonPayloadError, QueryPayloadError};
|
use aweb::error::{JsonPayloadError, QueryPayloadError};
|
||||||
use meilisearch_error::{Code, ErrorCode, ResponseError};
|
use meilisearch_types::error::{Code, ErrorCode, ResponseError};
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum MeilisearchHttpError {
|
pub enum MeilisearchHttpError {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use meilisearch_error::{Code, ErrorCode};
|
use meilisearch_types::error::{Code, ErrorCode};
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum AuthenticationError {
|
pub enum AuthenticationError {
|
||||||
|
@ -5,12 +5,11 @@ use std::ops::Deref;
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use actix_web::FromRequest;
|
use actix_web::FromRequest;
|
||||||
|
use error::AuthenticationError;
|
||||||
use futures::future::err;
|
use futures::future::err;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use meilisearch_error::{Code, ResponseError};
|
|
||||||
|
|
||||||
use error::AuthenticationError;
|
|
||||||
use meilisearch_auth::{AuthController, AuthFilter};
|
use meilisearch_auth::{AuthController, AuthFilter};
|
||||||
|
use meilisearch_types::error::{Code, ResponseError};
|
||||||
|
|
||||||
pub struct GuardedData<P, D> {
|
pub struct GuardedData<P, D> {
|
||||||
data: D,
|
data: D,
|
||||||
|
@ -148,10 +148,10 @@ macro_rules! create_app {
|
|||||||
use actix_web::middleware::TrailingSlash;
|
use actix_web::middleware::TrailingSlash;
|
||||||
use actix_web::App;
|
use actix_web::App;
|
||||||
use actix_web::{middleware, web};
|
use actix_web::{middleware, web};
|
||||||
use meilisearch_error::ResponseError;
|
|
||||||
use meilisearch_http::error::MeilisearchHttpError;
|
use meilisearch_http::error::MeilisearchHttpError;
|
||||||
use meilisearch_http::routes;
|
use meilisearch_http::routes;
|
||||||
use meilisearch_http::{configure_data, dashboard};
|
use meilisearch_http::{configure_data, dashboard};
|
||||||
|
use meilisearch_types::error::ResponseError;
|
||||||
|
|
||||||
App::new()
|
App::new()
|
||||||
.configure(|s| configure_data(s, $data.clone(), $auth.clone(), &$opt, $analytics))
|
.configure(|s| configure_data(s, $data.clone(), $auth.clone(), &$opt, $analytics))
|
||||||
|
@ -7,7 +7,7 @@ use time::OffsetDateTime;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use meilisearch_auth::{error::AuthControllerError, Action, AuthController, Key};
|
use meilisearch_auth::{error::AuthControllerError, Action, AuthController, Key};
|
||||||
use meilisearch_error::{Code, ResponseError};
|
use meilisearch_types::error::{Code, ResponseError};
|
||||||
|
|
||||||
use crate::extractors::{
|
use crate::extractors::{
|
||||||
authentication::{policies::*, GuardedData},
|
authentication::{policies::*, GuardedData},
|
||||||
@ -151,7 +151,7 @@ impl KeyView {
|
|||||||
key: generated_key,
|
key: generated_key,
|
||||||
uid: key.uid,
|
uid: key.uid,
|
||||||
actions: key.actions,
|
actions: key.actions,
|
||||||
indexes: key.indexes,
|
indexes: key.indexes.into_iter().map(String::from).collect(),
|
||||||
expires_at: key.expires_at,
|
expires_at: key.expires_at,
|
||||||
created_at: key.created_at,
|
created_at: key.created_at,
|
||||||
updated_at: key.updated_at,
|
updated_at: key.updated_at,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use actix_web::{web, HttpRequest, HttpResponse};
|
use actix_web::{web, HttpRequest, HttpResponse};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use meilisearch_error::ResponseError;
|
|
||||||
use meilisearch_lib::MeiliSearch;
|
use meilisearch_lib::MeiliSearch;
|
||||||
|
use meilisearch_types::error::ResponseError;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use crate::analytics::Analytics;
|
use crate::analytics::Analytics;
|
||||||
|
@ -6,10 +6,11 @@ use actix_web::{web, HttpRequest, HttpResponse};
|
|||||||
use bstr::ByteSlice;
|
use bstr::ByteSlice;
|
||||||
use futures::{Stream, StreamExt};
|
use futures::{Stream, StreamExt};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use meilisearch_error::ResponseError;
|
|
||||||
use meilisearch_lib::index_controller::{DocumentAdditionFormat, Update};
|
use meilisearch_lib::index_controller::{DocumentAdditionFormat, Update};
|
||||||
use meilisearch_lib::milli::update::IndexDocumentsMethod;
|
use meilisearch_lib::milli::update::IndexDocumentsMethod;
|
||||||
use meilisearch_lib::MeiliSearch;
|
use meilisearch_lib::MeiliSearch;
|
||||||
|
use meilisearch_types::error::ResponseError;
|
||||||
|
use meilisearch_types::star_or::StarOr;
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@ -22,7 +23,7 @@ use crate::error::MeilisearchHttpError;
|
|||||||
use crate::extractors::authentication::{policies::*, GuardedData};
|
use crate::extractors::authentication::{policies::*, GuardedData};
|
||||||
use crate::extractors::payload::Payload;
|
use crate::extractors::payload::Payload;
|
||||||
use crate::extractors::sequential_extractor::SeqHandler;
|
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;
|
use crate::task::SummarizedTaskView;
|
||||||
|
|
||||||
static ACCEPTED_CONTENT_TYPE: Lazy<Vec<String>> = Lazy::new(|| {
|
static ACCEPTED_CONTENT_TYPE: Lazy<Vec<String>> = Lazy::new(|| {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use actix_web::{web, HttpRequest, HttpResponse};
|
use actix_web::{web, HttpRequest, HttpResponse};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use meilisearch_error::ResponseError;
|
|
||||||
use meilisearch_lib::index_controller::Update;
|
use meilisearch_lib::index_controller::Update;
|
||||||
use meilisearch_lib::MeiliSearch;
|
use meilisearch_lib::MeiliSearch;
|
||||||
|
use meilisearch_types::error::ResponseError;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use actix_web::{web, HttpRequest, HttpResponse};
|
use actix_web::{web, HttpRequest, HttpResponse};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use meilisearch_auth::IndexSearchRules;
|
use meilisearch_auth::IndexSearchRules;
|
||||||
use meilisearch_error::ResponseError;
|
|
||||||
use meilisearch_lib::index::{
|
use meilisearch_lib::index::{
|
||||||
SearchQuery, DEFAULT_CROP_LENGTH, DEFAULT_CROP_MARKER, DEFAULT_HIGHLIGHT_POST_TAG,
|
SearchQuery, DEFAULT_CROP_LENGTH, DEFAULT_CROP_MARKER, DEFAULT_HIGHLIGHT_POST_TAG,
|
||||||
DEFAULT_HIGHLIGHT_PRE_TAG, DEFAULT_SEARCH_LIMIT,
|
DEFAULT_HIGHLIGHT_PRE_TAG, DEFAULT_SEARCH_LIMIT,
|
||||||
};
|
};
|
||||||
use meilisearch_lib::MeiliSearch;
|
use meilisearch_lib::MeiliSearch;
|
||||||
|
use meilisearch_types::error::ResponseError;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_cs::vec::CS;
|
use serde_cs::vec::CS;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
use actix_web::{web, HttpRequest, HttpResponse};
|
use actix_web::{web, HttpRequest, HttpResponse};
|
||||||
use meilisearch_error::ResponseError;
|
|
||||||
use meilisearch_lib::index::{Settings, Unchecked};
|
use meilisearch_lib::index::{Settings, Unchecked};
|
||||||
use meilisearch_lib::index_controller::Update;
|
use meilisearch_lib::index_controller::Update;
|
||||||
use meilisearch_lib::MeiliSearch;
|
use meilisearch_lib::MeiliSearch;
|
||||||
|
use meilisearch_types::error::ResponseError;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use crate::analytics::Analytics;
|
use crate::analytics::Analytics;
|
||||||
@ -21,7 +21,7 @@ macro_rules! make_setting_route {
|
|||||||
use meilisearch_lib::milli::update::Setting;
|
use meilisearch_lib::milli::update::Setting;
|
||||||
use meilisearch_lib::{index::Settings, index_controller::Update, MeiliSearch};
|
use meilisearch_lib::{index::Settings, index_controller::Update, MeiliSearch};
|
||||||
|
|
||||||
use meilisearch_error::ResponseError;
|
use meilisearch_types::error::ResponseError;
|
||||||
use $crate::analytics::Analytics;
|
use $crate::analytics::Analytics;
|
||||||
use $crate::extractors::authentication::{policies::*, GuardedData};
|
use $crate::extractors::authentication::{policies::*, GuardedData};
|
||||||
use $crate::extractors::sequential_extractor::SeqHandler;
|
use $crate::extractors::sequential_extractor::SeqHandler;
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use actix_web::{web, HttpResponse};
|
use actix_web::{web, HttpResponse};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
use meilisearch_error::ResponseError;
|
|
||||||
use meilisearch_lib::index::{Settings, Unchecked};
|
use meilisearch_lib::index::{Settings, Unchecked};
|
||||||
use meilisearch_lib::MeiliSearch;
|
use meilisearch_lib::MeiliSearch;
|
||||||
|
use meilisearch_types::error::ResponseError;
|
||||||
|
use meilisearch_types::star_or::StarOr;
|
||||||
|
|
||||||
use crate::extractors::authentication::{policies::*, GuardedData};
|
use crate::extractors::authentication::{policies::*, GuardedData};
|
||||||
|
|
||||||
@ -27,26 +26,6 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
|||||||
.service(web::scope("/indexes").configure(indexes::configure));
|
.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<T> {
|
|
||||||
Star,
|
|
||||||
Other(T),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: FromStr> FromStr for StarOr<T> {
|
|
||||||
type Err = T::Err;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
if s.trim() == "*" {
|
|
||||||
Ok(StarOr::Star)
|
|
||||||
} else {
|
|
||||||
T::from_str(s).map(StarOr::Other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extracts the raw values from the `StarOr` types and
|
/// Extracts the raw values from the `StarOr` types and
|
||||||
/// return None if a `StarOr::Star` is encountered.
|
/// return None if a `StarOr::Star` is encountered.
|
||||||
pub fn fold_star_or<T, O>(content: impl IntoIterator<Item = StarOr<T>>) -> Option<O>
|
pub fn fold_star_or<T, O>(content: impl IntoIterator<Item = StarOr<T>>) -> Option<O>
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
use actix_web::{web, HttpRequest, HttpResponse};
|
use actix_web::{web, HttpRequest, HttpResponse};
|
||||||
use meilisearch_error::ResponseError;
|
|
||||||
use meilisearch_lib::tasks::task::{TaskContent, TaskEvent, TaskId};
|
use meilisearch_lib::tasks::task::{TaskContent, TaskEvent, TaskId};
|
||||||
use meilisearch_lib::tasks::TaskFilter;
|
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::Deserialize;
|
||||||
use serde_cs::vec::CS;
|
use serde_cs::vec::CS;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
@ -12,7 +14,7 @@ use crate::extractors::authentication::{policies::*, GuardedData};
|
|||||||
use crate::extractors::sequential_extractor::SeqHandler;
|
use crate::extractors::sequential_extractor::SeqHandler;
|
||||||
use crate::task::{TaskListView, TaskStatus, TaskType, TaskView};
|
use crate::task::{TaskListView, TaskStatus, TaskType, TaskView};
|
||||||
|
|
||||||
use super::{fold_star_or, StarOr};
|
use super::fold_star_or;
|
||||||
|
|
||||||
const DEFAULT_LIMIT: fn() -> usize = || 20;
|
const DEFAULT_LIMIT: fn() -> usize = || 20;
|
||||||
|
|
||||||
|
@ -3,12 +3,12 @@ use std::fmt::{self, Write};
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::write;
|
use std::write;
|
||||||
|
|
||||||
use meilisearch_error::ResponseError;
|
|
||||||
use meilisearch_lib::index::{Settings, Unchecked};
|
use meilisearch_lib::index::{Settings, Unchecked};
|
||||||
use meilisearch_lib::tasks::batch::BatchId;
|
use meilisearch_lib::tasks::batch::BatchId;
|
||||||
use meilisearch_lib::tasks::task::{
|
use meilisearch_lib::tasks::task::{
|
||||||
DocumentDeletion, Task, TaskContent, TaskEvent, TaskId, TaskResult,
|
DocumentDeletion, Task, TaskContent, TaskEvent, TaskId, TaskResult,
|
||||||
};
|
};
|
||||||
|
use meilisearch_types::error::ResponseError;
|
||||||
use serde::{Deserialize, Serialize, Serializer};
|
use serde::{Deserialize, Serialize, Serializer};
|
||||||
use time::{Duration, OffsetDateTime};
|
use time::{Duration, OffsetDateTime};
|
||||||
|
|
||||||
|
@ -358,6 +358,32 @@ async fn error_add_api_key_invalid_parameters_indexes() {
|
|||||||
assert_eq!(response, expected_response);
|
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]
|
#[actix_rt::test]
|
||||||
async fn error_add_api_key_invalid_parameters_actions() {
|
async fn error_add_api_key_invalid_parameters_actions() {
|
||||||
let mut server = Server::new_auth().await;
|
let mut server = Server::new_auth().await;
|
||||||
|
@ -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 (response, code) = index.add_documents(json!([{"id": 1}]), None).await;
|
||||||
|
|
||||||
let expected_response = json!({
|
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",
|
"code": "invalid_index_uid",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"
|
"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 (response, code) = index.update_documents(json!([{"id": 1}]), None).await;
|
||||||
|
|
||||||
let expected_response = json!({
|
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",
|
"code": "invalid_index_uid",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"
|
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"
|
||||||
|
@ -102,7 +102,7 @@ async fn error_create_with_invalid_index_uid() {
|
|||||||
let (response, code) = index.create(None).await;
|
let (response, code) = index.create(None).await;
|
||||||
|
|
||||||
let expected_response = json!({
|
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",
|
"code": "invalid_index_uid",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"
|
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"
|
||||||
|
@ -197,7 +197,7 @@ async fn error_update_setting_unexisting_index_invalid_uid() {
|
|||||||
assert_eq!(code, 400);
|
assert_eq!(code, 400);
|
||||||
|
|
||||||
let expected = json!({
|
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",
|
"code": "invalid_index_uid",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"});
|
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"});
|
||||||
|
@ -29,7 +29,7 @@ itertools = "0.10.3"
|
|||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
meilisearch-auth = { path = "../meilisearch-auth" }
|
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" }
|
milli = { git = "https://github.com/meilisearch/milli.git", tag = "v0.29.2" }
|
||||||
mime = "0.3.16"
|
mime = "0.3.16"
|
||||||
num_cpus = "1.13.1"
|
num_cpus = "1.13.1"
|
||||||
@ -59,7 +59,7 @@ whoami = { version = "1.2.1", optional = true }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.7.0"
|
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"
|
mockall = "0.11.0"
|
||||||
nelson = { git = "https://github.com/meilisearch/nelson.git", rev = "675f13885548fb415ead8fbb447e9e6d9314000a"}
|
nelson = { git = "https://github.com/meilisearch/nelson.git", rev = "675f13885548fb415ead8fbb447e9e6d9314000a"}
|
||||||
paste = "1.0.6"
|
paste = "1.0.6"
|
||||||
|
@ -2,7 +2,8 @@ use std::borrow::Borrow;
|
|||||||
use std::fmt::{self, Debug, Display};
|
use std::fmt::{self, Debug, Display};
|
||||||
use std::io::{self, BufRead, BufReader, BufWriter, Cursor, Read, Seek, Write};
|
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;
|
use milli::documents::DocumentBatchBuilder;
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, DocumentFormatError>;
|
type Result<T> = std::result::Result<T, DocumentFormatError>;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use meilisearch_error::Code;
|
use meilisearch_types::error::Code;
|
||||||
use milli::update::IndexDocumentsMethod;
|
use milli::update::IndexDocumentsMethod;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
@ -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 milli::update::IndexDocumentsMethod;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
@ -6,7 +7,6 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use super::v4::{Task, TaskContent, TaskEvent};
|
use super::v4::{Task, TaskContent, TaskEvent};
|
||||||
use crate::index::{Settings, Unchecked};
|
use crate::index::{Settings, Unchecked};
|
||||||
use crate::index_resolver::IndexUid;
|
|
||||||
use crate::tasks::task::{DocumentDeletion, TaskId, TaskResult};
|
use crate::tasks::task::{DocumentDeletion, TaskId, TaskResult};
|
||||||
|
|
||||||
use super::v2;
|
use super::v2;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use meilisearch_error::ResponseError;
|
use meilisearch_types::error::ResponseError;
|
||||||
|
use meilisearch_types::index_uid::IndexUid;
|
||||||
use milli::update::IndexDocumentsMethod;
|
use milli::update::IndexDocumentsMethod;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
@ -9,7 +10,6 @@ use crate::tasks::batch::BatchId;
|
|||||||
use crate::tasks::task::{
|
use crate::tasks::task::{
|
||||||
DocumentDeletion, TaskContent as NewTaskContent, TaskEvent as NewTaskEvent, TaskId, TaskResult,
|
DocumentDeletion, TaskContent as NewTaskContent, TaskEvent as NewTaskEvent, TaskId, TaskResult,
|
||||||
};
|
};
|
||||||
use crate::IndexUid;
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use meilisearch_auth::error::AuthControllerError;
|
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};
|
use crate::{index_resolver::error::IndexResolverError, tasks::error::TaskError};
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use meilisearch_error::{Code, ErrorCode};
|
use meilisearch_types::error::{Code, ErrorCode};
|
||||||
use milli::UserError;
|
use milli::UserError;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::error::Error;
|
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 serde_json::Value;
|
||||||
|
|
||||||
use crate::{error::MilliError, update_file_store};
|
use crate::{error::MilliError, update_file_store};
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use meilisearch_error::Code;
|
use meilisearch_types::error::{Code, ErrorCode};
|
||||||
use meilisearch_error::{internal_error, ErrorCode};
|
use meilisearch_types::index_uid::IndexUidFormatError;
|
||||||
|
use meilisearch_types::internal_error;
|
||||||
use tokio::task::JoinError;
|
use tokio::task::JoinError;
|
||||||
|
|
||||||
use super::DocumentAdditionFormat;
|
use super::DocumentAdditionFormat;
|
||||||
@ -63,3 +64,9 @@ impl ErrorCode for IndexControllerError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<IndexUidFormatError> for IndexControllerError {
|
||||||
|
fn from(err: IndexUidFormatError) -> Self {
|
||||||
|
IndexResolverError::from(err).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,6 +11,7 @@ use actix_web::error::PayloadError;
|
|||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
use meilisearch_types::index_uid::IndexUid;
|
||||||
use milli::update::IndexDocumentsMethod;
|
use milli::update::IndexDocumentsMethod;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
@ -37,7 +38,6 @@ use error::Result;
|
|||||||
use self::error::IndexControllerError;
|
use self::error::IndexControllerError;
|
||||||
use crate::index_resolver::index_store::{IndexStore, MapIndexStore};
|
use crate::index_resolver::index_store::{IndexStore, MapIndexStore};
|
||||||
use crate::index_resolver::meta_store::{HeedMetaStore, IndexMetaStore};
|
use crate::index_resolver::meta_store::{HeedMetaStore, IndexMetaStore};
|
||||||
pub use crate::index_resolver::IndexUid;
|
|
||||||
use crate::index_resolver::{create_index_resolver, IndexResolver};
|
use crate::index_resolver::{create_index_resolver, IndexResolver};
|
||||||
use crate::update_file_store::UpdateFileStore;
|
use crate::update_file_store::UpdateFileStore;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use meilisearch_error::{internal_error, Code, ErrorCode};
|
use meilisearch_types::{internal_error, Code, ErrorCode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
document_formats::DocumentFormatError,
|
document_formats::DocumentFormatError,
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use std::fmt;
|
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::mpsc::error::SendError as MpscSendError;
|
||||||
use tokio::sync::oneshot::error::RecvError as OneshotRecvError;
|
use tokio::sync::oneshot::error::RecvError as OneshotRecvError;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@ -25,8 +27,8 @@ pub enum IndexResolverError {
|
|||||||
UuidAlreadyExists(Uuid),
|
UuidAlreadyExists(Uuid),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Milli(#[from] milli::Error),
|
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 (_).")]
|
#[error("{0}")]
|
||||||
BadlyFormatted(String),
|
BadlyFormatted(#[from] IndexUidFormatError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<MpscSendError<T>> for IndexResolverError
|
impl<T> From<MpscSendError<T>> for IndexResolverError
|
||||||
|
@ -2,20 +2,17 @@ pub mod error;
|
|||||||
pub mod index_store;
|
pub mod index_store;
|
||||||
pub mod meta_store;
|
pub mod meta_store;
|
||||||
|
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::TryFrom;
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::str::FromStr;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use error::{IndexResolverError, Result};
|
use error::{IndexResolverError, Result};
|
||||||
use index_store::{IndexStore, MapIndexStore};
|
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 meta_store::{HeedMetaStore, IndexMetaStore};
|
||||||
use milli::heed::Env;
|
use milli::heed::Env;
|
||||||
use milli::update::{DocumentDeletionResult, IndexerConfig};
|
use milli::update::{DocumentDeletionResult, IndexerConfig};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use tokio::task::spawn_blocking;
|
use tokio::task::spawn_blocking;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@ -35,12 +32,6 @@ pub use real::IndexResolver;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub use test::MockIndexResolver as IndexResolver;
|
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(
|
pub fn create_index_resolver(
|
||||||
path: impl AsRef<Path>,
|
path: impl AsRef<Path>,
|
||||||
index_size: usize,
|
index_size: usize,
|
||||||
@ -53,81 +44,6 @@ pub fn create_index_resolver(
|
|||||||
Ok(IndexResolver::new(uuid_store, index_store, file_store))
|
Ok(IndexResolver::new(uuid_store, index_store, file_store))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexUid {
|
|
||||||
pub fn new_unchecked(s: impl AsRef<str>) -> 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<IndexUid> for String {
|
|
||||||
type Error = IndexUidFormatError;
|
|
||||||
|
|
||||||
fn try_into(self) -> std::result::Result<IndexUid, IndexUidFormatError> {
|
|
||||||
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<IndexUidFormatError> 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<IndexUid, IndexUidFormatError> {
|
|
||||||
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 {
|
mod real {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ mod update_file_store;
|
|||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
pub use index_controller::{IndexUid, MeiliSearch};
|
pub use index_controller::MeiliSearch;
|
||||||
pub use milli;
|
pub use milli;
|
||||||
pub use milli::heed;
|
pub use milli::heed;
|
||||||
|
|
||||||
|
@ -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 tokio::task::JoinError;
|
||||||
|
|
||||||
use crate::update_file_store::UpdateFileStoreError;
|
use crate::update_file_store::UpdateFileStoreError;
|
||||||
|
@ -55,9 +55,9 @@ mod test {
|
|||||||
task::{Task, TaskContent},
|
task::{Task, TaskContent},
|
||||||
};
|
};
|
||||||
use crate::update_file_store::{Result as FileStoreResult, UpdateFileStore};
|
use crate::update_file_store::{Result as FileStoreResult, UpdateFileStore};
|
||||||
use crate::IndexUid;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use meilisearch_types::index_uid::IndexUid;
|
||||||
use milli::update::IndexDocumentsMethod;
|
use milli::update::IndexDocumentsMethod;
|
||||||
use nelson::Mocker;
|
use nelson::Mocker;
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
@ -534,10 +534,11 @@ fn make_batch(tasks: &mut TaskQueue, config: &SchedulerConfig) -> Processing {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use meilisearch_types::index_uid::IndexUid;
|
||||||
use milli::update::IndexDocumentsMethod;
|
use milli::update::IndexDocumentsMethod;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{index_resolver::IndexUid, tasks::task::TaskContent};
|
use crate::tasks::task::TaskContent;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -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 milli::update::{DocumentAdditionResult, IndexDocumentsMethod};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
@ -6,7 +7,6 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use super::batch::BatchId;
|
use super::batch::BatchId;
|
||||||
use crate::index::{Settings, Unchecked};
|
use crate::index::{Settings, Unchecked};
|
||||||
use crate::index_resolver::IndexUid;
|
|
||||||
|
|
||||||
pub type TaskId = u32;
|
pub type TaskId = u32;
|
||||||
|
|
||||||
|
@ -267,13 +267,11 @@ impl TaskStore {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use crate::{
|
use crate::tasks::{scheduler::Processing, task_store::store::test::tmp_env};
|
||||||
tasks::{scheduler::Processing, task_store::store::test::tmp_env},
|
|
||||||
IndexUid,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use meilisearch_types::index_uid::IndexUid;
|
||||||
use nelson::Mocker;
|
use nelson::Mocker;
|
||||||
use proptest::{
|
use proptest::{
|
||||||
strategy::Strategy,
|
strategy::Strategy,
|
||||||
|
@ -179,11 +179,11 @@ impl Store {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use meilisearch_types::index_uid::IndexUid;
|
||||||
use milli::heed::EnvOpenOptions;
|
use milli::heed::EnvOpenOptions;
|
||||||
use nelson::Mocker;
|
use nelson::Mocker;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
use crate::index_resolver::IndexUid;
|
|
||||||
use crate::tasks::task::TaskContent;
|
use crate::tasks::task::TaskContent;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "meilisearch-error"
|
name = "meilisearch-types"
|
||||||
version = "0.28.0"
|
version = "0.28.0"
|
||||||
authors = ["marin <postma.marin@protonmail.com>"]
|
authors = ["marin <postma.marin@protonmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
85
meilisearch-types/src/index_uid.rs
Normal file
85
meilisearch-types/src/index_uid.rs
Normal file
@ -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<str>) -> 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<String> for IndexUid {
|
||||||
|
type Error = IndexUidFormatError;
|
||||||
|
|
||||||
|
fn try_from(uid: String) -> Result<Self, Self::Error> {
|
||||||
|
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<IndexUid, IndexUidFormatError> {
|
||||||
|
uid.to_string().try_into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<IndexUid> 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 {}
|
3
meilisearch-types/src/lib.rs
Normal file
3
meilisearch-types/src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod error;
|
||||||
|
pub mod index_uid;
|
||||||
|
pub mod star_or;
|
138
meilisearch-types/src/star_or.rs
Normal file
138
meilisearch-types/src/star_or.rs
Normal file
@ -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<T> {
|
||||||
|
Star,
|
||||||
|
Other(T),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: FromStr> FromStr for StarOr<T> {
|
||||||
|
type Err = T::Err;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
if s.trim() == "*" {
|
||||||
|
Ok(StarOr::Star)
|
||||||
|
} else {
|
||||||
|
T::from_str(s).map(StarOr::Other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Deref<Target = str>> Deref for StarOr<T> {
|
||||||
|
type Target = str;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
match self {
|
||||||
|
Self::Star => "*",
|
||||||
|
Self::Other(t) => t.deref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<String>> From<StarOr<T>> for String {
|
||||||
|
fn from(s: StarOr<T>) -> Self {
|
||||||
|
match s {
|
||||||
|
StarOr::Star => "*".to_string(),
|
||||||
|
StarOr::Other(t) => t.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialEq> PartialEq for StarOr<T> {
|
||||||
|
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<T: PartialEq + Eq> Eq for StarOr<T> {}
|
||||||
|
|
||||||
|
impl<'de, T, E> Deserialize<'de> for StarOr<T>
|
||||||
|
where
|
||||||
|
T: FromStr<Err = E>,
|
||||||
|
E: Display,
|
||||||
|
{
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
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<T>(PhantomData<T>);
|
||||||
|
|
||||||
|
impl<'de, T, FE> Visitor<'de> for StarOrVisitor<T>
|
||||||
|
where
|
||||||
|
T: FromStr<Err = FE>,
|
||||||
|
FE: Display,
|
||||||
|
{
|
||||||
|
type Value = StarOr<T>;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
|
||||||
|
formatter.write_str("a string")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<SE>(self, v: &str) -> Result<Self::Value, SE>
|
||||||
|
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<T> Serialize for StarOr<T>
|
||||||
|
where
|
||||||
|
T: Deref<Target = str>,
|
||||||
|
{
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
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<String>) {
|
||||||
|
let deserialized: StarOr<String> = 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);
|
||||||
|
}
|
||||||
|
}
|
@ -206,7 +206,7 @@ fn create_value(value: &Document, mut selectors: HashSet<&str>) -> Document {
|
|||||||
new_value
|
new_value
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_array(array: &Vec<Value>, selectors: &HashSet<&str>) -> Vec<Value> {
|
fn create_array(array: &[Value], selectors: &HashSet<&str>) -> Vec<Value> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
|
|
||||||
for value in array {
|
for value in array {
|
||||||
|
Loading…
Reference in New Issue
Block a user