mirror of
https://github.com/meilisearch/meilisearch.git
synced 2024-11-26 03:55:07 +08:00
rewrite the compat API to something more generic
This commit is contained in:
parent
2f47443458
commit
47e0288747
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1147,6 +1147,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"big_s",
|
"big_s",
|
||||||
"flate2",
|
"flate2",
|
||||||
|
"http",
|
||||||
"index",
|
"index",
|
||||||
"index-scheduler",
|
"index-scheduler",
|
||||||
"insta",
|
"insta",
|
||||||
|
@ -20,6 +20,7 @@ log = "0.4.17"
|
|||||||
index-scheduler = { path = "../index-scheduler" }
|
index-scheduler = { path = "../index-scheduler" }
|
||||||
meilisearch-auth = { path = "../meilisearch-auth" }
|
meilisearch-auth = { path = "../meilisearch-auth" }
|
||||||
meilisearch-types = { path = "../meilisearch-types" }
|
meilisearch-types = { path = "../meilisearch-types" }
|
||||||
|
http = "0.2.8"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
big_s = "1.0.2"
|
big_s = "1.0.2"
|
||||||
|
@ -1,12 +1,150 @@
|
|||||||
// pub mod v2;
|
// pub mod v2;
|
||||||
// pub mod v3;
|
// pub mod v3;
|
||||||
// pub mod v4;
|
// pub mod v4;
|
||||||
|
use crate::Result;
|
||||||
|
|
||||||
// pub mod v4_to_v5;
|
use self::{
|
||||||
|
v4_to_v5::CompatV4ToV5,
|
||||||
|
v5_to_v6::{CompatIndexV5ToV6, CompatV5ToV6},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
v5::V5Reader,
|
||||||
|
v6::{self, V6IndexReader, V6Reader},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod v4_to_v5;
|
||||||
pub mod v5_to_v6;
|
pub mod v5_to_v6;
|
||||||
|
|
||||||
pub struct Compat<From: ?Sized> {
|
pub enum Compat {
|
||||||
from: Box<From>,
|
Current(V6Reader),
|
||||||
|
Compat(CompatV5ToV6),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compat {
|
||||||
|
pub fn version(&self) -> crate::Version {
|
||||||
|
match self {
|
||||||
|
Compat::Current(current) => current.version(),
|
||||||
|
Compat::Compat(compat) => compat.version(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn date(&self) -> Option<time::OffsetDateTime> {
|
||||||
|
match self {
|
||||||
|
Compat::Current(current) => current.date(),
|
||||||
|
Compat::Compat(compat) => compat.date(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn instance_uid(&self) -> Result<Option<uuid::Uuid>> {
|
||||||
|
match self {
|
||||||
|
Compat::Current(current) => current.instance_uid(),
|
||||||
|
Compat::Compat(compat) => compat.instance_uid(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn indexes(&self) -> Result<Box<dyn Iterator<Item = Result<CompatIndex>> + '_>> {
|
||||||
|
match self {
|
||||||
|
Compat::Current(current) => {
|
||||||
|
let indexes = Box::new(current.indexes()?.map(|res| res.map(CompatIndex::from)))
|
||||||
|
as Box<dyn Iterator<Item = Result<CompatIndex>> + '_>;
|
||||||
|
Ok(indexes)
|
||||||
|
}
|
||||||
|
Compat::Compat(compat) => {
|
||||||
|
let indexes = Box::new(compat.indexes()?.map(|res| res.map(CompatIndex::from)))
|
||||||
|
as Box<dyn Iterator<Item = Result<CompatIndex>> + '_>;
|
||||||
|
Ok(indexes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tasks(
|
||||||
|
&mut self,
|
||||||
|
) -> Box<dyn Iterator<Item = Result<(v6::Task, Option<v6::UpdateFile>)>> + '_> {
|
||||||
|
match self {
|
||||||
|
Compat::Current(current) => current.tasks(),
|
||||||
|
Compat::Compat(compat) => compat.tasks(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn keys(&mut self) -> Box<dyn Iterator<Item = Result<v6::Key>> + '_> {
|
||||||
|
match self {
|
||||||
|
Compat::Current(current) => current.keys(),
|
||||||
|
Compat::Compat(compat) => compat.keys(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<V6Reader> for Compat {
|
||||||
|
fn from(value: V6Reader) -> Self {
|
||||||
|
Compat::Current(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CompatV5ToV6> for Compat {
|
||||||
|
fn from(value: CompatV5ToV6) -> Self {
|
||||||
|
Compat::Compat(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<V5Reader> for Compat {
|
||||||
|
fn from(value: V5Reader) -> Self {
|
||||||
|
Compat::Compat(value.to_v6())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CompatV4ToV5> for Compat {
|
||||||
|
fn from(value: CompatV4ToV5) -> Self {
|
||||||
|
Compat::Compat(value.to_v6())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum CompatIndex {
|
||||||
|
Current(v6::V6IndexReader),
|
||||||
|
Compat(CompatIndexV5ToV6),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompatIndex {
|
||||||
|
pub fn new_v6(v6: v6::V6IndexReader) -> CompatIndex {
|
||||||
|
CompatIndex::Current(v6)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn metadata(&self) -> &crate::IndexMetadata {
|
||||||
|
match self {
|
||||||
|
CompatIndex::Current(v6) => v6.metadata(),
|
||||||
|
CompatIndex::Compat(compat) => compat.metadata(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn documents(&mut self) -> Result<Box<dyn Iterator<Item = Result<v6::Document>> + '_>> {
|
||||||
|
match self {
|
||||||
|
CompatIndex::Current(v6) => v6
|
||||||
|
.documents()
|
||||||
|
.map(|iter| Box::new(iter) as Box<dyn Iterator<Item = Result<v6::Document>> + '_>),
|
||||||
|
CompatIndex::Compat(compat) => compat
|
||||||
|
.documents()
|
||||||
|
.map(|iter| Box::new(iter) as Box<dyn Iterator<Item = Result<v6::Document>> + '_>),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn settings(&mut self) -> Result<v6::Settings<v6::Checked>> {
|
||||||
|
match self {
|
||||||
|
CompatIndex::Current(v6) => v6.settings(),
|
||||||
|
CompatIndex::Compat(compat) => compat.settings(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<V6IndexReader> for CompatIndex {
|
||||||
|
fn from(value: V6IndexReader) -> Self {
|
||||||
|
CompatIndex::Current(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CompatIndexV5ToV6> for CompatIndex {
|
||||||
|
fn from(value: CompatIndexV5ToV6) -> Self {
|
||||||
|
CompatIndex::Compat(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the v1 version of the Asc ranking rules `asc(price)`and returns the field name.
|
/// Parses the v1 version of the Asc ranking rules `asc(price)`and returns the field name.
|
||||||
|
444
dump/src/reader/compat/v4_to_v5.rs
Normal file
444
dump/src/reader/compat/v4_to_v5.rs
Normal file
@ -0,0 +1,444 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
|
||||||
|
use crate::reader::{v4, v5, DumpReader, IndexReader};
|
||||||
|
use crate::Result;
|
||||||
|
|
||||||
|
use super::v5_to_v6::CompatV5ToV6;
|
||||||
|
|
||||||
|
pub struct CompatV4ToV5 {
|
||||||
|
from: v4::V4Reader,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompatV4ToV5 {
|
||||||
|
pub fn new(v4: v4::V4Reader) -> CompatV4ToV5 {
|
||||||
|
CompatV4ToV5 { from: v4 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_v6(self) -> CompatV5ToV6 {
|
||||||
|
CompatV5ToV6::Compat(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn version(&self) -> crate::Version {
|
||||||
|
self.from.version()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn date(&self) -> Option<time::OffsetDateTime> {
|
||||||
|
self.from.date()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn instance_uid(&self) -> Result<Option<uuid::Uuid>> {
|
||||||
|
self.from.instance_uid()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn indexes(&self) -> Result<impl Iterator<Item = Result<CompatIndexV4ToV5>> + '_> {
|
||||||
|
Ok(self.from.indexes()?.map(|index_reader| -> Result<_> {
|
||||||
|
let compat = CompatIndexV4ToV5::new(index_reader?);
|
||||||
|
Ok(compat)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tasks(
|
||||||
|
&mut self,
|
||||||
|
) -> Box<dyn Iterator<Item = Result<(v5::Task, Option<v5::UpdateFile>)>> + '_> {
|
||||||
|
Box::new(self.from.tasks().map(|task| {
|
||||||
|
task.map(|(task, content_file)| {
|
||||||
|
// let task_view: v4::tasks::TaskView = task.into();
|
||||||
|
|
||||||
|
let task = v5::Task {
|
||||||
|
id: task.id,
|
||||||
|
content: match task.content {
|
||||||
|
v4::tasks::TaskContent::DocumentAddition {
|
||||||
|
content_uuid,
|
||||||
|
merge_strategy,
|
||||||
|
primary_key,
|
||||||
|
documents_count,
|
||||||
|
allow_index_creation,
|
||||||
|
} => v5::tasks::TaskContent::DocumentAddition {
|
||||||
|
index_uid: v5::meta::IndexUid(task.index_uid.0),
|
||||||
|
content_uuid,
|
||||||
|
merge_strategy: match merge_strategy {
|
||||||
|
v4::tasks::IndexDocumentsMethod::ReplaceDocuments => {
|
||||||
|
v5::tasks::IndexDocumentsMethod::ReplaceDocuments
|
||||||
|
}
|
||||||
|
v4::tasks::IndexDocumentsMethod::UpdateDocuments => {
|
||||||
|
v5::tasks::IndexDocumentsMethod::UpdateDocuments
|
||||||
|
}
|
||||||
|
},
|
||||||
|
primary_key,
|
||||||
|
documents_count,
|
||||||
|
allow_index_creation,
|
||||||
|
},
|
||||||
|
v4::tasks::TaskContent::DocumentDeletion(deletion) => {
|
||||||
|
v5::tasks::TaskContent::DocumentDeletion {
|
||||||
|
index_uid: v5::meta::IndexUid(task.index_uid.0),
|
||||||
|
deletion: match deletion {
|
||||||
|
v4::tasks::DocumentDeletion::Clear => {
|
||||||
|
v5::tasks::DocumentDeletion::Clear
|
||||||
|
}
|
||||||
|
v4::tasks::DocumentDeletion::Ids(ids) => {
|
||||||
|
v5::tasks::DocumentDeletion::Ids(ids)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v4::tasks::TaskContent::SettingsUpdate {
|
||||||
|
settings,
|
||||||
|
is_deletion,
|
||||||
|
allow_index_creation,
|
||||||
|
} => v5::tasks::TaskContent::SettingsUpdate {
|
||||||
|
index_uid: v5::meta::IndexUid(task.index_uid.0),
|
||||||
|
settings: settings.into(),
|
||||||
|
is_deletion,
|
||||||
|
allow_index_creation,
|
||||||
|
},
|
||||||
|
v4::tasks::TaskContent::IndexDeletion => {
|
||||||
|
v5::tasks::TaskContent::IndexDeletion {
|
||||||
|
index_uid: v5::meta::IndexUid(task.index_uid.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v4::tasks::TaskContent::IndexCreation { primary_key } => {
|
||||||
|
v5::tasks::TaskContent::IndexCreation {
|
||||||
|
index_uid: v5::meta::IndexUid(task.index_uid.0),
|
||||||
|
primary_key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v4::tasks::TaskContent::IndexUpdate { primary_key } => {
|
||||||
|
v5::tasks::TaskContent::IndexUpdate {
|
||||||
|
index_uid: v5::meta::IndexUid(task.index_uid.0),
|
||||||
|
primary_key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
events: task
|
||||||
|
.events
|
||||||
|
.into_iter()
|
||||||
|
.map(|event| match event {
|
||||||
|
v4::tasks::TaskEvent::Created(date) => {
|
||||||
|
v5::tasks::TaskEvent::Created(date)
|
||||||
|
}
|
||||||
|
v4::tasks::TaskEvent::Batched {
|
||||||
|
timestamp,
|
||||||
|
batch_id,
|
||||||
|
} => v5::tasks::TaskEvent::Batched {
|
||||||
|
timestamp,
|
||||||
|
batch_id,
|
||||||
|
},
|
||||||
|
v4::tasks::TaskEvent::Processing(date) => {
|
||||||
|
v5::tasks::TaskEvent::Processing(date)
|
||||||
|
}
|
||||||
|
v4::tasks::TaskEvent::Succeded { result, timestamp } => {
|
||||||
|
v5::tasks::TaskEvent::Succeeded {
|
||||||
|
result: match result {
|
||||||
|
v4::tasks::TaskResult::DocumentAddition {
|
||||||
|
indexed_documents,
|
||||||
|
} => v5::tasks::TaskResult::DocumentAddition {
|
||||||
|
indexed_documents,
|
||||||
|
},
|
||||||
|
v4::tasks::TaskResult::DocumentDeletion {
|
||||||
|
deleted_documents,
|
||||||
|
} => v5::tasks::TaskResult::DocumentDeletion {
|
||||||
|
deleted_documents,
|
||||||
|
},
|
||||||
|
v4::tasks::TaskResult::ClearAll { deleted_documents } => {
|
||||||
|
v5::tasks::TaskResult::ClearAll { deleted_documents }
|
||||||
|
}
|
||||||
|
v4::tasks::TaskResult::Other => {
|
||||||
|
v5::tasks::TaskResult::Other
|
||||||
|
}
|
||||||
|
},
|
||||||
|
timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v4::tasks::TaskEvent::Failed { error, timestamp } => {
|
||||||
|
v5::tasks::TaskEvent::Failed {
|
||||||
|
error: v5::ResponseError::from(error),
|
||||||
|
timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
|
||||||
|
(task, content_file)
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn keys(&mut self) -> Box<dyn Iterator<Item = Result<v5::Key>> + '_> {
|
||||||
|
Box::new(self.from.keys().map(|key| {
|
||||||
|
key.map(|key| v5::Key {
|
||||||
|
description: key.description,
|
||||||
|
name: None,
|
||||||
|
uid: v5::keys::KeyId::new_v4(),
|
||||||
|
actions: key
|
||||||
|
.actions
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|action| action.into())
|
||||||
|
.collect(),
|
||||||
|
indexes: key
|
||||||
|
.indexes
|
||||||
|
.into_iter()
|
||||||
|
.map(|index| match index.as_str() {
|
||||||
|
"*" => v5::StarOr::Star,
|
||||||
|
_ => v5::StarOr::Other(v5::meta::IndexUid(index)),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
expires_at: key.expires_at,
|
||||||
|
created_at: key.created_at,
|
||||||
|
updated_at: key.updated_at,
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CompatIndexV4ToV5 {
|
||||||
|
from: v4::V4IndexReader,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompatIndexV4ToV5 {
|
||||||
|
pub fn new(v4: v4::V4IndexReader) -> CompatIndexV4ToV5 {
|
||||||
|
CompatIndexV4ToV5 { from: v4 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn metadata(&self) -> &crate::IndexMetadata {
|
||||||
|
self.from.metadata()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn documents(&mut self) -> Result<Box<dyn Iterator<Item = Result<v5::Document>> + '_>> {
|
||||||
|
self.from
|
||||||
|
.documents()
|
||||||
|
.map(|iter| Box::new(iter) as Box<dyn Iterator<Item = Result<v5::Document>> + '_>)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn settings(&mut self) -> Result<v5::Settings<v5::Checked>> {
|
||||||
|
Ok(v5::Settings::<v5::Unchecked>::from(self.from.settings()?).check())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<v4::Setting<T>> for v5::Setting<T> {
|
||||||
|
fn from(setting: v4::Setting<T>) -> Self {
|
||||||
|
match setting {
|
||||||
|
v4::Setting::Set(t) => v5::Setting::Set(t),
|
||||||
|
v4::Setting::Reset => v5::Setting::Reset,
|
||||||
|
v4::Setting::NotSet => v5::Setting::NotSet,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<v4::ResponseError> for v5::ResponseError {
|
||||||
|
fn from(error: v4::ResponseError) -> Self {
|
||||||
|
let code = match error.error_code.as_ref() {
|
||||||
|
"CreateIndex" => v5::Code::CreateIndex,
|
||||||
|
"IndexAlreadyExists" => v5::Code::IndexAlreadyExists,
|
||||||
|
"IndexNotFound" => v5::Code::IndexNotFound,
|
||||||
|
"InvalidIndexUid" => v5::Code::InvalidIndexUid,
|
||||||
|
"InvalidMinWordLengthForTypo" => v5::Code::InvalidMinWordLengthForTypo,
|
||||||
|
"InvalidState" => v5::Code::InvalidState,
|
||||||
|
"MissingPrimaryKey" => v5::Code::MissingPrimaryKey,
|
||||||
|
"PrimaryKeyAlreadyPresent" => v5::Code::PrimaryKeyAlreadyPresent,
|
||||||
|
"MaxFieldsLimitExceeded" => v5::Code::MaxFieldsLimitExceeded,
|
||||||
|
"MissingDocumentId" => v5::Code::MissingDocumentId,
|
||||||
|
"InvalidDocumentId" => v5::Code::InvalidDocumentId,
|
||||||
|
"Filter" => v5::Code::Filter,
|
||||||
|
"Sort" => v5::Code::Sort,
|
||||||
|
"BadParameter" => v5::Code::BadParameter,
|
||||||
|
"BadRequest" => v5::Code::BadRequest,
|
||||||
|
"DatabaseSizeLimitReached" => v5::Code::DatabaseSizeLimitReached,
|
||||||
|
"DocumentNotFound" => v5::Code::DocumentNotFound,
|
||||||
|
"Internal" => v5::Code::Internal,
|
||||||
|
"InvalidGeoField" => v5::Code::InvalidGeoField,
|
||||||
|
"InvalidRankingRule" => v5::Code::InvalidRankingRule,
|
||||||
|
"InvalidStore" => v5::Code::InvalidStore,
|
||||||
|
"InvalidToken" => v5::Code::InvalidToken,
|
||||||
|
"MissingAuthorizationHeader" => v5::Code::MissingAuthorizationHeader,
|
||||||
|
"NoSpaceLeftOnDevice" => v5::Code::NoSpaceLeftOnDevice,
|
||||||
|
"DumpNotFound" => v5::Code::DumpNotFound,
|
||||||
|
"TaskNotFound" => v5::Code::TaskNotFound,
|
||||||
|
"PayloadTooLarge" => v5::Code::PayloadTooLarge,
|
||||||
|
"RetrieveDocument" => v5::Code::RetrieveDocument,
|
||||||
|
"SearchDocuments" => v5::Code::SearchDocuments,
|
||||||
|
"UnsupportedMediaType" => v5::Code::UnsupportedMediaType,
|
||||||
|
"DumpAlreadyInProgress" => v5::Code::DumpAlreadyInProgress,
|
||||||
|
"DumpProcessFailed" => v5::Code::DumpProcessFailed,
|
||||||
|
"InvalidContentType" => v5::Code::InvalidContentType,
|
||||||
|
"MissingContentType" => v5::Code::MissingContentType,
|
||||||
|
"MalformedPayload" => v5::Code::MalformedPayload,
|
||||||
|
"MissingPayload" => v5::Code::MissingPayload,
|
||||||
|
"ApiKeyNotFound" => v5::Code::ApiKeyNotFound,
|
||||||
|
"MissingParameter" => v5::Code::MissingParameter,
|
||||||
|
"InvalidApiKeyActions" => v5::Code::InvalidApiKeyActions,
|
||||||
|
"InvalidApiKeyIndexes" => v5::Code::InvalidApiKeyIndexes,
|
||||||
|
"InvalidApiKeyExpiresAt" => v5::Code::InvalidApiKeyExpiresAt,
|
||||||
|
"InvalidApiKeyDescription" => v5::Code::InvalidApiKeyDescription,
|
||||||
|
other => {
|
||||||
|
log::warn!("Unknown error code {}", other);
|
||||||
|
v5::Code::UnretrievableErrorCode
|
||||||
|
}
|
||||||
|
};
|
||||||
|
v5::ResponseError::from_msg(error.message, code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<v4::Settings<T>> for v5::Settings<v5::Unchecked> {
|
||||||
|
fn from(settings: v4::Settings<T>) -> Self {
|
||||||
|
v5::Settings {
|
||||||
|
displayed_attributes: settings.displayed_attributes.into(),
|
||||||
|
searchable_attributes: settings.searchable_attributes.into(),
|
||||||
|
filterable_attributes: settings.filterable_attributes.into(),
|
||||||
|
sortable_attributes: settings.sortable_attributes.into(),
|
||||||
|
ranking_rules: settings.ranking_rules.into(),
|
||||||
|
stop_words: settings.stop_words.into(),
|
||||||
|
synonyms: settings.synonyms.into(),
|
||||||
|
distinct_attribute: settings.distinct_attribute.into(),
|
||||||
|
typo_tolerance: match settings.typo_tolerance {
|
||||||
|
v4::Setting::Set(typo) => v5::Setting::Set(v5::TypoTolerance {
|
||||||
|
enabled: typo.enabled.into(),
|
||||||
|
min_word_size_for_typos: match typo.min_word_size_for_typos {
|
||||||
|
v4::Setting::Set(t) => v5::Setting::Set(v5::MinWordSizeForTypos {
|
||||||
|
one_typo: t.one_typo.into(),
|
||||||
|
two_typos: t.two_typos.into(),
|
||||||
|
}),
|
||||||
|
v4::Setting::Reset => v5::Setting::Reset,
|
||||||
|
v4::Setting::NotSet => v5::Setting::NotSet,
|
||||||
|
},
|
||||||
|
disable_on_words: typo.disable_on_words.into(),
|
||||||
|
disable_on_attributes: typo.disable_on_attributes.into(),
|
||||||
|
}),
|
||||||
|
v4::Setting::Reset => v5::Setting::Reset,
|
||||||
|
v4::Setting::NotSet => v5::Setting::NotSet,
|
||||||
|
},
|
||||||
|
faceting: v5::Setting::NotSet,
|
||||||
|
pagination: v5::Setting::NotSet,
|
||||||
|
_kind: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<v4::Action> for Option<v5::Action> {
|
||||||
|
fn from(key: v4::Action) -> Self {
|
||||||
|
match key {
|
||||||
|
v4::Action::All => Some(v5::Action::All),
|
||||||
|
v4::Action::Search => Some(v5::Action::Search),
|
||||||
|
v4::Action::DocumentsAdd => Some(v5::Action::DocumentsAdd),
|
||||||
|
v4::Action::DocumentsGet => Some(v5::Action::DocumentsGet),
|
||||||
|
v4::Action::DocumentsDelete => Some(v5::Action::DocumentsDelete),
|
||||||
|
v4::Action::IndexesAdd => Some(v5::Action::IndexesAdd),
|
||||||
|
v4::Action::IndexesGet => Some(v5::Action::IndexesGet),
|
||||||
|
v4::Action::IndexesUpdate => Some(v5::Action::IndexesUpdate),
|
||||||
|
v4::Action::IndexesDelete => Some(v5::Action::IndexesDelete),
|
||||||
|
v4::Action::TasksGet => Some(v5::Action::TasksGet),
|
||||||
|
v4::Action::SettingsGet => Some(v5::Action::SettingsGet),
|
||||||
|
v4::Action::SettingsUpdate => Some(v5::Action::SettingsUpdate),
|
||||||
|
v4::Action::StatsGet => Some(v5::Action::StatsGet),
|
||||||
|
v4::Action::DumpsCreate => Some(v5::Action::DumpsCreate),
|
||||||
|
v4::Action::DumpsGet => None,
|
||||||
|
v4::Action::Version => Some(v5::Action::Version),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) mod test {
|
||||||
|
use std::{fs::File, io::BufReader};
|
||||||
|
|
||||||
|
use flate2::bufread::GzDecoder;
|
||||||
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compat_v4_v5() {
|
||||||
|
let dump = File::open("tests/assets/v4.dump").unwrap();
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let mut dump = BufReader::new(dump);
|
||||||
|
let gz = GzDecoder::new(&mut dump);
|
||||||
|
let mut archive = tar::Archive::new(gz);
|
||||||
|
archive.unpack(dir.path()).unwrap();
|
||||||
|
|
||||||
|
let mut dump = v4::V4Reader::open(dir).unwrap().to_v5();
|
||||||
|
|
||||||
|
// top level infos
|
||||||
|
insta::assert_display_snapshot!(dump.date().unwrap(), @"2022-10-04 15:55:10.344982459 +00:00:00");
|
||||||
|
insta::assert_display_snapshot!(dump.instance_uid().unwrap().unwrap(), @"9e15e977-f2ae-4761-943f-1eaf75fd736d");
|
||||||
|
|
||||||
|
// tasks
|
||||||
|
let tasks = dump.tasks().collect::<Result<Vec<_>>>().unwrap();
|
||||||
|
let (tasks, update_files): (Vec<_>, Vec<_>) = tasks.into_iter().unzip();
|
||||||
|
insta::assert_json_snapshot!(tasks);
|
||||||
|
assert_eq!(update_files.len(), 22);
|
||||||
|
assert!(update_files[0].is_none()); // the dump creation
|
||||||
|
assert!(update_files[1].is_some()); // the enqueued document addition
|
||||||
|
assert!(update_files[2..].iter().all(|u| u.is_none())); // everything already processed
|
||||||
|
|
||||||
|
// keys
|
||||||
|
let keys = dump.keys().collect::<Result<Vec<_>>>().unwrap();
|
||||||
|
insta::assert_json_snapshot!(keys);
|
||||||
|
|
||||||
|
// indexes
|
||||||
|
let mut indexes = dump.indexes().unwrap().collect::<Result<Vec<_>>>().unwrap();
|
||||||
|
// the index are not ordered in any way by default
|
||||||
|
indexes.sort_by_key(|index| index.metadata().uid.to_string());
|
||||||
|
|
||||||
|
let mut products = indexes.pop().unwrap();
|
||||||
|
let mut movies = indexes.pop().unwrap();
|
||||||
|
let mut spells = indexes.pop().unwrap();
|
||||||
|
assert!(indexes.is_empty());
|
||||||
|
|
||||||
|
// products
|
||||||
|
insta::assert_json_snapshot!(products.metadata(), { ".createdAt" => "[now]", ".updatedAt" => "[now]" }, @r###"
|
||||||
|
{
|
||||||
|
"uid": "products",
|
||||||
|
"primaryKey": "sku",
|
||||||
|
"createdAt": "[now]",
|
||||||
|
"updatedAt": "[now]"
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
|
||||||
|
insta::assert_debug_snapshot!(products.settings());
|
||||||
|
let documents = products
|
||||||
|
.documents()
|
||||||
|
.unwrap()
|
||||||
|
.collect::<Result<Vec<_>>>()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(documents.len(), 10);
|
||||||
|
insta::assert_json_snapshot!(documents);
|
||||||
|
|
||||||
|
// movies
|
||||||
|
insta::assert_json_snapshot!(movies.metadata(), { ".createdAt" => "[now]", ".updatedAt" => "[now]" }, @r###"
|
||||||
|
{
|
||||||
|
"uid": "movies",
|
||||||
|
"primaryKey": "id",
|
||||||
|
"createdAt": "[now]",
|
||||||
|
"updatedAt": "[now]"
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
|
||||||
|
insta::assert_debug_snapshot!(movies.settings());
|
||||||
|
let documents = movies
|
||||||
|
.documents()
|
||||||
|
.unwrap()
|
||||||
|
.collect::<Result<Vec<_>>>()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(documents.len(), 200);
|
||||||
|
insta::assert_debug_snapshot!(documents);
|
||||||
|
|
||||||
|
// spells
|
||||||
|
insta::assert_json_snapshot!(spells.metadata(), { ".createdAt" => "[now]", ".updatedAt" => "[now]" }, @r###"
|
||||||
|
{
|
||||||
|
"uid": "dnd_spells",
|
||||||
|
"primaryKey": "index",
|
||||||
|
"createdAt": "[now]",
|
||||||
|
"updatedAt": "[now]"
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
|
||||||
|
insta::assert_debug_snapshot!(spells.settings());
|
||||||
|
let documents = spells
|
||||||
|
.documents()
|
||||||
|
.unwrap()
|
||||||
|
.collect::<Result<Vec<_>>>()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(documents.len(), 10);
|
||||||
|
insta::assert_json_snapshot!(documents);
|
||||||
|
}
|
||||||
|
}
|
@ -1,46 +1,65 @@
|
|||||||
use crate::reader::{v5, v6, DumpReader, IndexReader};
|
use crate::reader::{v5, v6, DumpReader, IndexReader};
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
|
||||||
pub struct CompatV5ToV6 {
|
use super::v4_to_v5::{CompatIndexV4ToV5, CompatV4ToV5};
|
||||||
from: v5::V5Reader,
|
|
||||||
|
pub enum CompatV5ToV6 {
|
||||||
|
V5(v5::V5Reader),
|
||||||
|
Compat(CompatV4ToV5),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompatV5ToV6 {
|
impl CompatV5ToV6 {
|
||||||
pub fn new(v5: v5::V5Reader) -> CompatV5ToV6 {
|
pub fn new_v5(v5: v5::V5Reader) -> CompatV5ToV6 {
|
||||||
CompatV5ToV6 { from: v5 }
|
CompatV5ToV6::V5(v5)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DumpReader for CompatV5ToV6 {
|
|
||||||
fn version(&self) -> crate::Version {
|
|
||||||
self.from.version()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn date(&self) -> Option<time::OffsetDateTime> {
|
pub fn version(&self) -> crate::Version {
|
||||||
self.from.date()
|
match self {
|
||||||
|
CompatV5ToV6::V5(v5) => v5.version(),
|
||||||
|
CompatV5ToV6::Compat(compat) => compat.version(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instance_uid(&self) -> Result<Option<uuid::Uuid>> {
|
pub fn date(&self) -> Option<time::OffsetDateTime> {
|
||||||
self.from.instance_uid()
|
match self {
|
||||||
|
CompatV5ToV6::V5(v5) => v5.date(),
|
||||||
|
CompatV5ToV6::Compat(compat) => compat.date(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn indexes(
|
pub fn instance_uid(&self) -> Result<Option<uuid::Uuid>> {
|
||||||
&self,
|
match self {
|
||||||
) -> Result<Box<dyn Iterator<Item = Result<Box<dyn crate::reader::IndexReader + '_>>> + '_>>
|
CompatV5ToV6::V5(v5) => v5.instance_uid(),
|
||||||
{
|
CompatV5ToV6::Compat(compat) => compat.instance_uid(),
|
||||||
Ok(Box::new(self.from.indexes()?.map(
|
}
|
||||||
|index_reader| -> Result<_> {
|
|
||||||
let compat = Box::new(CompatIndexV5ToV6::new(index_reader?))
|
|
||||||
as Box<dyn crate::reader::IndexReader + '_>;
|
|
||||||
Ok(compat)
|
|
||||||
},
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tasks(
|
pub fn indexes(&self) -> Result<Box<dyn Iterator<Item = Result<CompatIndexV5ToV6>> + '_>> {
|
||||||
|
let indexes = match self {
|
||||||
|
CompatV5ToV6::V5(v5) => Box::new(
|
||||||
|
v5.indexes()?
|
||||||
|
.map(|index| index.map(CompatIndexV5ToV6::from)),
|
||||||
|
)
|
||||||
|
as Box<dyn Iterator<Item = Result<CompatIndexV5ToV6>> + '_>,
|
||||||
|
|
||||||
|
CompatV5ToV6::Compat(compat) => Box::new(
|
||||||
|
compat
|
||||||
|
.indexes()?
|
||||||
|
.map(|index| index.map(CompatIndexV5ToV6::from)),
|
||||||
|
)
|
||||||
|
as Box<dyn Iterator<Item = Result<CompatIndexV5ToV6>> + '_>,
|
||||||
|
};
|
||||||
|
Ok(indexes)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tasks(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Box<dyn Iterator<Item = Result<(v6::Task, Option<v6::UpdateFile>)>> + '_> {
|
) -> Box<dyn Iterator<Item = Result<(v6::Task, Option<v6::UpdateFile>)>> + '_> {
|
||||||
Box::new(self.from.tasks().map(|task| {
|
let tasks = match self {
|
||||||
|
CompatV5ToV6::V5(v5) => v5.tasks(),
|
||||||
|
CompatV5ToV6::Compat(compat) => compat.tasks(),
|
||||||
|
};
|
||||||
|
Box::new(tasks.map(|task| {
|
||||||
task.map(|(task, content_file)| {
|
task.map(|(task, content_file)| {
|
||||||
let task_view: v5::tasks::TaskView = task.into();
|
let task_view: v5::tasks::TaskView = task.into();
|
||||||
|
|
||||||
@ -101,8 +120,12 @@ impl DumpReader for CompatV5ToV6 {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keys(&mut self) -> Box<dyn Iterator<Item = Result<v6::Key>> + '_> {
|
pub fn keys(&mut self) -> Box<dyn Iterator<Item = Result<v6::Key>> + '_> {
|
||||||
Box::new(self.from.keys().map(|key| {
|
let keys = match self {
|
||||||
|
CompatV5ToV6::V5(v5) => v5.keys(),
|
||||||
|
CompatV5ToV6::Compat(compat) => compat.keys(),
|
||||||
|
};
|
||||||
|
Box::new(keys.map(|key| {
|
||||||
key.map(|key| v6::Key {
|
key.map(|key| v6::Key {
|
||||||
description: key.description,
|
description: key.description,
|
||||||
name: key.name,
|
name: key.name,
|
||||||
@ -130,29 +153,51 @@ impl DumpReader for CompatV5ToV6 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CompatIndexV5ToV6 {
|
pub enum CompatIndexV5ToV6 {
|
||||||
from: v5::V5IndexReader,
|
V5(v5::V5IndexReader),
|
||||||
|
Compat(CompatIndexV4ToV5),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<v5::V5IndexReader> for CompatIndexV5ToV6 {
|
||||||
|
fn from(index_reader: v5::V5IndexReader) -> Self {
|
||||||
|
Self::V5(index_reader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CompatIndexV4ToV5> for CompatIndexV5ToV6 {
|
||||||
|
fn from(index_reader: CompatIndexV4ToV5) -> Self {
|
||||||
|
Self::Compat(index_reader)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompatIndexV5ToV6 {
|
impl CompatIndexV5ToV6 {
|
||||||
pub fn new(v5: v5::V5IndexReader) -> CompatIndexV5ToV6 {
|
pub fn new_v5(v5: v5::V5IndexReader) -> CompatIndexV5ToV6 {
|
||||||
CompatIndexV5ToV6 { from: v5 }
|
CompatIndexV5ToV6::V5(v5)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IndexReader for CompatIndexV5ToV6 {
|
|
||||||
fn metadata(&self) -> &crate::IndexMetadata {
|
|
||||||
self.from.metadata()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn documents(&mut self) -> Result<Box<dyn Iterator<Item = Result<v6::Document>> + '_>> {
|
pub fn metadata(&self) -> &crate::IndexMetadata {
|
||||||
self.from
|
match self {
|
||||||
.documents()
|
CompatIndexV5ToV6::V5(v5) => v5.metadata(),
|
||||||
.map(|iter| Box::new(iter) as Box<dyn Iterator<Item = Result<v6::Document>> + '_>)
|
CompatIndexV5ToV6::Compat(compat) => compat.metadata(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn settings(&mut self) -> Result<v6::Settings<v6::Checked>> {
|
pub fn documents(&mut self) -> Result<Box<dyn Iterator<Item = Result<v6::Document>> + '_>> {
|
||||||
Ok(v6::Settings::<v6::Unchecked>::from(self.from.settings()?).check())
|
match self {
|
||||||
|
CompatIndexV5ToV6::V5(v5) => v5
|
||||||
|
.documents()
|
||||||
|
.map(|iter| Box::new(iter) as Box<dyn Iterator<Item = Result<v6::Document>> + '_>),
|
||||||
|
CompatIndexV5ToV6::Compat(compat) => compat
|
||||||
|
.documents()
|
||||||
|
.map(|iter| Box::new(iter) as Box<dyn Iterator<Item = Result<v6::Document>> + '_>),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn settings(&mut self) -> Result<v6::Settings<v6::Checked>> {
|
||||||
|
match self {
|
||||||
|
CompatIndexV5ToV6::V5(v5) => Ok(v6::Settings::from(v5.settings()?).check()),
|
||||||
|
CompatIndexV5ToV6::Compat(compat) => Ok(v6::Settings::from(compat.settings()?).check()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,8 @@ use uuid::Uuid;
|
|||||||
// use crate::reader::compat::Compat;
|
// use crate::reader::compat::Compat;
|
||||||
use crate::{IndexMetadata, Result, Version};
|
use crate::{IndexMetadata, Result, Version};
|
||||||
|
|
||||||
|
use self::compat::Compat;
|
||||||
|
|
||||||
// use self::loaders::{v2, v3, v4, v5};
|
// use self::loaders::{v2, v3, v4, v5};
|
||||||
|
|
||||||
// pub mod error;
|
// pub mod error;
|
||||||
@ -23,7 +25,7 @@ pub(self) mod v4;
|
|||||||
pub(self) mod v5;
|
pub(self) mod v5;
|
||||||
pub(self) mod v6;
|
pub(self) mod v6;
|
||||||
|
|
||||||
pub fn open(dump: impl Read) -> Result<Box<dyn DumpReader>> {
|
pub fn open(dump: impl Read) -> Result<Compat> {
|
||||||
let path = TempDir::new()?;
|
let path = TempDir::new()?;
|
||||||
let mut dump = BufReader::new(dump);
|
let mut dump = BufReader::new(dump);
|
||||||
let gz = GzDecoder::new(&mut dump);
|
let gz = GzDecoder::new(&mut dump);
|
||||||
@ -44,8 +46,8 @@ pub fn open(dump: impl Read) -> Result<Box<dyn DumpReader>> {
|
|||||||
Version::V2 => todo!(),
|
Version::V2 => todo!(),
|
||||||
Version::V3 => todo!(),
|
Version::V3 => todo!(),
|
||||||
Version::V4 => todo!(),
|
Version::V4 => todo!(),
|
||||||
Version::V5 => Ok(Box::new(v5::V5Reader::open(path)?.to_v6())),
|
Version::V5 => Ok(v5::V5Reader::open(path)?.to_v6().into()),
|
||||||
Version::V6 => Ok(Box::new(v6::V6Reader::open(path)?)),
|
Version::V6 => Ok(v6::V6Reader::open(path)?.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
320
dump/src/reader/v4/errors.rs
Normal file
320
dump/src/reader/v4/errors.rs
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use http::StatusCode;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[cfg_attr(feature = "test-traits", derive(proptest_derive::Arbitrary))]
|
||||||
|
pub struct ResponseError {
|
||||||
|
#[serde(skip)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "test-traits",
|
||||||
|
proptest(strategy = "strategy::status_code_strategy()")
|
||||||
|
)]
|
||||||
|
pub code: StatusCode,
|
||||||
|
pub message: String,
|
||||||
|
#[serde(rename = "code")]
|
||||||
|
pub error_code: String,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub error_type: String,
|
||||||
|
#[serde(rename = "link")]
|
||||||
|
pub error_link: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResponseError {
|
||||||
|
pub fn from_msg(message: String, code: Code) -> Self {
|
||||||
|
Self {
|
||||||
|
code: code.http(),
|
||||||
|
message,
|
||||||
|
error_code: code.err_code().error_name.to_string(),
|
||||||
|
error_type: code.type_(),
|
||||||
|
error_link: code.url(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ResponseError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.message.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for ResponseError {}
|
||||||
|
|
||||||
|
impl<T> From<T> for ResponseError
|
||||||
|
where
|
||||||
|
T: ErrorCode,
|
||||||
|
{
|
||||||
|
fn from(other: T) -> Self {
|
||||||
|
Self {
|
||||||
|
code: other.http_status(),
|
||||||
|
message: other.to_string(),
|
||||||
|
error_code: other.error_name(),
|
||||||
|
error_type: other.error_type(),
|
||||||
|
error_link: other.error_url(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ErrorCode: std::error::Error {
|
||||||
|
fn error_code(&self) -> Code;
|
||||||
|
|
||||||
|
/// returns the HTTP status code ascociated with the error
|
||||||
|
fn http_status(&self) -> StatusCode {
|
||||||
|
self.error_code().http()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns the doc url ascociated with the error
|
||||||
|
fn error_url(&self) -> String {
|
||||||
|
self.error_code().url()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns error name, used as error code
|
||||||
|
fn error_name(&self) -> String {
|
||||||
|
self.error_code().name()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return the error type
|
||||||
|
fn error_type(&self) -> String {
|
||||||
|
self.error_code().type_()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
enum ErrorType {
|
||||||
|
InternalError,
|
||||||
|
InvalidRequestError,
|
||||||
|
AuthenticationError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ErrorType {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
use ErrorType::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
InternalError => write!(f, "internal"),
|
||||||
|
InvalidRequestError => write!(f, "invalid_request"),
|
||||||
|
AuthenticationError => write!(f, "auth"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
|
||||||
|
pub enum Code {
|
||||||
|
// index related error
|
||||||
|
CreateIndex,
|
||||||
|
IndexAlreadyExists,
|
||||||
|
IndexNotFound,
|
||||||
|
InvalidIndexUid,
|
||||||
|
InvalidMinWordLengthForTypo,
|
||||||
|
|
||||||
|
// invalid state error
|
||||||
|
InvalidState,
|
||||||
|
MissingPrimaryKey,
|
||||||
|
PrimaryKeyAlreadyPresent,
|
||||||
|
|
||||||
|
MaxFieldsLimitExceeded,
|
||||||
|
MissingDocumentId,
|
||||||
|
InvalidDocumentId,
|
||||||
|
|
||||||
|
Filter,
|
||||||
|
Sort,
|
||||||
|
|
||||||
|
BadParameter,
|
||||||
|
BadRequest,
|
||||||
|
DatabaseSizeLimitReached,
|
||||||
|
DocumentNotFound,
|
||||||
|
Internal,
|
||||||
|
InvalidGeoField,
|
||||||
|
InvalidRankingRule,
|
||||||
|
InvalidStore,
|
||||||
|
InvalidToken,
|
||||||
|
MissingAuthorizationHeader,
|
||||||
|
NoSpaceLeftOnDevice,
|
||||||
|
DumpNotFound,
|
||||||
|
TaskNotFound,
|
||||||
|
PayloadTooLarge,
|
||||||
|
RetrieveDocument,
|
||||||
|
SearchDocuments,
|
||||||
|
UnsupportedMediaType,
|
||||||
|
|
||||||
|
DumpAlreadyInProgress,
|
||||||
|
DumpProcessFailed,
|
||||||
|
|
||||||
|
InvalidContentType,
|
||||||
|
MissingContentType,
|
||||||
|
MalformedPayload,
|
||||||
|
MissingPayload,
|
||||||
|
|
||||||
|
ApiKeyNotFound,
|
||||||
|
MissingParameter,
|
||||||
|
InvalidApiKeyActions,
|
||||||
|
InvalidApiKeyIndexes,
|
||||||
|
InvalidApiKeyExpiresAt,
|
||||||
|
InvalidApiKeyDescription,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Code {
|
||||||
|
/// ascociate a `Code` variant to the actual ErrCode
|
||||||
|
fn err_code(&self) -> ErrCode {
|
||||||
|
use Code::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
// index related errors
|
||||||
|
// create index is thrown on internal error while creating an index.
|
||||||
|
CreateIndex => {
|
||||||
|
ErrCode::internal("index_creation_failed", StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
}
|
||||||
|
IndexAlreadyExists => ErrCode::invalid("index_already_exists", StatusCode::CONFLICT),
|
||||||
|
// thrown when requesting an unexisting index
|
||||||
|
IndexNotFound => ErrCode::invalid("index_not_found", StatusCode::NOT_FOUND),
|
||||||
|
InvalidIndexUid => ErrCode::invalid("invalid_index_uid", StatusCode::BAD_REQUEST),
|
||||||
|
|
||||||
|
// invalid state error
|
||||||
|
InvalidState => ErrCode::internal("invalid_state", StatusCode::INTERNAL_SERVER_ERROR),
|
||||||
|
// thrown when no primary key has been set
|
||||||
|
MissingPrimaryKey => {
|
||||||
|
ErrCode::invalid("primary_key_inference_failed", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
// error thrown when trying to set an already existing primary key
|
||||||
|
PrimaryKeyAlreadyPresent => {
|
||||||
|
ErrCode::invalid("index_primary_key_already_exists", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
// invalid ranking rule
|
||||||
|
InvalidRankingRule => ErrCode::invalid("invalid_ranking_rule", StatusCode::BAD_REQUEST),
|
||||||
|
|
||||||
|
// invalid database
|
||||||
|
InvalidStore => {
|
||||||
|
ErrCode::internal("invalid_store_file", StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid document
|
||||||
|
MaxFieldsLimitExceeded => {
|
||||||
|
ErrCode::invalid("max_fields_limit_exceeded", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
MissingDocumentId => ErrCode::invalid("missing_document_id", StatusCode::BAD_REQUEST),
|
||||||
|
InvalidDocumentId => ErrCode::invalid("invalid_document_id", StatusCode::BAD_REQUEST),
|
||||||
|
|
||||||
|
// error related to filters
|
||||||
|
Filter => ErrCode::invalid("invalid_filter", StatusCode::BAD_REQUEST),
|
||||||
|
// error related to sorts
|
||||||
|
Sort => ErrCode::invalid("invalid_sort", StatusCode::BAD_REQUEST),
|
||||||
|
|
||||||
|
BadParameter => ErrCode::invalid("bad_parameter", StatusCode::BAD_REQUEST),
|
||||||
|
BadRequest => ErrCode::invalid("bad_request", StatusCode::BAD_REQUEST),
|
||||||
|
DatabaseSizeLimitReached => ErrCode::internal(
|
||||||
|
"database_size_limit_reached",
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
),
|
||||||
|
DocumentNotFound => ErrCode::invalid("document_not_found", StatusCode::NOT_FOUND),
|
||||||
|
Internal => ErrCode::internal("internal", StatusCode::INTERNAL_SERVER_ERROR),
|
||||||
|
InvalidGeoField => ErrCode::invalid("invalid_geo_field", StatusCode::BAD_REQUEST),
|
||||||
|
InvalidToken => ErrCode::authentication("invalid_api_key", StatusCode::FORBIDDEN),
|
||||||
|
MissingAuthorizationHeader => {
|
||||||
|
ErrCode::authentication("missing_authorization_header", StatusCode::UNAUTHORIZED)
|
||||||
|
}
|
||||||
|
TaskNotFound => ErrCode::invalid("task_not_found", StatusCode::NOT_FOUND),
|
||||||
|
DumpNotFound => ErrCode::invalid("dump_not_found", StatusCode::NOT_FOUND),
|
||||||
|
NoSpaceLeftOnDevice => {
|
||||||
|
ErrCode::internal("no_space_left_on_device", StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
}
|
||||||
|
PayloadTooLarge => ErrCode::invalid("payload_too_large", StatusCode::PAYLOAD_TOO_LARGE),
|
||||||
|
RetrieveDocument => {
|
||||||
|
ErrCode::internal("unretrievable_document", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
SearchDocuments => ErrCode::internal("search_error", StatusCode::BAD_REQUEST),
|
||||||
|
UnsupportedMediaType => {
|
||||||
|
ErrCode::invalid("unsupported_media_type", StatusCode::UNSUPPORTED_MEDIA_TYPE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// error related to dump
|
||||||
|
DumpAlreadyInProgress => {
|
||||||
|
ErrCode::invalid("dump_already_processing", StatusCode::CONFLICT)
|
||||||
|
}
|
||||||
|
DumpProcessFailed => {
|
||||||
|
ErrCode::internal("dump_process_failed", StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
}
|
||||||
|
MissingContentType => {
|
||||||
|
ErrCode::invalid("missing_content_type", StatusCode::UNSUPPORTED_MEDIA_TYPE)
|
||||||
|
}
|
||||||
|
MalformedPayload => ErrCode::invalid("malformed_payload", StatusCode::BAD_REQUEST),
|
||||||
|
InvalidContentType => {
|
||||||
|
ErrCode::invalid("invalid_content_type", StatusCode::UNSUPPORTED_MEDIA_TYPE)
|
||||||
|
}
|
||||||
|
MissingPayload => ErrCode::invalid("missing_payload", StatusCode::BAD_REQUEST),
|
||||||
|
|
||||||
|
// error related to keys
|
||||||
|
ApiKeyNotFound => ErrCode::invalid("api_key_not_found", StatusCode::NOT_FOUND),
|
||||||
|
MissingParameter => ErrCode::invalid("missing_parameter", StatusCode::BAD_REQUEST),
|
||||||
|
InvalidApiKeyActions => {
|
||||||
|
ErrCode::invalid("invalid_api_key_actions", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
InvalidApiKeyIndexes => {
|
||||||
|
ErrCode::invalid("invalid_api_key_indexes", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
InvalidApiKeyExpiresAt => {
|
||||||
|
ErrCode::invalid("invalid_api_key_expires_at", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
InvalidApiKeyDescription => {
|
||||||
|
ErrCode::invalid("invalid_api_key_description", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
InvalidMinWordLengthForTypo => {
|
||||||
|
ErrCode::invalid("invalid_min_word_length_for_typo", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return the HTTP status code ascociated with the `Code`
|
||||||
|
fn http(&self) -> StatusCode {
|
||||||
|
self.err_code().status_code
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return error name, used as error code
|
||||||
|
fn name(&self) -> String {
|
||||||
|
self.err_code().error_name.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return the error type
|
||||||
|
fn type_(&self) -> String {
|
||||||
|
self.err_code().error_type.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return the doc url ascociated with the error
|
||||||
|
fn url(&self) -> String {
|
||||||
|
format!("https://docs.meilisearch.com/errors#{}", self.name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal structure providing a convenient way to create error codes
|
||||||
|
struct ErrCode {
|
||||||
|
status_code: StatusCode,
|
||||||
|
error_type: ErrorType,
|
||||||
|
error_name: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ErrCode {
|
||||||
|
fn authentication(error_name: &'static str, status_code: StatusCode) -> ErrCode {
|
||||||
|
ErrCode {
|
||||||
|
status_code,
|
||||||
|
error_name,
|
||||||
|
error_type: ErrorType::AuthenticationError,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn internal(error_name: &'static str, status_code: StatusCode) -> ErrCode {
|
||||||
|
ErrCode {
|
||||||
|
status_code,
|
||||||
|
error_name,
|
||||||
|
error_type: ErrorType::InternalError,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalid(error_name: &'static str, status_code: StatusCode) -> ErrCode {
|
||||||
|
ErrCode {
|
||||||
|
status_code,
|
||||||
|
error_name,
|
||||||
|
error_type: ErrorType::InvalidRequestError,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,10 +9,11 @@ use tempfile::TempDir;
|
|||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
mod keys;
|
pub mod errors;
|
||||||
mod meta;
|
pub mod keys;
|
||||||
mod settings;
|
pub mod meta;
|
||||||
mod tasks;
|
pub mod settings;
|
||||||
|
pub mod tasks;
|
||||||
|
|
||||||
use crate::{IndexMetadata, Result, Version};
|
use crate::{IndexMetadata, Result, Version};
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ pub type StarOr<T> = meta::StarOr<T>;
|
|||||||
pub type IndexUid = meta::IndexUid;
|
pub type IndexUid = meta::IndexUid;
|
||||||
|
|
||||||
// everything related to the errors
|
// everything related to the errors
|
||||||
pub type ResponseError = tasks::ResponseError;
|
pub type ResponseError = errors::ResponseError;
|
||||||
pub type Code = meilisearch_types::error::Code;
|
pub type Code = meilisearch_types::error::Code;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@ -119,8 +120,8 @@ impl V4Reader {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tasks(&mut self) -> impl Iterator<Item = Result<(Task, Option<UpdateFile>)>> + '_ {
|
pub fn tasks(&mut self) -> Box<dyn Iterator<Item = Result<(Task, Option<UpdateFile>)>> + '_> {
|
||||||
(&mut self.tasks).lines().map(|line| -> Result<_> {
|
Box::new((&mut self.tasks).lines().map(|line| -> Result<_> {
|
||||||
let task: Task = serde_json::from_str(&line?)?;
|
let task: Task = serde_json::from_str(&line?)?;
|
||||||
if !task.is_finished() {
|
if !task.is_finished() {
|
||||||
if let Some(uuid) = task.get_content_uuid() {
|
if let Some(uuid) = task.get_content_uuid() {
|
||||||
@ -137,13 +138,15 @@ impl V4Reader {
|
|||||||
} else {
|
} else {
|
||||||
Ok((task, None))
|
Ok((task, None))
|
||||||
}
|
}
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn keys(&mut self) -> impl Iterator<Item = Result<Key>> + '_ {
|
pub fn keys(&mut self) -> Box<dyn Iterator<Item = Result<Key>> + '_> {
|
||||||
(&mut self.keys)
|
Box::new(
|
||||||
.lines()
|
(&mut self.keys)
|
||||||
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) })
|
.lines()
|
||||||
|
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ use time::{Duration, OffsetDateTime};
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
errors::ResponseError,
|
||||||
meta::IndexUid,
|
meta::IndexUid,
|
||||||
settings::{Settings, Unchecked},
|
settings::{Settings, Unchecked},
|
||||||
};
|
};
|
||||||
@ -148,19 +149,6 @@ pub enum TaskResult {
|
|||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(test, derive(serde::Serialize))]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct ResponseError {
|
|
||||||
pub message: String,
|
|
||||||
#[serde(rename = "code")]
|
|
||||||
pub error_code: String,
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
pub error_type: String,
|
|
||||||
#[serde(rename = "link")]
|
|
||||||
pub error_link: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Task {
|
impl Task {
|
||||||
/// Return true when a task is finished.
|
/// Return true when a task is finished.
|
||||||
/// A task is finished when its last state is either `Succeeded` or `Failed`.
|
/// A task is finished when its last state is either `Succeeded` or `Failed`.
|
||||||
|
285
dump/src/reader/v5/errors.rs
Normal file
285
dump/src/reader/v5/errors.rs
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use http::StatusCode;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[cfg_attr(feature = "test-traits", derive(proptest_derive::Arbitrary))]
|
||||||
|
#[cfg_attr(test, derive(serde::Serialize))]
|
||||||
|
pub struct ResponseError {
|
||||||
|
#[serde(skip)]
|
||||||
|
code: StatusCode,
|
||||||
|
|
||||||
|
pub message: String,
|
||||||
|
#[serde(rename = "code")]
|
||||||
|
pub error_code: String,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub error_type: String,
|
||||||
|
#[serde(rename = "link")]
|
||||||
|
pub error_link: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResponseError {
|
||||||
|
pub fn from_msg(message: String, code: Code) -> Self {
|
||||||
|
Self {
|
||||||
|
code: code.http(),
|
||||||
|
message,
|
||||||
|
error_code: code.err_code().error_name.to_string(),
|
||||||
|
error_type: code.type_(),
|
||||||
|
error_link: code.url(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Clone, Copy)]
|
||||||
|
#[cfg_attr(test, derive(serde::Serialize))]
|
||||||
|
pub enum Code {
|
||||||
|
// index related error
|
||||||
|
CreateIndex,
|
||||||
|
IndexAlreadyExists,
|
||||||
|
IndexNotFound,
|
||||||
|
InvalidIndexUid,
|
||||||
|
InvalidMinWordLengthForTypo,
|
||||||
|
|
||||||
|
// invalid state error
|
||||||
|
InvalidState,
|
||||||
|
MissingPrimaryKey,
|
||||||
|
PrimaryKeyAlreadyPresent,
|
||||||
|
|
||||||
|
MaxFieldsLimitExceeded,
|
||||||
|
MissingDocumentId,
|
||||||
|
InvalidDocumentId,
|
||||||
|
|
||||||
|
Filter,
|
||||||
|
Sort,
|
||||||
|
|
||||||
|
BadParameter,
|
||||||
|
BadRequest,
|
||||||
|
DatabaseSizeLimitReached,
|
||||||
|
DocumentNotFound,
|
||||||
|
Internal,
|
||||||
|
InvalidGeoField,
|
||||||
|
InvalidRankingRule,
|
||||||
|
InvalidStore,
|
||||||
|
InvalidToken,
|
||||||
|
MissingAuthorizationHeader,
|
||||||
|
NoSpaceLeftOnDevice,
|
||||||
|
DumpNotFound,
|
||||||
|
TaskNotFound,
|
||||||
|
PayloadTooLarge,
|
||||||
|
RetrieveDocument,
|
||||||
|
SearchDocuments,
|
||||||
|
UnsupportedMediaType,
|
||||||
|
|
||||||
|
DumpAlreadyInProgress,
|
||||||
|
DumpProcessFailed,
|
||||||
|
|
||||||
|
InvalidContentType,
|
||||||
|
MissingContentType,
|
||||||
|
MalformedPayload,
|
||||||
|
MissingPayload,
|
||||||
|
|
||||||
|
ApiKeyNotFound,
|
||||||
|
MissingParameter,
|
||||||
|
InvalidApiKeyActions,
|
||||||
|
InvalidApiKeyIndexes,
|
||||||
|
InvalidApiKeyExpiresAt,
|
||||||
|
InvalidApiKeyDescription,
|
||||||
|
InvalidApiKeyName,
|
||||||
|
InvalidApiKeyUid,
|
||||||
|
ImmutableField,
|
||||||
|
ApiKeyAlreadyExists,
|
||||||
|
|
||||||
|
UnretrievableErrorCode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Code {
|
||||||
|
/// associate a `Code` variant to the actual ErrCode
|
||||||
|
fn err_code(&self) -> ErrCode {
|
||||||
|
use Code::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
// index related errors
|
||||||
|
// create index is thrown on internal error while creating an index.
|
||||||
|
CreateIndex => {
|
||||||
|
ErrCode::internal("index_creation_failed", StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
}
|
||||||
|
IndexAlreadyExists => ErrCode::invalid("index_already_exists", StatusCode::CONFLICT),
|
||||||
|
// thrown when requesting an unexisting index
|
||||||
|
IndexNotFound => ErrCode::invalid("index_not_found", StatusCode::NOT_FOUND),
|
||||||
|
InvalidIndexUid => ErrCode::invalid("invalid_index_uid", StatusCode::BAD_REQUEST),
|
||||||
|
|
||||||
|
// invalid state error
|
||||||
|
InvalidState => ErrCode::internal("invalid_state", StatusCode::INTERNAL_SERVER_ERROR),
|
||||||
|
// thrown when no primary key has been set
|
||||||
|
MissingPrimaryKey => {
|
||||||
|
ErrCode::invalid("primary_key_inference_failed", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
// error thrown when trying to set an already existing primary key
|
||||||
|
PrimaryKeyAlreadyPresent => {
|
||||||
|
ErrCode::invalid("index_primary_key_already_exists", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
// invalid ranking rule
|
||||||
|
InvalidRankingRule => ErrCode::invalid("invalid_ranking_rule", StatusCode::BAD_REQUEST),
|
||||||
|
|
||||||
|
// invalid database
|
||||||
|
InvalidStore => {
|
||||||
|
ErrCode::internal("invalid_store_file", StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid document
|
||||||
|
MaxFieldsLimitExceeded => {
|
||||||
|
ErrCode::invalid("max_fields_limit_exceeded", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
MissingDocumentId => ErrCode::invalid("missing_document_id", StatusCode::BAD_REQUEST),
|
||||||
|
InvalidDocumentId => ErrCode::invalid("invalid_document_id", StatusCode::BAD_REQUEST),
|
||||||
|
|
||||||
|
// error related to filters
|
||||||
|
Filter => ErrCode::invalid("invalid_filter", StatusCode::BAD_REQUEST),
|
||||||
|
// error related to sorts
|
||||||
|
Sort => ErrCode::invalid("invalid_sort", StatusCode::BAD_REQUEST),
|
||||||
|
|
||||||
|
BadParameter => ErrCode::invalid("bad_parameter", StatusCode::BAD_REQUEST),
|
||||||
|
BadRequest => ErrCode::invalid("bad_request", StatusCode::BAD_REQUEST),
|
||||||
|
DatabaseSizeLimitReached => ErrCode::internal(
|
||||||
|
"database_size_limit_reached",
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
),
|
||||||
|
DocumentNotFound => ErrCode::invalid("document_not_found", StatusCode::NOT_FOUND),
|
||||||
|
Internal => ErrCode::internal("internal", StatusCode::INTERNAL_SERVER_ERROR),
|
||||||
|
InvalidGeoField => ErrCode::invalid("invalid_geo_field", StatusCode::BAD_REQUEST),
|
||||||
|
InvalidToken => ErrCode::authentication("invalid_api_key", StatusCode::FORBIDDEN),
|
||||||
|
MissingAuthorizationHeader => {
|
||||||
|
ErrCode::authentication("missing_authorization_header", StatusCode::UNAUTHORIZED)
|
||||||
|
}
|
||||||
|
TaskNotFound => ErrCode::invalid("task_not_found", StatusCode::NOT_FOUND),
|
||||||
|
DumpNotFound => ErrCode::invalid("dump_not_found", StatusCode::NOT_FOUND),
|
||||||
|
NoSpaceLeftOnDevice => {
|
||||||
|
ErrCode::internal("no_space_left_on_device", StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
}
|
||||||
|
PayloadTooLarge => ErrCode::invalid("payload_too_large", StatusCode::PAYLOAD_TOO_LARGE),
|
||||||
|
RetrieveDocument => {
|
||||||
|
ErrCode::internal("unretrievable_document", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
SearchDocuments => ErrCode::internal("search_error", StatusCode::BAD_REQUEST),
|
||||||
|
UnsupportedMediaType => {
|
||||||
|
ErrCode::invalid("unsupported_media_type", StatusCode::UNSUPPORTED_MEDIA_TYPE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// error related to dump
|
||||||
|
DumpAlreadyInProgress => {
|
||||||
|
ErrCode::invalid("dump_already_processing", StatusCode::CONFLICT)
|
||||||
|
}
|
||||||
|
DumpProcessFailed => {
|
||||||
|
ErrCode::internal("dump_process_failed", StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
}
|
||||||
|
MissingContentType => {
|
||||||
|
ErrCode::invalid("missing_content_type", StatusCode::UNSUPPORTED_MEDIA_TYPE)
|
||||||
|
}
|
||||||
|
MalformedPayload => ErrCode::invalid("malformed_payload", StatusCode::BAD_REQUEST),
|
||||||
|
InvalidContentType => {
|
||||||
|
ErrCode::invalid("invalid_content_type", StatusCode::UNSUPPORTED_MEDIA_TYPE)
|
||||||
|
}
|
||||||
|
MissingPayload => ErrCode::invalid("missing_payload", StatusCode::BAD_REQUEST),
|
||||||
|
|
||||||
|
// error related to keys
|
||||||
|
ApiKeyNotFound => ErrCode::invalid("api_key_not_found", StatusCode::NOT_FOUND),
|
||||||
|
MissingParameter => ErrCode::invalid("missing_parameter", StatusCode::BAD_REQUEST),
|
||||||
|
InvalidApiKeyActions => {
|
||||||
|
ErrCode::invalid("invalid_api_key_actions", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
InvalidApiKeyIndexes => {
|
||||||
|
ErrCode::invalid("invalid_api_key_indexes", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
InvalidApiKeyExpiresAt => {
|
||||||
|
ErrCode::invalid("invalid_api_key_expires_at", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
InvalidApiKeyDescription => {
|
||||||
|
ErrCode::invalid("invalid_api_key_description", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
InvalidApiKeyName => ErrCode::invalid("invalid_api_key_name", StatusCode::BAD_REQUEST),
|
||||||
|
InvalidApiKeyUid => ErrCode::invalid("invalid_api_key_uid", StatusCode::BAD_REQUEST),
|
||||||
|
ApiKeyAlreadyExists => ErrCode::invalid("api_key_already_exists", StatusCode::CONFLICT),
|
||||||
|
ImmutableField => ErrCode::invalid("immutable_field", StatusCode::BAD_REQUEST),
|
||||||
|
InvalidMinWordLengthForTypo => {
|
||||||
|
ErrCode::invalid("invalid_min_word_length_for_typo", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
UnretrievableErrorCode => {
|
||||||
|
ErrCode::invalid("unretrievable_error_code", StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return the HTTP status code associated with the `Code`
|
||||||
|
fn http(&self) -> StatusCode {
|
||||||
|
self.err_code().status_code
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return error name, used as error code
|
||||||
|
fn name(&self) -> String {
|
||||||
|
self.err_code().error_name.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return the error type
|
||||||
|
fn type_(&self) -> String {
|
||||||
|
self.err_code().error_type.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return the doc url associated with the error
|
||||||
|
fn url(&self) -> String {
|
||||||
|
format!("https://docs.meilisearch.com/errors#{}", self.name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal structure providing a convenient way to create error codes
|
||||||
|
struct ErrCode {
|
||||||
|
status_code: StatusCode,
|
||||||
|
error_type: ErrorType,
|
||||||
|
error_name: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ErrCode {
|
||||||
|
fn authentication(error_name: &'static str, status_code: StatusCode) -> ErrCode {
|
||||||
|
ErrCode {
|
||||||
|
status_code,
|
||||||
|
error_name,
|
||||||
|
error_type: ErrorType::AuthenticationError,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn internal(error_name: &'static str, status_code: StatusCode) -> ErrCode {
|
||||||
|
ErrCode {
|
||||||
|
status_code,
|
||||||
|
error_name,
|
||||||
|
error_type: ErrorType::InternalError,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalid(error_name: &'static str, status_code: StatusCode) -> ErrCode {
|
||||||
|
ErrCode {
|
||||||
|
status_code,
|
||||||
|
error_name,
|
||||||
|
error_type: ErrorType::InvalidRequestError,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
enum ErrorType {
|
||||||
|
InternalError,
|
||||||
|
InvalidRequestError,
|
||||||
|
AuthenticationError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ErrorType {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
use ErrorType::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
InternalError => write!(f, "internal"),
|
||||||
|
InvalidRequestError => write!(f, "invalid_request"),
|
||||||
|
AuthenticationError => write!(f, "auth"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -47,6 +47,7 @@ use crate::{IndexMetadata, Result, Version};
|
|||||||
|
|
||||||
use super::{compat::v5_to_v6::CompatV5ToV6, DumpReader, IndexReader};
|
use super::{compat::v5_to_v6::CompatV5ToV6, DumpReader, IndexReader};
|
||||||
|
|
||||||
|
pub mod errors;
|
||||||
pub mod keys;
|
pub mod keys;
|
||||||
pub mod meta;
|
pub mod meta;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
@ -80,8 +81,8 @@ pub type StarOr<T> = meta::StarOr<T>;
|
|||||||
pub type IndexUid = meta::IndexUid;
|
pub type IndexUid = meta::IndexUid;
|
||||||
|
|
||||||
// everything related to the errors
|
// everything related to the errors
|
||||||
pub type ResponseError = tasks::ResponseError;
|
pub type ResponseError = errors::ResponseError;
|
||||||
pub type Code = meilisearch_types::error::Code;
|
pub type Code = errors::Code;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@ -124,7 +125,7 @@ impl V5Reader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_v6(self) -> CompatV5ToV6 {
|
pub fn to_v6(self) -> CompatV5ToV6 {
|
||||||
CompatV5ToV6::new(self)
|
CompatV5ToV6::new_v5(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn version(&self) -> Version {
|
pub fn version(&self) -> Version {
|
||||||
@ -153,8 +154,8 @@ impl V5Reader {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tasks(&mut self) -> impl Iterator<Item = Result<(Task, Option<UpdateFile>)>> + '_ {
|
pub fn tasks(&mut self) -> Box<dyn Iterator<Item = Result<(Task, Option<UpdateFile>)>> + '_> {
|
||||||
(&mut self.tasks).lines().map(|line| -> Result<_> {
|
Box::new((&mut self.tasks).lines().map(|line| -> Result<_> {
|
||||||
let task: Task = serde_json::from_str(&line?)?;
|
let task: Task = serde_json::from_str(&line?)?;
|
||||||
if !task.is_finished() {
|
if !task.is_finished() {
|
||||||
if let Some(uuid) = task.get_content_uuid() {
|
if let Some(uuid) = task.get_content_uuid() {
|
||||||
@ -171,13 +172,15 @@ impl V5Reader {
|
|||||||
} else {
|
} else {
|
||||||
Ok((task, None))
|
Ok((task, None))
|
||||||
}
|
}
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn keys(&mut self) -> impl Iterator<Item = Result<Key>> + '_ {
|
pub fn keys(&mut self) -> Box<dyn Iterator<Item = Result<Key>> + '_> {
|
||||||
(&mut self.keys)
|
Box::new(
|
||||||
.lines()
|
(&mut self.keys)
|
||||||
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) })
|
.lines()
|
||||||
|
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ use time::{Duration, OffsetDateTime};
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
errors::ResponseError,
|
||||||
meta::IndexUid,
|
meta::IndexUid,
|
||||||
settings::{Settings, Unchecked},
|
settings::{Settings, Unchecked},
|
||||||
};
|
};
|
||||||
@ -113,19 +114,6 @@ pub enum TaskResult {
|
|||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(test, derive(serde::Serialize))]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct ResponseError {
|
|
||||||
pub message: String,
|
|
||||||
#[serde(rename = "code")]
|
|
||||||
pub error_code: String,
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
pub error_type: String,
|
|
||||||
#[serde(rename = "link")]
|
|
||||||
pub error_link: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Task {
|
impl Task {
|
||||||
/// Return true when a task is finished.
|
/// Return true when a task is finished.
|
||||||
/// A task is finished when its last state is either `Succeeded` or `Failed`.
|
/// A task is finished when its last state is either `Succeeded` or `Failed`.
|
||||||
|
@ -68,42 +68,45 @@ impl V6Reader {
|
|||||||
dump,
|
dump,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn version(&self) -> Version {
|
|
||||||
|
pub fn version(&self) -> Version {
|
||||||
Version::V6
|
Version::V6
|
||||||
}
|
}
|
||||||
|
|
||||||
fn date(&self) -> Option<OffsetDateTime> {
|
pub fn date(&self) -> Option<OffsetDateTime> {
|
||||||
Some(self.metadata.dump_date)
|
Some(self.metadata.dump_date)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instance_uid(&self) -> Result<Option<Uuid>> {
|
pub fn instance_uid(&self) -> Result<Option<Uuid>> {
|
||||||
Ok(Some(self.instance_uid))
|
Ok(Some(self.instance_uid))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn indexes(&self) -> Result<impl Iterator<Item = Result<V6IndexReader>> + '_> {
|
pub fn indexes(&self) -> Result<Box<dyn Iterator<Item = Result<V6IndexReader>> + '_>> {
|
||||||
let entries = fs::read_dir(self.dump.path().join("indexes"))?;
|
let entries = fs::read_dir(self.dump.path().join("indexes"))?;
|
||||||
Ok(entries
|
Ok(Box::new(
|
||||||
.map(|entry| -> Result<Option<_>> {
|
entries
|
||||||
let entry = entry?;
|
.map(|entry| -> Result<Option<_>> {
|
||||||
if entry.file_type()?.is_dir() {
|
let entry = entry?;
|
||||||
let index = V6IndexReader::new(
|
if entry.file_type()?.is_dir() {
|
||||||
entry
|
let index = V6IndexReader::new(
|
||||||
.file_name()
|
entry
|
||||||
.to_str()
|
.file_name()
|
||||||
.ok_or(Error::BadIndexName)?
|
.to_str()
|
||||||
.to_string(),
|
.ok_or(Error::BadIndexName)?
|
||||||
&entry.path(),
|
.to_string(),
|
||||||
)?;
|
&entry.path(),
|
||||||
Ok(Some(index))
|
)?;
|
||||||
} else {
|
Ok(Some(index))
|
||||||
Ok(None)
|
} else {
|
||||||
}
|
Ok(None)
|
||||||
})
|
}
|
||||||
.filter_map(|entry| entry.transpose()))
|
})
|
||||||
|
.filter_map(|entry| entry.transpose()),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tasks(&mut self) -> impl Iterator<Item = Result<(Task, Option<UpdateFile>)>> + '_ {
|
pub fn tasks(&mut self) -> Box<dyn Iterator<Item = Result<(Task, Option<UpdateFile>)>> + '_> {
|
||||||
(&mut self.tasks).lines().map(|line| -> Result<_> {
|
Box::new((&mut self.tasks).lines().map(|line| -> Result<_> {
|
||||||
let mut task: index_scheduler::TaskView = serde_json::from_str(&line?)?;
|
let mut task: index_scheduler::TaskView = serde_json::from_str(&line?)?;
|
||||||
// TODO: this can be removed once we can `Deserialize` the duration from the `TaskView`.
|
// TODO: this can be removed once we can `Deserialize` the duration from the `TaskView`.
|
||||||
if let Some((started_at, finished_at)) = task.started_at.zip(task.finished_at) {
|
if let Some((started_at, finished_at)) = task.started_at.zip(task.finished_at) {
|
||||||
@ -121,13 +124,15 @@ impl V6Reader {
|
|||||||
} else {
|
} else {
|
||||||
Ok((task, None))
|
Ok((task, None))
|
||||||
}
|
}
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keys(&mut self) -> impl Iterator<Item = Result<Key>> + '_ {
|
pub fn keys(&mut self) -> Box<dyn Iterator<Item = Result<Key>> + '_> {
|
||||||
(&mut self.keys)
|
Box::new(
|
||||||
.lines()
|
(&mut self.keys)
|
||||||
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) })
|
.lines()
|
||||||
|
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,7 +170,7 @@ impl DumpReader for V6Reader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct V6IndexReader {
|
pub struct V6IndexReader {
|
||||||
metadata: IndexMetadata,
|
metadata: IndexMetadata,
|
||||||
documents: BufReader<File>,
|
documents: BufReader<File>,
|
||||||
settings: BufReader<File>,
|
settings: BufReader<File>,
|
||||||
@ -184,17 +189,17 @@ impl V6IndexReader {
|
|||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn metadata(&self) -> &IndexMetadata {
|
pub fn metadata(&self) -> &IndexMetadata {
|
||||||
&self.metadata
|
&self.metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
fn documents(&mut self) -> Result<impl Iterator<Item = Result<Document>> + '_> {
|
pub fn documents(&mut self) -> Result<impl Iterator<Item = Result<Document>> + '_> {
|
||||||
Ok((&mut self.documents)
|
Ok((&mut self.documents)
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }))
|
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn settings(&mut self) -> Result<Settings<Checked>> {
|
pub fn settings(&mut self) -> Result<Settings<Checked>> {
|
||||||
let settings: Settings<Unchecked> = serde_json::from_reader(&mut self.settings)?;
|
let settings: Settings<Unchecked> = serde_json::from_reader(&mut self.settings)?;
|
||||||
Ok(settings.check())
|
Ok(settings.check())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user