reductstore 1.19.8

ReductStore is a time series database designed specifically for storing and managing large amounts of blob data.
Documentation
// Copyright 2021-2026 ReductSoftware UG
// Licensed under the Apache License, Version 2.0

mod create;
mod get;
mod head;
mod remove;
mod rename;
mod update;

use crate::api::http::bucket::create::create_bucket;
use crate::api::http::bucket::get::get_bucket;
use crate::api::http::bucket::head::head_bucket;
use crate::api::http::bucket::remove::remove_bucket;
use crate::api::http::bucket::rename::rename_bucket;
use crate::api::http::bucket::update::update_bucket;
use crate::api::http::{HttpError, StateKeeper};
use axum::body::Body;
use axum::extract::FromRequest;
use axum::http::Request;
use axum::response::{IntoResponse, Response};
use axum::routing::{delete, get, head, post, put};
use axum_extra::headers::HeaderMapExt;
use bytes::Bytes;
use reduct_base::msg::bucket_api::{BucketSettings, FullBucketInfo};
use reduct_macros::{IntoResponse, Twin};
use std::sync::Arc;

//
// BucketSettings wrapper
//
#[derive(IntoResponse, Twin)]
pub(super) struct BucketSettingsAxum(BucketSettings);

impl Default for BucketSettingsAxum {
    fn default() -> Self {
        Self(BucketSettings::default())
    }
}

#[derive(IntoResponse, Twin)]
pub(super) struct FullBucketInfoAxum(FullBucketInfo);

impl<S> FromRequest<S> for BucketSettingsAxum
where
    Bytes: FromRequest<S>,
    S: Send + Sync,
{
    type Rejection = Response;

    async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
        let body = Bytes::from_request(req, state)
            .await
            .map_err(IntoResponse::into_response)?;

        if body.is_empty() {
            return Ok(Self::default());
        }

        let settings: BucketSettings =
            serde_json::from_slice(&body).map_err(|e| HttpError::from(e).into_response())?;
        Ok(Self(settings))
    }
}

pub(super) fn create_bucket_api_routes() -> axum::Router<Arc<StateKeeper>> {
    axum::Router::new()
        .route("/{bucket_name}", get(get_bucket))
        .route("/{bucket_name}", head(head_bucket))
        .route("/{bucket_name}", post(create_bucket))
        .route("/{bucket_name}", put(update_bucket))
        .route("/{bucket_name}", delete(remove_bucket))
        .route("/{bucket_name}/rename", put(rename_bucket))
}

#[cfg(test)]
mod tests {
    use super::*;
    use axum::http::Method;

    #[tokio::test]
    async fn test_bucket_settings_quota_parsing() {
        let req = Request::builder()
            .method(Method::POST)
            .uri("/b/bucket-1")
            .header("Content-Type", "application/json")
            .body(Body::from(r#"{"quota_type": 1}"#))
            .unwrap();
        let resp = BucketSettingsAxum::from_request(req, &())
            .await
            .err()
            .unwrap();
        assert_eq!(resp.status(), 422);
        assert_eq!(
            resp.headers()
                .get("x-reduct-error")
                .unwrap()
                .to_str()
                .unwrap(),
            "Invalid JSON: expected value at line 1 column 16"
        );
    }
}