feat: add replay-stream-tasks pattern support
This commit is contained in:
@@ -2,19 +2,23 @@ use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
use axum::Router;
|
||||
|
||||
use crate::app::AppContext;
|
||||
use crate::app::AppContextTrait;
|
||||
|
||||
pub trait ControllerTrait: Sized {
|
||||
fn apply_to(self, router: Router<Arc<AppContext>>) -> Router<Arc<AppContext>>;
|
||||
fn apply_to(self, router: Router<Arc<dyn AppContextTrait>>)
|
||||
-> Router<Arc<dyn AppContextTrait>>;
|
||||
}
|
||||
|
||||
pub struct PrefixController {
|
||||
prefix: Cow<'static, str>,
|
||||
router: Router<Arc<AppContext>>,
|
||||
router: Router<Arc<dyn AppContextTrait>>,
|
||||
}
|
||||
|
||||
impl PrefixController {
|
||||
pub fn new(prefix: impl Into<Cow<'static, str>>, router: Router<Arc<AppContext>>) -> Self {
|
||||
pub fn new(
|
||||
prefix: impl Into<Cow<'static, str>>,
|
||||
router: Router<Arc<dyn AppContextTrait>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
prefix: prefix.into(),
|
||||
router,
|
||||
@@ -23,7 +27,10 @@ impl PrefixController {
|
||||
}
|
||||
|
||||
impl ControllerTrait for PrefixController {
|
||||
fn apply_to(self, router: Router<Arc<AppContext>>) -> Router<Arc<AppContext>> {
|
||||
fn apply_to(
|
||||
self,
|
||||
router: Router<Arc<dyn AppContextTrait>>,
|
||||
) -> Router<Arc<dyn AppContextTrait>> {
|
||||
router.nest(&self.prefix, self.router)
|
||||
}
|
||||
}
|
||||
@@ -35,14 +42,17 @@ pub enum Controller {
|
||||
impl Controller {
|
||||
pub fn from_prefix(
|
||||
prefix: impl Into<Cow<'static, str>>,
|
||||
router: Router<Arc<AppContext>>,
|
||||
router: Router<Arc<dyn AppContextTrait>>,
|
||||
) -> Self {
|
||||
Self::Prefix(PrefixController::new(prefix, router))
|
||||
}
|
||||
}
|
||||
|
||||
impl ControllerTrait for Controller {
|
||||
fn apply_to(self, router: Router<Arc<AppContext>>) -> Router<Arc<AppContext>> {
|
||||
fn apply_to(
|
||||
self,
|
||||
router: Router<Arc<dyn AppContextTrait>>,
|
||||
) -> Router<Arc<dyn AppContextTrait>> {
|
||||
match self {
|
||||
Self::Prefix(p) => p.apply_to(router),
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use axum::{Extension, Router, extract::State, middleware::from_fn_with_state, ro
|
||||
|
||||
use super::core::Controller;
|
||||
use crate::{
|
||||
app::AppContext,
|
||||
app::AppContextTrait,
|
||||
auth::{AuthUserInfo, header_www_authenticate_middleware},
|
||||
errors::RResult,
|
||||
};
|
||||
@@ -13,11 +13,11 @@ use crate::{
|
||||
pub const CONTROLLER_PREFIX: &str = "/api/graphql";
|
||||
|
||||
async fn graphql_handler(
|
||||
State(ctx): State<Arc<AppContext>>,
|
||||
State(ctx): State<Arc<dyn AppContextTrait>>,
|
||||
Extension(auth_user_info): Extension<AuthUserInfo>,
|
||||
req: GraphQLRequest,
|
||||
) -> GraphQLResponse {
|
||||
let graphql_service = &ctx.graphql;
|
||||
let graphql_service = ctx.graphql();
|
||||
|
||||
let mut req = req.into_inner();
|
||||
req = req.data(auth_user_info);
|
||||
@@ -25,8 +25,8 @@ async fn graphql_handler(
|
||||
graphql_service.schema.execute(req).await.into()
|
||||
}
|
||||
|
||||
pub async fn create(ctx: Arc<AppContext>) -> RResult<Controller> {
|
||||
let router = Router::<Arc<AppContext>>::new()
|
||||
pub async fn create(ctx: Arc<dyn AppContextTrait>) -> RResult<Controller> {
|
||||
let router = Router::<Arc<dyn AppContextTrait>>::new()
|
||||
.route("/", post(graphql_handler))
|
||||
.layer(from_fn_with_state(ctx, header_www_authenticate_middleware));
|
||||
Ok(Controller::from_prefix(CONTROLLER_PREFIX, router))
|
||||
|
||||
@@ -9,7 +9,7 @@ use axum::{
|
||||
|
||||
use super::core::Controller;
|
||||
use crate::{
|
||||
app::AppContext,
|
||||
app::AppContextTrait,
|
||||
auth::{
|
||||
AuthError, AuthService, AuthServiceTrait,
|
||||
oidc::{OidcAuthCallbackPayload, OidcAuthCallbackQuery, OidcAuthRequest},
|
||||
@@ -22,10 +22,10 @@ use crate::{
|
||||
pub const CONTROLLER_PREFIX: &str = "/api/oidc";
|
||||
|
||||
async fn oidc_callback(
|
||||
State(ctx): State<Arc<AppContext>>,
|
||||
State(ctx): State<Arc<dyn AppContextTrait>>,
|
||||
Query(query): Query<OidcAuthCallbackQuery>,
|
||||
) -> Result<Json<OidcAuthCallbackPayload>, AuthError> {
|
||||
let auth_service = &ctx.auth;
|
||||
let auth_service = ctx.auth();
|
||||
if let AuthService::Oidc(oidc_auth_service) = auth_service {
|
||||
let response = oidc_auth_service
|
||||
.extract_authorization_request_callback(query)
|
||||
@@ -40,10 +40,10 @@ async fn oidc_callback(
|
||||
}
|
||||
|
||||
async fn oidc_auth(
|
||||
State(ctx): State<Arc<AppContext>>,
|
||||
State(ctx): State<Arc<dyn AppContextTrait>>,
|
||||
parts: Parts,
|
||||
) -> Result<Json<OidcAuthRequest>, AuthError> {
|
||||
let auth_service = &ctx.auth;
|
||||
let auth_service = ctx.auth();
|
||||
if let AuthService::Oidc(oidc_auth_service) = auth_service {
|
||||
let mut redirect_uri = ForwardedRelatedInfo::from_request_parts(&parts)
|
||||
.resolved_origin()
|
||||
@@ -70,8 +70,8 @@ async fn oidc_auth(
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create(_context: Arc<AppContext>) -> RResult<Controller> {
|
||||
let router = Router::<Arc<AppContext>>::new()
|
||||
pub async fn create(_context: Arc<dyn AppContextTrait>) -> RResult<Controller> {
|
||||
let router = Router::<Arc<dyn AppContextTrait>>::new()
|
||||
.route("/auth", get(oidc_auth))
|
||||
.route("/callback", get(oidc_callback));
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ use http::StatusCode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tower_http::catch_panic::CatchPanicLayer;
|
||||
|
||||
use crate::{app::AppContext, errors::RResult, web::middleware::MiddlewareLayer};
|
||||
use crate::{app::AppContextTrait, errors::RResult, web::middleware::MiddlewareLayer};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct CatchPanic {
|
||||
@@ -52,7 +52,10 @@ impl MiddlewareLayer for CatchPanic {
|
||||
}
|
||||
|
||||
/// Applies the Catch Panic middleware layer to the Axum router.
|
||||
fn apply(&self, app: Router<Arc<AppContext>>) -> RResult<Router<Arc<AppContext>>> {
|
||||
fn apply(
|
||||
&self,
|
||||
app: Router<Arc<dyn AppContextTrait>>,
|
||||
) -> RResult<Router<Arc<dyn AppContextTrait>>> {
|
||||
Ok(app.layer(CatchPanicLayer::custom(handle_panic)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use axum::Router;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tower_http::compression::CompressionLayer;
|
||||
|
||||
use crate::{app::AppContext, errors::RResult, web::middleware::MiddlewareLayer};
|
||||
use crate::{app::AppContextTrait, errors::RResult, web::middleware::MiddlewareLayer};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Compression {
|
||||
@@ -35,7 +35,10 @@ impl MiddlewareLayer for Compression {
|
||||
}
|
||||
|
||||
/// Applies the Compression middleware layer to the Axum router.
|
||||
fn apply(&self, app: Router<Arc<AppContext>>) -> RResult<Router<Arc<AppContext>>> {
|
||||
fn apply(
|
||||
&self,
|
||||
app: Router<Arc<dyn AppContextTrait>>,
|
||||
) -> RResult<Router<Arc<dyn AppContextTrait>>> {
|
||||
Ok(app.layer(CompressionLayer::new()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use tower_http::cors::{self, Any};
|
||||
|
||||
use crate::{app::AppContext, web::middleware::MiddlewareLayer, errors::RResult};
|
||||
use crate::{app::AppContextTrait, web::middleware::MiddlewareLayer, errors::RResult};
|
||||
|
||||
/// CORS middleware configuration
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
@@ -157,7 +157,7 @@ impl MiddlewareLayer for Cors {
|
||||
}
|
||||
|
||||
/// Applies the CORS middleware layer to the Axum router.
|
||||
fn apply(&self, app: Router<Arc<AppContext>>) -> RResult<Router<Arc<AppContext>>> {
|
||||
fn apply(&self, app: Router<Arc<dyn AppContextTrait>>) -> RResult<Router<Arc<dyn AppContextTrait>>> {
|
||||
Ok(app.layer(self.cors()?))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ use futures_util::future::BoxFuture;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tower::{Layer, Service};
|
||||
|
||||
use crate::{app::AppContext, errors::RResult, web::middleware::MiddlewareLayer};
|
||||
use crate::{app::AppContextTrait, errors::RResult, web::middleware::MiddlewareLayer};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Etag {
|
||||
@@ -49,7 +49,10 @@ impl MiddlewareLayer for Etag {
|
||||
}
|
||||
|
||||
/// Applies the `ETag` middleware to the application router.
|
||||
fn apply(&self, app: Router<Arc<AppContext>>) -> RResult<Router<Arc<AppContext>>> {
|
||||
fn apply(
|
||||
&self,
|
||||
app: Router<Arc<dyn AppContextTrait>>,
|
||||
) -> RResult<Router<Arc<dyn AppContextTrait>>> {
|
||||
Ok(app.layer(EtagLayer))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ use serde::{Deserialize, Serialize};
|
||||
use tower_http::{add_extension::AddExtensionLayer, trace::TraceLayer};
|
||||
|
||||
use crate::{
|
||||
app::{AppContext, Environment},
|
||||
app::{AppContextTrait, Environment},
|
||||
errors::RResult,
|
||||
web::middleware::{MiddlewareLayer, request_id::LocoRequestId},
|
||||
};
|
||||
@@ -35,10 +35,10 @@ pub struct Middleware {
|
||||
/// Creates a new instance of [`Middleware`] by cloning the [`Config`]
|
||||
/// configuration.
|
||||
#[must_use]
|
||||
pub fn new(config: &Config, context: Arc<AppContext>) -> Middleware {
|
||||
pub fn new(config: &Config, context: Arc<dyn AppContextTrait>) -> Middleware {
|
||||
Middleware {
|
||||
config: config.clone(),
|
||||
environment: context.environment.clone(),
|
||||
environment: context.environment().clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,10 @@ impl MiddlewareLayer for Middleware {
|
||||
/// The `TraceLayer` is customized with `make_span_with` to extract
|
||||
/// request-specific details like method, URI, version, user agent, and
|
||||
/// request ID, then create a tracing span for the request.
|
||||
fn apply(&self, app: Router<Arc<AppContext>>) -> RResult<Router<Arc<AppContext>>> {
|
||||
fn apply(
|
||||
&self,
|
||||
app: Router<Arc<dyn AppContextTrait>>,
|
||||
) -> RResult<Router<Arc<dyn AppContextTrait>>> {
|
||||
Ok(app
|
||||
.layer(
|
||||
TraceLayer::new_for_http().make_span_with(|request: &http::Request<_>| {
|
||||
|
||||
@@ -14,7 +14,7 @@ use std::sync::Arc;
|
||||
use axum::Router;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{app::AppContext, errors::RResult};
|
||||
use crate::{app::AppContextTrait, errors::RResult};
|
||||
|
||||
/// Trait representing the behavior of middleware components in the application.
|
||||
/// When implementing a new middleware, make sure to go over this checklist:
|
||||
@@ -52,14 +52,17 @@ pub trait MiddlewareLayer {
|
||||
/// # Errors
|
||||
///
|
||||
/// If there is an issue when adding the middleware to the router.
|
||||
fn apply(&self, app: Router<Arc<AppContext>>) -> RResult<Router<Arc<AppContext>>>;
|
||||
fn apply(
|
||||
&self,
|
||||
app: Router<Arc<dyn AppContextTrait>>,
|
||||
) -> RResult<Router<Arc<dyn AppContextTrait>>>;
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_lazy_evaluations)]
|
||||
#[must_use]
|
||||
pub fn default_middleware_stack(ctx: Arc<AppContext>) -> Vec<Box<dyn MiddlewareLayer>> {
|
||||
pub fn default_middleware_stack(ctx: Arc<dyn AppContextTrait>) -> Vec<Box<dyn MiddlewareLayer>> {
|
||||
// Shortened reference to middlewares
|
||||
let middlewares = &ctx.config.server.middlewares;
|
||||
let middlewares = &ctx.config().server.middlewares;
|
||||
|
||||
vec![
|
||||
// CORS middleware with a default if none
|
||||
|
||||
@@ -31,7 +31,7 @@ use tower::{Layer, Service};
|
||||
use tracing::error;
|
||||
|
||||
use crate::{
|
||||
app::AppContext,
|
||||
app::AppContextTrait,
|
||||
errors::{RError, RResult},
|
||||
web::middleware::MiddlewareLayer,
|
||||
};
|
||||
@@ -123,7 +123,10 @@ impl MiddlewareLayer for RemoteIpMiddleware {
|
||||
}
|
||||
|
||||
/// Applies the Remote IP middleware to the given Axum router.
|
||||
fn apply(&self, app: Router<Arc<AppContext>>) -> RResult<Router<Arc<AppContext>>> {
|
||||
fn apply(
|
||||
&self,
|
||||
app: Router<Arc<dyn AppContextTrait>>,
|
||||
) -> RResult<Router<Arc<dyn AppContextTrait>>> {
|
||||
Ok(app.layer(RemoteIPLayer::new(self)?))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{web::middleware::MiddlewareLayer, app::AppContext, errors::RResult};
|
||||
use crate::{app::AppContextTrait, errors::RResult, web::middleware::MiddlewareLayer};
|
||||
|
||||
const X_REQUEST_ID: &str = "x-request-id";
|
||||
const MAX_LEN: usize = 255;
|
||||
@@ -52,7 +52,10 @@ impl MiddlewareLayer for RequestId {
|
||||
///
|
||||
/// # Errors
|
||||
/// This function returns an error if the middleware cannot be applied.
|
||||
fn apply(&self, app: Router<Arc<AppContext>>) -> RResult<Router<Arc<AppContext>>> {
|
||||
fn apply(
|
||||
&self,
|
||||
app: Router<Arc<dyn AppContextTrait>>,
|
||||
) -> RResult<Router<Arc<dyn AppContextTrait>>> {
|
||||
Ok(app.layer(axum::middleware::from_fn(request_id_middleware)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ use serde_json::{self, json};
|
||||
use tower::{Layer, Service};
|
||||
|
||||
use crate::{
|
||||
app::AppContext,
|
||||
app::AppContextTrait,
|
||||
web::middleware::MiddlewareLayer,
|
||||
errors::{RError, RResult},
|
||||
};
|
||||
@@ -115,7 +115,7 @@ impl MiddlewareLayer for SecureHeader {
|
||||
}
|
||||
|
||||
/// Applies the secure headers layer to the application router
|
||||
fn apply(&self, app: Router<Arc<AppContext>>) -> RResult<Router<Arc<AppContext>>> {
|
||||
fn apply(&self, app: Router<Arc<dyn AppContextTrait>>) -> RResult<Router<Arc<dyn AppContextTrait>>> {
|
||||
Ok(app.layer(SecureHeaders::new(self)?))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use tower_http::timeout::TimeoutLayer;
|
||||
|
||||
use crate::{app::AppContext, errors::RResult, web::middleware::MiddlewareLayer};
|
||||
use crate::{app::AppContextTrait, errors::RResult, web::middleware::MiddlewareLayer};
|
||||
|
||||
/// Timeout middleware configuration
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
@@ -58,7 +58,10 @@ impl MiddlewareLayer for TimeOut {
|
||||
/// This method wraps the provided [`AXRouter`] in a [`TimeoutLayer`],
|
||||
/// ensuring that requests exceeding the specified timeout duration will
|
||||
/// be interrupted.
|
||||
fn apply(&self, app: Router<Arc<AppContext>>) -> RResult<Router<Arc<AppContext>>> {
|
||||
fn apply(
|
||||
&self,
|
||||
app: Router<Arc<dyn AppContextTrait>>,
|
||||
) -> RResult<Router<Arc<dyn AppContextTrait>>> {
|
||||
Ok(app.layer(TimeoutLayer::new(Duration::from_millis(self.timeout))))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user