mirror of
https://github.com/meilisearch/MeiliSearch
synced 2024-11-26 14:54:27 +01:00
Merge #228
228: Authentication rework r=curquiza a=MarinPostma In an attempt to fix #201, I ended up rewriting completely the authentication system we use. This is because actix doesn't allow to wrap a single route into a middleware, so we initially put each route into it's own service to use the authentication middleware. Routes are now grouped in resources, fixing #201. As for the authentication, I decided to take a very different approach, and ditch middleware altogether. Instead, I decided to use actix's [extractor](https://actix.rs/docs/extractors/). `Data` is now wrapped in a `GuardedData<P: Policy, T>` (where `T` is `Data`) in each route. The `Policy` trait, thanks to the `authenticate` method tell if a request is authorized to access the resources in the route. Concretely, before the server starts, it is configured with a `AuthConfig` instance that can either be `AuthConfig::NoAuth` when no auth is required at runtime, or `AuthConfig::Auth(Policies)`, where `Policies` maps the `Policy` type to it singleton instance. In the current implementation, and this to match the legacy meilisearch behaviour, each policy implementation contains a `HashSet` of token (`Vec<u8>` for now), that represents the user it can authenticate. When starting the program, each key (identified as a user) is given a set of `Policy`, representing its roles. The later is facilitated by the `create_users` macro, like so: ```rust create_users!( policies, master_key.as_bytes() => { Admin, Private, Public }, private_key.as_bytes() => { Private, Public }, public_key.as_bytes() => { Public } ); ``` This is some groundwork for later development on a full fledged authentication system for meilisearch. fix #201 Co-authored-by: marin postma <postma.marin@protonmail.com>
This commit is contained in:
commit
d7ca68d8e9
@ -10,23 +10,6 @@ use meilisearch_error::{Code, ErrorCode};
|
||||
use milli::UserError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum AuthenticationError {
|
||||
#[error("You must have an authorization token")]
|
||||
MissingAuthorizationHeader,
|
||||
#[error("Invalid API key")]
|
||||
InvalidToken(String),
|
||||
}
|
||||
|
||||
impl ErrorCode for AuthenticationError {
|
||||
fn error_code(&self) -> Code {
|
||||
match self {
|
||||
AuthenticationError::MissingAuthorizationHeader => Code::MissingAuthorizationHeader,
|
||||
AuthenticationError::InvalidToken(_) => Code::InvalidToken,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ResponseError {
|
||||
|
26
meilisearch-http/src/extractors/authentication/error.rs
Normal file
26
meilisearch-http/src/extractors/authentication/error.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use meilisearch_error::{Code, ErrorCode};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum AuthenticationError {
|
||||
#[error("You must have an authorization token")]
|
||||
MissingAuthorizationHeader,
|
||||
#[error("Invalid API key")]
|
||||
InvalidToken(String),
|
||||
// Triggered on configuration error.
|
||||
#[error("Irretrievable state")]
|
||||
IrretrievableState,
|
||||
#[error("Unknown authentication policy")]
|
||||
UnknownPolicy,
|
||||
}
|
||||
|
||||
impl ErrorCode for AuthenticationError {
|
||||
fn error_code(&self) -> Code {
|
||||
match self {
|
||||
AuthenticationError::MissingAuthorizationHeader => Code::MissingAuthorizationHeader,
|
||||
AuthenticationError::InvalidToken(_) => Code::InvalidToken,
|
||||
AuthenticationError::IrretrievableState => Code::Internal,
|
||||
AuthenticationError::UnknownPolicy => Code::Internal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
182
meilisearch-http/src/extractors/authentication/mod.rs
Normal file
182
meilisearch-http/src/extractors/authentication/mod.rs
Normal file
@ -0,0 +1,182 @@
|
||||
mod error;
|
||||
|
||||
use std::any::{Any, TypeId};
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
|
||||
use actix_web::FromRequest;
|
||||
use futures::future::err;
|
||||
use futures::future::{ok, Ready};
|
||||
|
||||
use crate::error::ResponseError;
|
||||
use error::AuthenticationError;
|
||||
|
||||
macro_rules! create_policies {
|
||||
($($name:ident), *) => {
|
||||
pub mod policies {
|
||||
use std::collections::HashSet;
|
||||
use crate::extractors::authentication::Policy;
|
||||
|
||||
$(
|
||||
#[derive(Debug, Default)]
|
||||
pub struct $name {
|
||||
inner: HashSet<Vec<u8>>
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub fn new() -> Self {
|
||||
Self { inner: HashSet::new() }
|
||||
}
|
||||
|
||||
pub fn add(&mut self, token: Vec<u8>) {
|
||||
&mut self.inner.insert(token);
|
||||
}
|
||||
}
|
||||
|
||||
impl Policy for $name {
|
||||
fn authenticate(&self, token: &[u8]) -> bool {
|
||||
self.inner.contains(token)
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
create_policies!(Public, Private, Admin);
|
||||
|
||||
/// Instanciate a `Policies`, filled with the given policies.
|
||||
macro_rules! init_policies {
|
||||
($($name:ident), *) => {
|
||||
{
|
||||
let mut policies = crate::extractors::authentication::Policies::new();
|
||||
$(
|
||||
let policy = $name::new();
|
||||
policies.insert(policy);
|
||||
)*
|
||||
policies
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Adds user to all specified policies.
|
||||
macro_rules! create_users {
|
||||
($policies:ident, $($user:expr => { $($policy:ty), * }), *) => {
|
||||
{
|
||||
$(
|
||||
$(
|
||||
$policies.get_mut::<$policy>().map(|p| p.add($user.to_owned()));
|
||||
)*
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct GuardedData<T, D> {
|
||||
data: D,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T, D> Deref for GuardedData<T, D> {
|
||||
type Target = D;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Policy {
|
||||
fn authenticate(&self, token: &[u8]) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Policies {
|
||||
inner: HashMap<TypeId, Box<dyn Any>>,
|
||||
}
|
||||
|
||||
impl Policies {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert<S: Policy + 'static>(&mut self, policy: S) {
|
||||
self.inner.insert(TypeId::of::<S>(), Box::new(policy));
|
||||
}
|
||||
|
||||
pub fn get<S: Policy + 'static>(&self) -> Option<&S> {
|
||||
self.inner
|
||||
.get(&TypeId::of::<S>())
|
||||
.and_then(|p| p.downcast_ref::<S>())
|
||||
}
|
||||
|
||||
pub fn get_mut<S: Policy + 'static>(&mut self) -> Option<&mut S> {
|
||||
self.inner
|
||||
.get_mut(&TypeId::of::<S>())
|
||||
.and_then(|p| p.downcast_mut::<S>())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Policies {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AuthConfig {
|
||||
NoAuth,
|
||||
Auth(Policies),
|
||||
}
|
||||
|
||||
impl Default for AuthConfig {
|
||||
fn default() -> Self {
|
||||
Self::NoAuth
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Policy + 'static, D: 'static + Clone> FromRequest for GuardedData<P, D> {
|
||||
type Config = AuthConfig;
|
||||
|
||||
type Error = ResponseError;
|
||||
|
||||
type Future = Ready<Result<Self, Self::Error>>;
|
||||
|
||||
fn from_request(
|
||||
req: &actix_web::HttpRequest,
|
||||
_payload: &mut actix_http::Payload,
|
||||
) -> Self::Future {
|
||||
match req.app_data::<Self::Config>() {
|
||||
Some(config) => match config {
|
||||
AuthConfig::NoAuth => match req.app_data::<D>().cloned() {
|
||||
Some(data) => ok(Self {
|
||||
data,
|
||||
_marker: PhantomData,
|
||||
}),
|
||||
None => err(AuthenticationError::IrretrievableState.into()),
|
||||
},
|
||||
AuthConfig::Auth(policies) => match policies.get::<P>() {
|
||||
Some(policy) => match req.headers().get("x-meili-api-key") {
|
||||
Some(token) => {
|
||||
if policy.authenticate(token.as_bytes()) {
|
||||
match req.app_data::<D>().cloned() {
|
||||
Some(data) => ok(Self {
|
||||
data,
|
||||
_marker: PhantomData,
|
||||
}),
|
||||
None => err(AuthenticationError::IrretrievableState.into()),
|
||||
}
|
||||
} else {
|
||||
err(AuthenticationError::InvalidToken(String::from("hello")).into())
|
||||
}
|
||||
}
|
||||
None => err(AuthenticationError::MissingAuthorizationHeader.into()),
|
||||
},
|
||||
None => err(AuthenticationError::UnknownPolicy.into()),
|
||||
},
|
||||
},
|
||||
None => err(AuthenticationError::IrretrievableState.into()),
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +1,3 @@
|
||||
pub mod payload;
|
||||
#[macro_use]
|
||||
pub mod authentication;
|
||||
|
@ -1,150 +0,0 @@
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use actix_web::body::Body;
|
||||
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
|
||||
use actix_web::web;
|
||||
use actix_web::ResponseError as _;
|
||||
use futures::future::{ok, Future, Ready};
|
||||
use futures::ready;
|
||||
use pin_project::pin_project;
|
||||
|
||||
use crate::error::{AuthenticationError, ResponseError};
|
||||
use crate::Data;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Authentication {
|
||||
Public,
|
||||
Private,
|
||||
Admin,
|
||||
}
|
||||
|
||||
impl<S: 'static> Transform<S, ServiceRequest> for Authentication
|
||||
where
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<Body>, Error = actix_web::Error>,
|
||||
{
|
||||
type Response = ServiceResponse<Body>;
|
||||
type Error = actix_web::Error;
|
||||
type InitError = ();
|
||||
type Transform = LoggingMiddleware<S>;
|
||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||
|
||||
fn new_transform(&self, service: S) -> Self::Future {
|
||||
ok(LoggingMiddleware {
|
||||
acl: *self,
|
||||
service,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LoggingMiddleware<S> {
|
||||
acl: Authentication,
|
||||
service: S,
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
impl<S> Service<ServiceRequest> for LoggingMiddleware<S>
|
||||
where
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<Body>, Error = actix_web::Error>,
|
||||
{
|
||||
type Response = ServiceResponse<Body>;
|
||||
type Error = actix_web::Error;
|
||||
type Future = AuthenticationFuture<S>;
|
||||
|
||||
fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||
self.service.poll_ready(cx)
|
||||
}
|
||||
|
||||
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||
let data = req.app_data::<web::Data<Data>>().unwrap();
|
||||
|
||||
if data.api_keys().master.is_none() {
|
||||
return AuthenticationFuture::Authenticated(self.service.call(req));
|
||||
}
|
||||
|
||||
let auth_header = match req.headers().get("X-Meili-API-Key") {
|
||||
Some(auth) => match auth.to_str() {
|
||||
Ok(auth) => auth,
|
||||
Err(_) => return AuthenticationFuture::NoHeader(Some(req)),
|
||||
},
|
||||
None => return AuthenticationFuture::NoHeader(Some(req)),
|
||||
};
|
||||
|
||||
let authenticated = match self.acl {
|
||||
Authentication::Admin => data.api_keys().master.as_deref() == Some(auth_header),
|
||||
Authentication::Private => {
|
||||
data.api_keys().master.as_deref() == Some(auth_header)
|
||||
|| data.api_keys().private.as_deref() == Some(auth_header)
|
||||
}
|
||||
Authentication::Public => {
|
||||
data.api_keys().master.as_deref() == Some(auth_header)
|
||||
|| data.api_keys().private.as_deref() == Some(auth_header)
|
||||
|| data.api_keys().public.as_deref() == Some(auth_header)
|
||||
}
|
||||
};
|
||||
|
||||
if authenticated {
|
||||
AuthenticationFuture::Authenticated(self.service.call(req))
|
||||
} else {
|
||||
AuthenticationFuture::Refused(Some(req))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project(project = AuthProj)]
|
||||
pub enum AuthenticationFuture<S>
|
||||
where
|
||||
S: Service<ServiceRequest>,
|
||||
{
|
||||
Authenticated(#[pin] S::Future),
|
||||
NoHeader(Option<ServiceRequest>),
|
||||
Refused(Option<ServiceRequest>),
|
||||
}
|
||||
|
||||
impl<S> Future for AuthenticationFuture<S>
|
||||
where
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<Body>, Error = actix_web::Error>,
|
||||
{
|
||||
type Output = Result<ServiceResponse<Body>, actix_web::Error>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
match this {
|
||||
AuthProj::Authenticated(fut) => match ready!(fut.poll(cx)) {
|
||||
Ok(resp) => Poll::Ready(Ok(resp)),
|
||||
Err(e) => Poll::Ready(Err(e)),
|
||||
},
|
||||
AuthProj::NoHeader(req) => {
|
||||
match req.take() {
|
||||
Some(req) => {
|
||||
let response =
|
||||
ResponseError::from(AuthenticationError::MissingAuthorizationHeader);
|
||||
let response = response.error_response();
|
||||
let response = req.into_response(response);
|
||||
Poll::Ready(Ok(response))
|
||||
}
|
||||
// https://doc.rust-lang.org/nightly/std/future/trait.Future.html#panics
|
||||
None => unreachable!("poll called again on ready future"),
|
||||
}
|
||||
}
|
||||
AuthProj::Refused(req) => {
|
||||
match req.take() {
|
||||
Some(req) => {
|
||||
let bad_token = req
|
||||
.headers()
|
||||
.get("X-Meili-API-Key")
|
||||
.map(|h| h.to_str().map(String::from).unwrap_or_default())
|
||||
.unwrap_or_default();
|
||||
let response =
|
||||
ResponseError::from(AuthenticationError::InvalidToken(bad_token));
|
||||
let response = response.error_response();
|
||||
let response = req.into_response(response);
|
||||
Poll::Ready(Ok(response))
|
||||
}
|
||||
// https://doc.rust-lang.org/nightly/std/future/trait.Future.html#panics
|
||||
None => unreachable!("poll called again on ready future"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,4 @@
|
||||
pub mod authentication;
|
||||
pub mod compression;
|
||||
mod env;
|
||||
|
||||
pub use authentication::Authentication;
|
||||
pub use env::EnvSizer;
|
||||
|
@ -1,6 +1,7 @@
|
||||
pub mod data;
|
||||
#[macro_use]
|
||||
pub mod error;
|
||||
#[macro_use]
|
||||
pub mod extractors;
|
||||
pub mod helpers;
|
||||
mod index;
|
||||
@ -11,17 +12,21 @@ pub mod routes;
|
||||
#[cfg(all(not(debug_assertions), feature = "analytics"))]
|
||||
pub mod analytics;
|
||||
|
||||
use crate::extractors::authentication::AuthConfig;
|
||||
|
||||
pub use self::data::Data;
|
||||
pub use option::Opt;
|
||||
|
||||
use actix_web::web;
|
||||
|
||||
use extractors::authentication::policies::*;
|
||||
use extractors::payload::PayloadConfig;
|
||||
|
||||
pub fn configure_data(config: &mut web::ServiceConfig, data: Data) {
|
||||
let http_payload_size_limit = data.http_payload_size_limit();
|
||||
config
|
||||
.data(data)
|
||||
.data(data.clone())
|
||||
.app_data(data)
|
||||
.app_data(
|
||||
web::JsonConfig::default()
|
||||
.limit(http_payload_size_limit)
|
||||
@ -35,10 +40,30 @@ pub fn configure_data(config: &mut web::ServiceConfig, data: Data) {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn configure_auth(config: &mut web::ServiceConfig, data: &Data) {
|
||||
let keys = data.api_keys();
|
||||
let auth_config = if let Some(ref master_key) = keys.master {
|
||||
let private_key = keys.private.as_ref().unwrap();
|
||||
let public_key = keys.public.as_ref().unwrap();
|
||||
let mut policies = init_policies!(Public, Private, Admin);
|
||||
create_users!(
|
||||
policies,
|
||||
master_key.as_bytes() => { Admin, Private, Public },
|
||||
private_key.as_bytes() => { Private, Public },
|
||||
public_key.as_bytes() => { Public }
|
||||
);
|
||||
AuthConfig::Auth(policies)
|
||||
} else {
|
||||
AuthConfig::NoAuth
|
||||
};
|
||||
|
||||
config.app_data(auth_config);
|
||||
}
|
||||
|
||||
#[cfg(feature = "mini-dashboard")]
|
||||
pub fn dashboard(config: &mut web::ServiceConfig, enable_frontend: bool) {
|
||||
use actix_web_static_files::Resource;
|
||||
use actix_web::HttpResponse;
|
||||
use actix_web_static_files::Resource;
|
||||
|
||||
mod generated {
|
||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||
@ -49,27 +74,29 @@ pub fn dashboard(config: &mut web::ServiceConfig, enable_frontend: bool) {
|
||||
let mut scope = web::scope("/");
|
||||
// Generate routes for mini-dashboard assets
|
||||
for (path, resource) in generated.into_iter() {
|
||||
let Resource {mime_type, data, ..} = resource;
|
||||
let Resource {
|
||||
mime_type, data, ..
|
||||
} = resource;
|
||||
// Redirect index.html to /
|
||||
if path == "index.html" {
|
||||
config.service(web::resource("/").route(web::get().to(move || {
|
||||
HttpResponse::Ok().content_type(mime_type).body(data)
|
||||
})));
|
||||
config.service(web::resource("/").route(
|
||||
web::get().to(move || HttpResponse::Ok().content_type(mime_type).body(data)),
|
||||
));
|
||||
} else {
|
||||
scope = scope.service(web::resource(path).route(web::get().to(move || {
|
||||
HttpResponse::Ok().content_type(mime_type).body(data)
|
||||
})));
|
||||
scope = scope.service(web::resource(path).route(
|
||||
web::get().to(move || HttpResponse::Ok().content_type(mime_type).body(data)),
|
||||
));
|
||||
}
|
||||
}
|
||||
config.service(scope);
|
||||
} else {
|
||||
config.service(routes::running);
|
||||
config.service(web::resource("/").route(web::get().to(routes::running)));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "mini-dashboard"))]
|
||||
pub fn dashboard(config: &mut web::ServiceConfig, _enable_frontend: bool) {
|
||||
config.service(routes::running);
|
||||
config.service(web::resource("/").route(web::get().to(routes::running)));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
@ -80,10 +107,11 @@ macro_rules! create_app {
|
||||
use actix_web::App;
|
||||
use actix_web::{middleware, web};
|
||||
use meilisearch_http::routes::*;
|
||||
use meilisearch_http::{configure_data, dashboard};
|
||||
use meilisearch_http::{configure_auth, configure_data, dashboard};
|
||||
|
||||
App::new()
|
||||
.configure(|s| configure_data(s, $data.clone()))
|
||||
.configure(|s| configure_auth(s, &$data))
|
||||
.configure(document::services)
|
||||
.configure(index::services)
|
||||
.configure(search::services)
|
||||
|
@ -1,14 +1,12 @@
|
||||
use actix_web::{delete, get, post, put};
|
||||
use actix_web::{web, HttpResponse};
|
||||
use indexmap::IndexMap;
|
||||
use log::{debug, error};
|
||||
use log::debug;
|
||||
use milli::update::{IndexDocumentsMethod, UpdateFormat};
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::error::ResponseError;
|
||||
use crate::extractors::authentication::{policies::*, GuardedData};
|
||||
use crate::extractors::payload::Payload;
|
||||
use crate::helpers::Authentication;
|
||||
use crate::routes::IndexParam;
|
||||
use crate::Data;
|
||||
|
||||
@ -17,7 +15,6 @@ const DEFAULT_RETRIEVE_DOCUMENTS_LIMIT: usize = 20;
|
||||
|
||||
macro_rules! guard_content_type {
|
||||
($fn_name:ident, $guard_value:literal) => {
|
||||
#[allow(dead_code)]
|
||||
fn $fn_name(head: &actix_web::dev::RequestHead) -> bool {
|
||||
if let Some(content_type) = head.headers.get("Content-Type") {
|
||||
content_type
|
||||
@ -33,8 +30,6 @@ macro_rules! guard_content_type {
|
||||
|
||||
guard_content_type!(guard_json, "application/json");
|
||||
|
||||
type Document = IndexMap<String, Value>;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct DocumentParam {
|
||||
index_uid: String,
|
||||
@ -42,21 +37,27 @@ struct DocumentParam {
|
||||
}
|
||||
|
||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(get_document)
|
||||
.service(delete_document)
|
||||
.service(get_all_documents)
|
||||
.service(add_documents)
|
||||
.service(update_documents)
|
||||
.service(delete_documents)
|
||||
.service(clear_all_documents);
|
||||
cfg.service(
|
||||
web::scope("/indexes/{index_uid}/documents")
|
||||
.service(
|
||||
web::resource("")
|
||||
.route(web::get().to(get_all_documents))
|
||||
.route(web::post().guard(guard_json).to(add_documents))
|
||||
.route(web::put().guard(guard_json).to(update_documents))
|
||||
.route(web::delete().to(clear_all_documents)),
|
||||
)
|
||||
// this route needs to be before the /documents/{document_id} to match properly
|
||||
.service(web::resource("/delete-batch").route(web::post().to(delete_documents)))
|
||||
.service(
|
||||
web::resource("/{document_id}")
|
||||
.route(web::get().to(get_document))
|
||||
.route(web::delete().to(delete_document)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[get(
|
||||
"/indexes/{index_uid}/documents/{document_id}",
|
||||
wrap = "Authentication::Public"
|
||||
)]
|
||||
async fn get_document(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Public, Data>,
|
||||
path: web::Path<DocumentParam>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let index = path.index_uid.clone();
|
||||
@ -68,12 +69,8 @@ async fn get_document(
|
||||
Ok(HttpResponse::Ok().json(document))
|
||||
}
|
||||
|
||||
#[delete(
|
||||
"/indexes/{index_uid}/documents/{document_id}",
|
||||
wrap = "Authentication::Private"
|
||||
)]
|
||||
async fn delete_document(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Private, Data>,
|
||||
path: web::Path<DocumentParam>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let update_status = data
|
||||
@ -91,9 +88,8 @@ struct BrowseQuery {
|
||||
attributes_to_retrieve: Option<String>,
|
||||
}
|
||||
|
||||
#[get("/indexes/{index_uid}/documents", wrap = "Authentication::Public")]
|
||||
async fn get_all_documents(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Public, Data>,
|
||||
path: web::Path<IndexParam>,
|
||||
params: web::Query<BrowseQuery>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
@ -129,9 +125,8 @@ struct UpdateDocumentsQuery {
|
||||
|
||||
/// Route used when the payload type is "application/json"
|
||||
/// Used to add or replace documents
|
||||
#[post("/indexes/{index_uid}/documents", wrap = "Authentication::Private")]
|
||||
async fn add_documents(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Private, Data>,
|
||||
path: web::Path<IndexParam>,
|
||||
params: web::Query<UpdateDocumentsQuery>,
|
||||
body: Payload,
|
||||
@ -151,33 +146,8 @@ async fn add_documents(
|
||||
Ok(HttpResponse::Accepted().json(serde_json::json!({ "updateId": update_status.id() })))
|
||||
}
|
||||
|
||||
/// Default route for adding documents, this should return an error and redirect to the documentation
|
||||
#[post("/indexes/{index_uid}/documents", wrap = "Authentication::Private")]
|
||||
async fn add_documents_default(
|
||||
_data: web::Data<Data>,
|
||||
_path: web::Path<IndexParam>,
|
||||
_params: web::Query<UpdateDocumentsQuery>,
|
||||
_body: web::Json<Vec<Document>>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
error!("Unknown document type");
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Default route for adding documents, this should return an error and redirect to the documentation
|
||||
#[put("/indexes/{index_uid}/documents", wrap = "Authentication::Private")]
|
||||
async fn update_documents_default(
|
||||
_data: web::Data<Data>,
|
||||
_path: web::Path<IndexParam>,
|
||||
_params: web::Query<UpdateDocumentsQuery>,
|
||||
_body: web::Json<Vec<Document>>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
error!("Unknown document type");
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[put("/indexes/{index_uid}/documents", wrap = "Authentication::Private")]
|
||||
async fn update_documents(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Private, Data>,
|
||||
path: web::Path<IndexParam>,
|
||||
params: web::Query<UpdateDocumentsQuery>,
|
||||
body: Payload,
|
||||
@ -197,12 +167,8 @@ async fn update_documents(
|
||||
Ok(HttpResponse::Accepted().json(serde_json::json!({ "updateId": update.id() })))
|
||||
}
|
||||
|
||||
#[post(
|
||||
"/indexes/{index_uid}/documents/delete-batch",
|
||||
wrap = "Authentication::Private"
|
||||
)]
|
||||
async fn delete_documents(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Private, Data>,
|
||||
path: web::Path<IndexParam>,
|
||||
body: web::Json<Vec<Value>>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
@ -221,10 +187,8 @@ async fn delete_documents(
|
||||
Ok(HttpResponse::Accepted().json(serde_json::json!({ "updateId": update_status.id() })))
|
||||
}
|
||||
|
||||
/// delete all documents
|
||||
#[delete("/indexes/{index_uid}/documents", wrap = "Authentication::Private")]
|
||||
async fn clear_all_documents(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Private, Data>,
|
||||
path: web::Path<IndexParam>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let update_status = data.clear_documents(path.index_uid.clone()).await?;
|
||||
|
@ -1,18 +1,17 @@
|
||||
use actix_web::HttpResponse;
|
||||
use actix_web::{get, post, web};
|
||||
use log::debug;
|
||||
use actix_web::{web, HttpResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::ResponseError;
|
||||
use crate::helpers::Authentication;
|
||||
use crate::extractors::authentication::{policies::*, GuardedData};
|
||||
use crate::Data;
|
||||
|
||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(create_dump).service(get_dump_status);
|
||||
cfg.service(web::resource("/dumps").route(web::post().to(create_dump)))
|
||||
.service(web::resource("/dumps/{dump_uid}/status").route(web::get().to(get_dump_status)));
|
||||
}
|
||||
|
||||
#[post("/dumps", wrap = "Authentication::Private")]
|
||||
async fn create_dump(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
|
||||
async fn create_dump(data: GuardedData<Private, Data>) -> Result<HttpResponse, ResponseError> {
|
||||
let res = data.create_dump().await?;
|
||||
|
||||
debug!("returns: {:?}", res);
|
||||
@ -30,9 +29,8 @@ struct DumpParam {
|
||||
dump_uid: String,
|
||||
}
|
||||
|
||||
#[get("/dumps/{dump_uid}/status", wrap = "Authentication::Private")]
|
||||
async fn get_dump_status(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Private, Data>,
|
||||
path: web::Path<DumpParam>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let res = data.dump_status(path.dump_uid.clone()).await?;
|
||||
|
@ -1,13 +1,11 @@
|
||||
use actix_web::get;
|
||||
use actix_web::{web, HttpResponse};
|
||||
|
||||
use crate::error::ResponseError;
|
||||
|
||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(get_health);
|
||||
cfg.service(web::resource("/health").route(web::get().to(get_health)));
|
||||
}
|
||||
|
||||
#[get("/health")]
|
||||
async fn get_health() -> Result<HttpResponse, ResponseError> {
|
||||
Ok(HttpResponse::Ok().json(serde_json::json!({ "status": "available" })))
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
use actix_web::{delete, get, post, put};
|
||||
use actix_web::{web, HttpResponse};
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::debug;
|
||||
@ -6,34 +5,29 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{IndexParam, UpdateStatusResponse};
|
||||
use crate::error::ResponseError;
|
||||
use crate::helpers::Authentication;
|
||||
use crate::extractors::authentication::{policies::*, GuardedData};
|
||||
use crate::Data;
|
||||
|
||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(list_indexes)
|
||||
.service(get_index)
|
||||
.service(create_index)
|
||||
.service(update_index)
|
||||
.service(delete_index)
|
||||
.service(get_update_status)
|
||||
.service(get_all_updates_status);
|
||||
}
|
||||
|
||||
#[get("/indexes", wrap = "Authentication::Private")]
|
||||
async fn list_indexes(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
|
||||
let indexes = data.list_indexes().await?;
|
||||
debug!("returns: {:?}", indexes);
|
||||
Ok(HttpResponse::Ok().json(indexes))
|
||||
}
|
||||
|
||||
#[get("/indexes/{index_uid}", wrap = "Authentication::Private")]
|
||||
async fn get_index(
|
||||
data: web::Data<Data>,
|
||||
path: web::Path<IndexParam>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let meta = data.index(path.index_uid.clone()).await?;
|
||||
debug!("returns: {:?}", meta);
|
||||
Ok(HttpResponse::Ok().json(meta))
|
||||
cfg.service(
|
||||
web::resource("indexes")
|
||||
.route(web::get().to(list_indexes))
|
||||
.route(web::post().to(create_index)),
|
||||
)
|
||||
.service(
|
||||
web::resource("/indexes/{index_uid}")
|
||||
.route(web::get().to(get_index))
|
||||
.route(web::put().to(update_index))
|
||||
.route(web::delete().to(delete_index)),
|
||||
)
|
||||
.service(
|
||||
web::resource("/indexes/{index_uid}/updates")
|
||||
.route(web::get().to(get_all_updates_status))
|
||||
)
|
||||
.service(
|
||||
web::resource("/indexes/{index_uid}/updates/{update_id}")
|
||||
.route(web::get().to(get_update_status))
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@ -43,18 +37,6 @@ struct IndexCreateRequest {
|
||||
primary_key: Option<String>,
|
||||
}
|
||||
|
||||
#[post("/indexes", wrap = "Authentication::Private")]
|
||||
async fn create_index(
|
||||
data: web::Data<Data>,
|
||||
body: web::Json<IndexCreateRequest>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
debug!("called with params: {:?}", body);
|
||||
let body = body.into_inner();
|
||||
let meta = data.create_index(body.uid, body.primary_key).await?;
|
||||
debug!("returns: {:?}", meta);
|
||||
Ok(HttpResponse::Ok().json(meta))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
struct UpdateIndexRequest {
|
||||
@ -72,9 +54,32 @@ pub struct UpdateIndexResponse {
|
||||
primary_key: Option<String>,
|
||||
}
|
||||
|
||||
#[put("/indexes/{index_uid}", wrap = "Authentication::Private")]
|
||||
async fn list_indexes(data: GuardedData<Private, Data>) -> Result<HttpResponse, ResponseError> {
|
||||
let indexes = data.list_indexes().await?;
|
||||
debug!("returns: {:?}", indexes);
|
||||
Ok(HttpResponse::Ok().json(indexes))
|
||||
}
|
||||
|
||||
async fn create_index(
|
||||
data: GuardedData<Private, Data>,
|
||||
body: web::Json<IndexCreateRequest>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let body = body.into_inner();
|
||||
let meta = data.create_index(body.uid, body.primary_key).await?;
|
||||
Ok(HttpResponse::Ok().json(meta))
|
||||
}
|
||||
|
||||
async fn get_index(
|
||||
data: GuardedData<Private, Data>,
|
||||
path: web::Path<IndexParam>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let meta = data.index(path.index_uid.clone()).await?;
|
||||
debug!("returns: {:?}", meta);
|
||||
Ok(HttpResponse::Ok().json(meta))
|
||||
}
|
||||
|
||||
async fn update_index(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Private, Data>,
|
||||
path: web::Path<IndexParam>,
|
||||
body: web::Json<UpdateIndexRequest>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
@ -87,9 +92,8 @@ async fn update_index(
|
||||
Ok(HttpResponse::Ok().json(meta))
|
||||
}
|
||||
|
||||
#[delete("/indexes/{index_uid}", wrap = "Authentication::Private")]
|
||||
async fn delete_index(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Private, Data>,
|
||||
path: web::Path<IndexParam>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
data.delete_index(path.index_uid.clone()).await?;
|
||||
@ -102,12 +106,8 @@ struct UpdateParam {
|
||||
update_id: u64,
|
||||
}
|
||||
|
||||
#[get(
|
||||
"/indexes/{index_uid}/updates/{update_id}",
|
||||
wrap = "Authentication::Private"
|
||||
)]
|
||||
async fn get_update_status(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Private, Data>,
|
||||
path: web::Path<UpdateParam>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let params = path.into_inner();
|
||||
@ -119,9 +119,8 @@ async fn get_update_status(
|
||||
Ok(HttpResponse::Ok().json(meta))
|
||||
}
|
||||
|
||||
#[get("/indexes/{index_uid}/updates", wrap = "Authentication::Private")]
|
||||
async fn get_all_updates_status(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Private, Data>,
|
||||
path: web::Path<IndexParam>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let metas = data.get_updates_status(path.into_inner().index_uid).await?;
|
||||
|
@ -1,13 +1,11 @@
|
||||
use actix_web::get;
|
||||
use actix_web::web;
|
||||
use actix_web::HttpResponse;
|
||||
use actix_web::{web, HttpResponse};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::helpers::Authentication;
|
||||
use crate::extractors::authentication::{policies::*, GuardedData};
|
||||
use crate::Data;
|
||||
|
||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(list);
|
||||
cfg.service(web::resource("/keys").route(web::get().to(list)));
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@ -16,8 +14,7 @@ struct KeysResponse {
|
||||
public: Option<String>,
|
||||
}
|
||||
|
||||
#[get("/keys", wrap = "Authentication::Admin")]
|
||||
async fn list(data: web::Data<Data>) -> HttpResponse {
|
||||
async fn list(data: GuardedData<Admin, Data>) -> HttpResponse {
|
||||
let api_keys = data.api_keys.clone();
|
||||
HttpResponse::Ok().json(&KeysResponse {
|
||||
private: api_keys.private,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use actix_web::{get, HttpResponse};
|
||||
use actix_web::HttpResponse;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -220,7 +220,6 @@ impl IndexUpdateResponse {
|
||||
/// "status": "Meilisearch is running"
|
||||
/// }
|
||||
/// ```
|
||||
#[get("/")]
|
||||
pub async fn running() -> HttpResponse {
|
||||
HttpResponse::Ok().json(serde_json::json!({ "status": "MeiliSearch is running" }))
|
||||
}
|
||||
|
@ -1,18 +1,22 @@
|
||||
use std::collections::{BTreeSet, HashSet};
|
||||
|
||||
use actix_web::{get, post, web, HttpResponse};
|
||||
use log::debug;
|
||||
use actix_web::{web, HttpResponse};
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::error::ResponseError;
|
||||
use crate::helpers::Authentication;
|
||||
use crate::extractors::authentication::{policies::*, GuardedData};
|
||||
use crate::index::{default_crop_length, SearchQuery, DEFAULT_SEARCH_LIMIT};
|
||||
use crate::routes::IndexParam;
|
||||
use crate::Data;
|
||||
|
||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(search_with_post).service(search_with_url_query);
|
||||
cfg.service(
|
||||
web::resource("/indexes/{index_uid}/search")
|
||||
.route(web::get().to(search_with_url_query))
|
||||
.route(web::post().to(search_with_post)),
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
@ -73,9 +77,8 @@ impl From<SearchQueryGet> for SearchQuery {
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/indexes/{index_uid}/search", wrap = "Authentication::Public")]
|
||||
async fn search_with_url_query(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Admin, Data>,
|
||||
path: web::Path<IndexParam>,
|
||||
params: web::Query<SearchQueryGet>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
@ -86,9 +89,8 @@ async fn search_with_url_query(
|
||||
Ok(HttpResponse::Ok().json(search_result))
|
||||
}
|
||||
|
||||
#[post("/indexes/{index_uid}/search", wrap = "Authentication::Public")]
|
||||
async fn search_with_post(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Public, Data>,
|
||||
path: web::Path<IndexParam>,
|
||||
params: web::Json<SearchQuery>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use actix_web::{delete, get, post, web, HttpResponse};
|
||||
use log::debug;
|
||||
use actix_web::{web, HttpResponse};
|
||||
|
||||
use crate::helpers::Authentication;
|
||||
use crate::extractors::authentication::{policies::*, GuardedData};
|
||||
use crate::index::Settings;
|
||||
use crate::Data;
|
||||
use crate::{error::ResponseError, index::Unchecked};
|
||||
@ -11,16 +11,15 @@ macro_rules! make_setting_route {
|
||||
($route:literal, $type:ty, $attr:ident, $camelcase_attr:literal) => {
|
||||
mod $attr {
|
||||
use log::debug;
|
||||
use actix_web::{web, HttpResponse};
|
||||
use actix_web::{web, HttpResponse, Resource};
|
||||
|
||||
use crate::data;
|
||||
use crate::error::ResponseError;
|
||||
use crate::helpers::Authentication;
|
||||
use crate::index::Settings;
|
||||
use crate::extractors::authentication::{GuardedData, policies::*};
|
||||
|
||||
#[actix_web::delete($route, wrap = "Authentication::Private")]
|
||||
pub async fn delete(
|
||||
data: web::Data<data::Data>,
|
||||
async fn delete(
|
||||
data: GuardedData<Private, data::Data>,
|
||||
index_uid: web::Path<String>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
use crate::index::Settings;
|
||||
@ -33,9 +32,8 @@ macro_rules! make_setting_route {
|
||||
Ok(HttpResponse::Accepted().json(serde_json::json!({ "updateId": update_status.id() })))
|
||||
}
|
||||
|
||||
#[actix_web::post($route, wrap = "Authentication::Private")]
|
||||
pub async fn update(
|
||||
data: actix_web::web::Data<data::Data>,
|
||||
async fn update(
|
||||
data: GuardedData<Private, data::Data>,
|
||||
index_uid: actix_web::web::Path<String>,
|
||||
body: actix_web::web::Json<Option<$type>>,
|
||||
) -> std::result::Result<HttpResponse, ResponseError> {
|
||||
@ -49,9 +47,8 @@ macro_rules! make_setting_route {
|
||||
Ok(HttpResponse::Accepted().json(serde_json::json!({ "updateId": update_status.id() })))
|
||||
}
|
||||
|
||||
#[actix_web::get($route, wrap = "Authentication::Private")]
|
||||
pub async fn get(
|
||||
data: actix_web::web::Data<data::Data>,
|
||||
async fn get(
|
||||
data: GuardedData<Private, data::Data>,
|
||||
index_uid: actix_web::web::Path<String>,
|
||||
) -> std::result::Result<HttpResponse, ResponseError> {
|
||||
let settings = data.settings(index_uid.into_inner()).await?;
|
||||
@ -60,6 +57,13 @@ macro_rules! make_setting_route {
|
||||
let val = json[$camelcase_attr].take();
|
||||
Ok(HttpResponse::Ok().json(val))
|
||||
}
|
||||
|
||||
pub fn resources() -> Resource {
|
||||
Resource::new($route)
|
||||
.route(web::get().to(get))
|
||||
.route(web::post().to(update))
|
||||
.route(web::delete().to(delete))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -117,14 +121,11 @@ macro_rules! create_services {
|
||||
($($mod:ident),*) => {
|
||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
cfg
|
||||
.service(update_all)
|
||||
.service(get_all)
|
||||
.service(delete_all)
|
||||
$(
|
||||
.service($mod::get)
|
||||
.service($mod::update)
|
||||
.service($mod::delete)
|
||||
)*;
|
||||
.service(web::resource("/indexes/{index_uid}/settings")
|
||||
.route(web::post().to(update_all))
|
||||
.route(web::get().to(get_all))
|
||||
.route(web::delete().to(delete_all)))
|
||||
$(.service($mod::resources()))*;
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -139,9 +140,8 @@ create_services!(
|
||||
ranking_rules
|
||||
);
|
||||
|
||||
#[post("/indexes/{index_uid}/settings", wrap = "Authentication::Private")]
|
||||
async fn update_all(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Private, Data>,
|
||||
index_uid: web::Path<String>,
|
||||
body: web::Json<Settings<Unchecked>>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
@ -154,9 +154,8 @@ async fn update_all(
|
||||
Ok(HttpResponse::Accepted().json(json))
|
||||
}
|
||||
|
||||
#[get("/indexes/{index_uid}/settings", wrap = "Authentication::Private")]
|
||||
async fn get_all(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Private, Data>,
|
||||
index_uid: web::Path<String>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let settings = data.settings(index_uid.into_inner()).await?;
|
||||
@ -164,9 +163,8 @@ async fn get_all(
|
||||
Ok(HttpResponse::Ok().json(settings))
|
||||
}
|
||||
|
||||
#[delete("/indexes/{index_uid}/settings", wrap = "Authentication::Private")]
|
||||
async fn delete_all(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Private, Data>,
|
||||
index_uid: web::Path<String>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let settings = Settings::cleared();
|
||||
|
@ -1,23 +1,20 @@
|
||||
use actix_web::get;
|
||||
use actix_web::web;
|
||||
use actix_web::HttpResponse;
|
||||
use log::debug;
|
||||
use actix_web::{web, HttpResponse};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::error::ResponseError;
|
||||
use crate::helpers::Authentication;
|
||||
use crate::extractors::authentication::{policies::*, GuardedData};
|
||||
use crate::routes::IndexParam;
|
||||
use crate::Data;
|
||||
|
||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(get_index_stats)
|
||||
.service(get_stats)
|
||||
.service(get_version);
|
||||
cfg.service(web::resource("/indexes/{index_uid}/stats").route(web::get().to(get_index_stats)))
|
||||
.service(web::resource("/stats").route(web::get().to(get_stats)))
|
||||
.service(web::resource("/version").route(web::get().to(get_version)));
|
||||
}
|
||||
|
||||
#[get("/indexes/{index_uid}/stats", wrap = "Authentication::Private")]
|
||||
async fn get_index_stats(
|
||||
data: web::Data<Data>,
|
||||
data: GuardedData<Private, Data>,
|
||||
path: web::Path<IndexParam>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let response = data.get_index_stats(path.index_uid.clone()).await?;
|
||||
@ -26,8 +23,7 @@ async fn get_index_stats(
|
||||
Ok(HttpResponse::Ok().json(response))
|
||||
}
|
||||
|
||||
#[get("/stats", wrap = "Authentication::Private")]
|
||||
async fn get_stats(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
|
||||
async fn get_stats(data: GuardedData<Private, Data>) -> Result<HttpResponse, ResponseError> {
|
||||
let response = data.get_all_stats().await?;
|
||||
|
||||
debug!("returns: {:?}", response);
|
||||
@ -42,8 +38,7 @@ struct VersionResponse {
|
||||
pkg_version: String,
|
||||
}
|
||||
|
||||
#[get("/version", wrap = "Authentication::Private")]
|
||||
async fn get_version() -> HttpResponse {
|
||||
async fn get_version(_data: GuardedData<Private, Data>) -> HttpResponse {
|
||||
let commit_sha = match option_env!("COMMIT_SHA") {
|
||||
Some("") | None => env!("VERGEN_SHA"),
|
||||
Some(commit_sha) => commit_sha,
|
||||
|
@ -14,8 +14,8 @@ async fn delete_one_unexisting_document() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
index.create(None).await;
|
||||
let (_response, code) = index.delete_document(0).await;
|
||||
assert_eq!(code, 202);
|
||||
let (response, code) = index.delete_document(0).await;
|
||||
assert_eq!(code, 202, "{}", response);
|
||||
let update = index.wait_update_id(0).await;
|
||||
assert_eq!(update["status"], "processed");
|
||||
}
|
||||
@ -85,8 +85,8 @@ async fn clear_all_documents_empty_index() {
|
||||
#[actix_rt::test]
|
||||
async fn delete_batch_unexisting_index() {
|
||||
let server = Server::new().await;
|
||||
let (_response, code) = server.index("test").delete_batch(vec![]).await;
|
||||
assert_eq!(code, 404);
|
||||
let (response, code) = server.index("test").delete_batch(vec![]).await;
|
||||
assert_eq!(code, 404, "{}", response);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use serde_json::{Value, json};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::common::Server;
|
||||
|
||||
@ -11,7 +11,10 @@ static DEFAULT_SETTINGS_VALUES: Lazy<HashMap<&'static str, Value>> = Lazy::new(|
|
||||
map.insert("searchable_attributes", json!(["*"]));
|
||||
map.insert("filterable_attributes", json!([]));
|
||||
map.insert("distinct_attribute", json!(Value::Null));
|
||||
map.insert("ranking_rules", json!(["words", "typo", "proximity", "attribute", "exactness"]));
|
||||
map.insert(
|
||||
"ranking_rules",
|
||||
json!(["words", "typo", "proximity", "attribute", "exactness"]),
|
||||
);
|
||||
map.insert("stop_words", json!([]));
|
||||
map.insert("synonyms", json!({}));
|
||||
map
|
||||
|
Loading…
Reference in New Issue
Block a user