diff --git a/examples/minimal/Cargo.toml b/examples/minimal/Cargo.toml index 6c50c739..d9157a3f 100644 --- a/examples/minimal/Cargo.toml +++ b/examples/minimal/Cargo.toml @@ -13,12 +13,17 @@ tracing = { version = "0.1.40", features = ["async-await"] } async-trait = "0.1.77" aide = { version = "0.13.3", features = ["axum"] } axum = "0.7.5" +schemars = "0.8.16" # DB entity = { path = "entity" } migration = { path = "migration" } clap = { version = "4.5.4", features = ["derive"] } +# The default `rss-stats` feature has a dependency that currently can't be satisfied (memchr: ~2.3) +rusty-sidekiq = { version = "0.10.4", default-features = false } +serde = { version = "1.0.198", features = ["derive"] } + [dev-dependencies] cargo-husky = { version = "1.5.0", features = ["default", "run-cargo-check", "run-cargo-clippy", "run-cargo-fmt", "run-cargo-test"] } diff --git a/examples/minimal/config/default.toml b/examples/minimal/config/default.toml index 6a1cfe5a..6036c3d2 100644 --- a/examples/minimal/config/default.toml +++ b/examples/minimal/config/default.toml @@ -15,3 +15,6 @@ acquire-timeout = 5000 idle-timeout = 60 min-connections = 0 max-connections = 10 + +[worker.sidekiq] +queues = ["default"] diff --git a/examples/minimal/src/app.rs b/examples/minimal/src/app.rs index 17849d20..86cf1003 100644 --- a/examples/minimal/src/app.rs +++ b/examples/minimal/src/app.rs @@ -1,22 +1,39 @@ use aide::axum::ApiRouter; +use async_trait::async_trait; use migration::Migrator; use roadster::app::App as RoadsterApp; +use roadster::app_context::AppContext; use roadster::config::app_config::AppConfig; use roadster::controller::default_routes; +use roadster::worker::app_worker::AppWorker; +use roadster::worker::registry::WorkerRegistry; use crate::app_state::AppState; use crate::cli::AppCli; +use crate::controller; +use crate::worker::example::ExampleWorker; const BASE: &str = "/api"; #[derive(Default)] pub struct App; + +#[async_trait] impl RoadsterApp for App { type State = AppState; type Cli = AppCli; type M = Migrator; fn router(config: &AppConfig) -> ApiRouter { - default_routes(BASE, config) + default_routes(BASE, config).merge(controller::routes(BASE)) + } + + async fn workers( + registry: &mut WorkerRegistry, + _context: &AppContext, + state: &Self::State, + ) -> anyhow::Result<()> { + registry.register_app_worker(ExampleWorker::build(state)); + Ok(()) } } diff --git a/examples/minimal/src/controller/example.rs b/examples/minimal/src/controller/example.rs new file mode 100644 index 00000000..53d936e5 --- /dev/null +++ b/examples/minimal/src/controller/example.rs @@ -0,0 +1,38 @@ +use crate::app_state::AppState; +use crate::worker::example::ExampleWorker; +use aide::axum::routing::get_with; +use aide::axum::ApiRouter; +use aide::transform::TransformOperation; +use axum::extract::State; +use axum::Json; +use roadster::controller::build_path; +use roadster::view::app_error::AppError; +use roadster::worker::app_worker::AppWorker; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use tracing::instrument; + +const BASE: &str = "/example"; +const TAG: &str = "Example"; + +pub fn routes(parent: &str) -> ApiRouter { + let root = build_path(parent, BASE); + + ApiRouter::new().api_route(&root, get_with(example_get, example_get_docs)) +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct ExampleResponse {} + +#[instrument(skip_all)] +async fn example_get(State(state): State) -> Result, AppError> { + ExampleWorker::enqueue(&state, "Example".to_string()).await?; + Ok(Json(ExampleResponse {})) +} + +fn example_get_docs(op: TransformOperation) -> TransformOperation { + op.description("Example API.") + .tag(TAG) + .response_with::<200, Json, _>(|res| res.example(ExampleResponse {})) +} diff --git a/examples/minimal/src/controller/mod.rs b/examples/minimal/src/controller/mod.rs new file mode 100644 index 00000000..a790f6e6 --- /dev/null +++ b/examples/minimal/src/controller/mod.rs @@ -0,0 +1,8 @@ +use crate::app_state::AppState; +use aide::axum::ApiRouter; + +pub mod example; + +pub fn routes(parent: &str) -> ApiRouter { + ApiRouter::new().merge(example::routes(parent)) +} diff --git a/examples/minimal/src/lib.rs b/examples/minimal/src/lib.rs index 8f783412..8efad027 100644 --- a/examples/minimal/src/lib.rs +++ b/examples/minimal/src/lib.rs @@ -1,3 +1,5 @@ pub mod app; pub mod app_state; pub mod cli; +pub mod controller; +pub mod worker; diff --git a/examples/minimal/src/worker/example.rs b/examples/minimal/src/worker/example.rs new file mode 100644 index 00000000..7956cd34 --- /dev/null +++ b/examples/minimal/src/worker/example.rs @@ -0,0 +1,24 @@ +use crate::app::App; +use crate::app_state::AppState; +use async_trait::async_trait; +use roadster::worker::app_worker::AppWorker; +use sidekiq::Worker; +use tracing::{info, instrument}; + +pub struct ExampleWorker {} + +#[async_trait] +impl Worker for ExampleWorker { + #[instrument(skip_all)] + async fn perform(&self, args: String) -> sidekiq::Result<()> { + info!("Processing job with args: {args}"); + Ok(()) + } +} + +#[async_trait] +impl AppWorker for ExampleWorker { + fn build(_state: &AppState) -> Self { + Self {} + } +} diff --git a/examples/minimal/src/worker/mod.rs b/examples/minimal/src/worker/mod.rs new file mode 100644 index 00000000..d4d8a94c --- /dev/null +++ b/examples/minimal/src/worker/mod.rs @@ -0,0 +1 @@ +pub mod example;