diff --git a/controller/src/api.rs b/controller/src/api.rs index 5be86ce..4728059 100644 --- a/controller/src/api.rs +++ b/controller/src/api.rs @@ -11,7 +11,7 @@ type ControllerApiDescription = ApiDescription<Arc<ControllerContext>>; pub fn api() -> Result<ControllerApiDescription> { let mut api = ControllerApiDescription::new(); - user::register_api(&mut api)?; + api.register(user::get_user_by_id)?; api.register(version::version)?; Ok(api) } diff --git a/controller/src/lib.rs b/controller/src/lib.rs index 2d12df1..2774561 100644 --- a/controller/src/lib.rs +++ b/controller/src/lib.rs @@ -3,3 +3,4 @@ pub mod context; mod user; mod version; + diff --git a/controller/src/user.rs b/controller/src/user.rs new file mode 100644 index 0000000..7f203ef --- /dev/null +++ b/controller/src/user.rs @@ -0,0 +1,58 @@ +use clap::error::RichFormatter; +use dropshot::{endpoint, HttpError, HttpResponseOk, Path, RequestContext}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use trace_request::trace_request; +use uuid::Uuid; + +use std::sync::Arc; + +use crate::context::ControllerContext; + +/// User +#[derive(Serialize, JsonSchema)] +struct User { + id: Uuid, + name: String, +} + +#[allow(dead_code)] +#[derive(Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +struct UsersPathParams { + user_id: Uuid, +} + +/// Fetch user info. +#[endpoint { + method = GET, + path = "/users/{userId}", + tags = [ "user" ], +}] +#[trace_request] +pub(crate) async fn get_user_by_id( + rqctx: RequestContext<Arc<ControllerContext>>, + params: Path<UsersPathParams>, +) -> Result<HttpResponseOk<User>, HttpError> { + let id = params.into_inner().user_id; + tracing::debug!(id = id.to_string(), "Getting user by id"); + + let pg = rqctx.context().pg_pool.to_owned(); + + let rec = sqlx::query!(r#"SELECT * FROM users WHERE id = $1"#, id) + .fetch_one(&pg) + .await + .map_err(|e| match e { + sqlx::Error::RowNotFound => { + HttpError::for_not_found(None, format!("User not found by id: {:?}", id)) + } + err => HttpError::for_internal_error(format!("Error: {}", err)), + })?; + + let user = User { + id: rec.id, + name: rec.name, + }; + + Ok(HttpResponseOk(user)) +} diff --git a/controller/src/user/api.rs b/controller/src/user/api.rs deleted file mode 100644 index 67397d9..0000000 --- a/controller/src/user/api.rs +++ /dev/null @@ -1,126 +0,0 @@ -use dropshot::{ - endpoint, EmptyScanParams, HttpError, HttpResponseOk, PaginationParams, Path, Query, - RequestContext, ResultsPage, WhichPage, -}; -use dropshot::{ApiDescription, ApiDescriptionRegisterError}; -use schemars::JsonSchema; -use serde::Deserialize; -use serde::Serialize; -use trace_request::trace_request; -use uuid::Uuid; - -use std::sync::Arc; - -use super::User; -use crate::context::ControllerContext; - -#[derive(Deserialize, JsonSchema, Serialize)] -#[serde(rename_all = "camelCase")] -struct UsersPathParams { - user_id: Uuid, -} - -#[derive(Deserialize, JsonSchema, Serialize)] -#[serde(rename_all = "camelCase")] -struct UserPage { - user_id: Uuid, -} - -pub fn register_api( - api: &mut ApiDescription<Arc<ControllerContext>>, -) -> Result<(), ApiDescriptionRegisterError> { - api.register(get_user_by_id)?; - api.register(list_users) -} - -/// Fetch user info. -#[endpoint { - method = GET, - path = "/users/{userId}", - tags = [ "user" ], -}] -#[trace_request] -async fn get_user_by_id( - rqctx: RequestContext<Arc<ControllerContext>>, - params: Path<UsersPathParams>, -) -> Result<HttpResponseOk<User>, HttpError> { - let id = params.into_inner().user_id; - tracing::debug!(id = id.to_string(), "Getting user by id"); - - let pg = rqctx.context().pg_pool.to_owned(); - - let rec = sqlx::query!(r#"SELECT * FROM users WHERE id = $1"#, id) - .fetch_one(&pg) - .await - .map_err(|e| match e { - sqlx::Error::RowNotFound => { - HttpError::for_not_found(None, format!("User not found by id: {:?}", id)) - } - err => HttpError::for_internal_error(format!("Error: {}", err)), - })?; - - let user = User { - id: rec.id, - name: rec.name, - }; - - Ok(HttpResponseOk(user)) -} - -/// List users -#[endpoint { - method = GET, - path = "/users", - tags = [ "user" ], -}] -#[trace_request] -async fn list_users( - rqctx: RequestContext<Arc<ControllerContext>>, - query: Query<PaginationParams<EmptyScanParams, UserPage>>, -) -> Result<HttpResponseOk<ResultsPage<User>>, HttpError> { - let pag_params = query.into_inner(); - let limit = rqctx.page_limit(&pag_params)?.get() as usize; - let pg = rqctx.context().pg_pool.to_owned(); - - let users = match &pag_params.page { - WhichPage::First(..) => { - tracing::debug!("Listing users. First page"); - sqlx::query!(r#"SELECT * FROM users ORDER BY id LIMIT $1"#, limit as i64) - .fetch_all(&pg) - .await - .map_err(|e| HttpError::for_internal_error(format!("Error: {}", e)))? - .into_iter() - .map(|rec| User { - id: rec.id, - name: rec.name, - }) - .collect() - } - WhichPage::Next(UserPage { user_id: last_seen }) => { - tracing::debug!( - last_seen = last_seen.to_string(), - "Listing users. Next page" - ); - sqlx::query!( - r#"SELECT * FROM users WHERE id > $1 ORDER BY id LIMIT $2"#, - last_seen, - limit as i64 - ) - .fetch_all(&pg) - .await - .map_err(|e| HttpError::for_internal_error(format!("Error: {}", e)))? - .into_iter() - .map(|rec| User { - id: rec.id, - name: rec.name, - }) - .collect() - } - }; - - Ok(HttpResponseOk(ResultsPage::new( - users, - &EmptyScanParams {}, - |u: &User, _| UserPage { user_id: u.id }, - )?)) -} diff --git a/controller/src/user/mod.rs b/controller/src/user/mod.rs deleted file mode 100644 index 46397e9..0000000 --- a/controller/src/user/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -use schemars::JsonSchema; -use serde::Serialize; -use uuid::Uuid; - -mod api; - -pub use self::api::register_api; - -/// User -#[derive(Serialize, JsonSchema)] -struct User { - id: Uuid, - name: String, -} diff --git a/instrumentation/src/lib.rs b/instrumentation/src/lib.rs index fd69950..31df7f6 100644 --- a/instrumentation/src/lib.rs +++ b/instrumentation/src/lib.rs @@ -96,18 +96,20 @@ pub fn init_tracing(otel_endpoint: Option<&String>, log_stderr: bool) -> Result< None => None, }; - let metrics_layer = meter_provider - .as_ref() - .map(|p| MetricsLayer::new(p.to_owned())); + let metrics_layer = match meter_provider { + Some(ref p) => Some(MetricsLayer::new(p.to_owned())), + None => None, + }; let tracer_provider = match otel_endpoint { Some(endpoint) => Some(init_tracer_provider(endpoint, resource)?), None => None, }; - let trace_layer = tracer_provider - .as_ref() - .map(|p| OpenTelemetryLayer::new(p.tracer("tracing-otel-subscriber"))); + let trace_layer = match tracer_provider { + Some(ref p) => Some(OpenTelemetryLayer::new(p.tracer("tracing-otel-subscriber"))), + None => None, + }; opentelemetry::global::set_text_map_propagator(TraceContextPropagator::new()); diff --git a/justfile b/justfile index 9dabd7b..5c4779f 100644 --- a/justfile +++ b/justfile @@ -11,7 +11,7 @@ run-controller $RUST_LOG="debug,h2=info,hyper_util=info,tower=info": # Run controller local development [group('controller')] -dev-controller: dev-controller-db-migrate +dev-controller: watchexec --clear --restart --stop-signal INT --debounce 300ms -- just run-controller # Run agent @@ -76,7 +76,6 @@ dev-postgres: --volume patagia-postgres:/var/lib/postgresql/data \ --volume "${XDG_RUNTIME_DIR}/patagia-postgres:/var/run/postgresql" \ docker.io/postgres:17 - sleep 0.3 # Clean up PostgreSQL data [group('controller')] @@ -91,7 +90,7 @@ dev-postgres-psql: [group('controller')] [working-directory: 'controller'] -dev-controller-db-migrate: dev-postgres +dev-controller-db-migrate: cargo sqlx migrate run [group('controller')]