#![allow(clippy::print_stdout)]
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use axum::body::Body;
use axum::extract::FromRef;
use axum::http::{Request, StatusCode};
use axum::Router;
use boson::prelude::Result as BosonResult;
use boson::{
boson_router, Boson, BosonState, ExecutionContext, JsonExecutionContextFactory,
MemQueueBackend, StaticTokenAdminAuth, TaskDescriptor, TaskRegistry, NEST_PATH,
};
use tower::ServiceExt;
const ADMIN_TOKEN: &str = "lab-token";
fn echo_task(
_ctx: Box<dyn ExecutionContext>,
_params: serde_json::Value,
) -> Pin<Box<dyn Future<Output = BosonResult<()>> + Send + 'static>> {
Box::pin(async { Ok(()) })
}
#[derive(Clone)]
struct AppState {
boson: BosonState,
}
impl FromRef<AppState> for BosonState {
fn from_ref(state: &AppState) -> Self {
state.boson.clone()
}
}
fn enqueue_request(token: Option<&str>) -> anyhow::Result<Request<Body>> {
let mut builder = Request::builder()
.method("POST")
.uri("/api/boson/jobs/enqueue")
.header("content-type", "application/json");
if let Some(token) = token {
builder = builder.header("x-boson-admin-token", token);
}
Ok(builder.body(Body::from(r#"{"task_name":"echo"}"#))?)
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut registry = TaskRegistry::new();
let desc: &'static TaskDescriptor = Box::leak(Box::new(TaskDescriptor::new("echo", echo_task)));
registry.register(desc);
let boson = Arc::new(
Boson::builder()
.queue_backend(Arc::new(MemQueueBackend::new()))
.execution_context_factory(JsonExecutionContextFactory)
.registry(Arc::new(registry))
.build()?,
);
let boson_state = BosonState::builder(Arc::clone(&boson))
.admin_auth(Arc::new(StaticTokenAdminAuth::new(ADMIN_TOKEN)))
.require_admin_auth(true)
.build()
.map_err(anyhow::Error::msg)?;
let app = Router::new()
.nest(NEST_PATH, boson_router())
.with_state(AppState { boson: boson_state });
let unauthorized = app.clone().oneshot(enqueue_request(None)?).await?;
println!("no token -> {}", unauthorized.status());
anyhow::ensure!(
unauthorized.status() == StatusCode::UNAUTHORIZED,
"expected 401 without a token, got {}",
unauthorized.status()
);
let authorized = app
.clone()
.oneshot(enqueue_request(Some(ADMIN_TOKEN))?)
.await?;
println!("valid token -> {}", authorized.status());
anyhow::ensure!(
authorized.status() == StatusCode::OK,
"expected 200 with a valid token, got {}",
authorized.status()
);
println!("fail-closed admin auth proven: 401 without token, 200 with token");
if std::env::var_os("BOSON_EXAMPLE_SERVE").is_none() {
return Ok(());
}
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
println!(
"listening on http://127.0.0.1:3000{NEST_PATH} (send x-boson-admin-token: {ADMIN_TOKEN})"
);
axum::serve(listener, app).await?;
Ok(())
}