Skip to content

Commit

Permalink
Merge pull request #26 from rodneylab/refactor_create_app_module
Browse files Browse the repository at this point in the history
refactor: 🏄🏽 move app logic into new module
  • Loading branch information
rodneylab authored Nov 13, 2024
2 parents 903636b + d731474 commit 7139252
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 168 deletions.
42 changes: 21 additions & 21 deletions scripts/minify-css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,34 @@ import init, { browserslistToTargets, transform } from "lightningcss";
await init();

const targets = browserslistToTargets(
browserslist(
"> 0.5%, last 2 versions, Firefox >= 102, Firefox ESR, not dead",
),
browserslist(
"> 0.5%, last 2 versions, Firefox >= 115, Firefox ESR, not dead",
),
);

async function minifyStylesheet(path: string) {
try {
const inputPath = `./assets/${path}`;
const outputPath = `./public/${path}`;
const css = await Deno.readTextFile(inputPath);
const { code: outputCss } = transform({
filename: inputPath,
code: new TextEncoder().encode(css),
minify: true,
targets,
});
try {
const inputPath = `./assets/${path}`;
const outputPath = `./public/${path}`;
const css = await Deno.readTextFile(inputPath);
const { code: outputCss } = transform({
filename: inputPath,
code: new TextEncoder().encode(css),
minify: true,
targets,
});

const decoder = new TextDecoder();
await Deno.writeTextFile(outputPath, `${decoder.decode(outputCss)}\n`);
} catch (error: unknown) {
console.error(
`Error building styles for path ${path}: ${error as string}`,
);
}
const decoder = new TextDecoder();
await Deno.writeTextFile(outputPath, `${decoder.decode(outputCss)}\n`);
} catch (error: unknown) {
console.error(
`Error building styles for path ${path}: ${error as string}`,
);
}
}

const cssFiles = ["fonts/fonts.css", "static/css/index.css"];

for await (const file of cssFiles) {
minifyStylesheet(file);
minifyStylesheet(file);
}
128 changes: 16 additions & 112 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,129 +4,33 @@ mod database;
mod model;
mod observability;
mod routes;
mod startup;

use std::{env, future::ready};
use std::env;

use axum::{extract::Extension, middleware, routing::get, serve, Router};
use dotenvy::dotenv;
use metrics_exporter_prometheus::PrometheusHandle;
use sqlx::SqlitePool;
use tokio::signal;
use tower_http::{compression::CompressionLayer, services::ServeDir, timeout::TimeoutLayer};
use tracing::info;

use database::{create as create_database, run_migrations};
use model::get_schema;
use observability::{
metrics::{create_prometheus_recorder, track_metrics},
tracing::create_tracing_subscriber_from_env,
use crate::{
database::create as create_database,
observability::{
metrics::create_prometheus_recorder, tracing::create_tracing_subscriber_from_env,
},
startup::Application,
};
use routes::{graphql_handler, graphql_playground, health};

async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};

#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};

#[cfg(not(unix))]
let terminate = std::future::pending::<()>();

tokio::select! {
() = ctrl_c => {
tracing::info!("Ctrl-C registered");
opentelemetry::global::shutdown_tracer_provider();
},
() = terminate => {
tracing::info!("Terminate registered");
opentelemetry::global::shutdown_tracer_provider();
},
}
}

async fn main_app(database_url: &str) -> Router<SqlitePool> {
tracing::info!("Main app service starting");

let db_pool = SqlitePool::connect(database_url)
.await
.expect("SQLite database should be reachable");
run_migrations(&db_pool).await;

let schema = get_schema(db_pool);

Router::new()
.route("/", get(graphql_playground).post(graphql_handler))
.route("/health", get(health))
// serve GraphQL Playground CDN assets locally
.nest_service("/assets", ServeDir::new("public"))
.layer(CompressionLayer::new())
.layer((
middleware::from_fn(track_metrics),
TimeoutLayer::new(std::time::Duration::from_secs(10)),
))
.layer(Extension(schema))
}

pub fn metrics_app(recorder_handle: PrometheusHandle) -> Router {
info!("Metrics service starting");
Router::new().route("/metrics", get(move || ready(recorder_handle.render())))
}
#[tokio::main]
async fn main() -> std::io::Result<()> {
dotenv().ok();

async fn start_main_server(database_url: &str) {
create_tracing_subscriber_from_env();
let app = main_app(database_url).await;
let local_addr = "127.0.0.1:8000";
let listener = tokio::net::TcpListener::bind(local_addr)
.await
.unwrap_or_else(|_| panic!("`{}` should not already be in use", &local_addr));
tracing::info!(
"Main app service listening on {}",
listener.local_addr().unwrap()
);

let db_pool = SqlitePool::connect(database_url)
.await
.expect("SQLite database should be reachable");

serve(listener, app.with_state(db_pool))
.with_graceful_shutdown(shutdown_signal())
.await
.unwrap();
}
let database_url = env::var("DATABASE_URL").unwrap_or_else(|_| "sqlite://sqlite.db".into());
create_database(&database_url).await;

async fn start_metrics_server() {
let recorder_handle = create_prometheus_recorder();
let app = metrics_app(recorder_handle);

let listener = tokio::net::TcpListener::bind("127.0.0.1:8001")
.await
.unwrap();
tracing::info!(
"Metrics service listening on {}",
listener.local_addr().unwrap()
);
serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
.await
.unwrap();
}

#[tokio::main]
async fn main() {
dotenv().ok();

let database_url = env::var("DATABASE_URL").unwrap_or_else(|_| "sqlite://sqlite.db".into());
create_database(&database_url).await;
let application = Application::build(&database_url, recorder_handle).await?;
application.run_until_stopped().await?;

let (_main_server, _metrics_server) =
tokio::join!(start_main_server(&database_url), start_metrics_server());
Ok(())
}
13 changes: 2 additions & 11 deletions src/model/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,13 @@ mod helpers {
};
use http_body_util::BodyExt;
use serde_json::{json, Value};
use sqlx::sqlite::SqlitePoolOptions;
use tower::{Service, ServiceExt};

use crate::main_app;
use crate::startup::main_router;

pub async fn get_app() -> Router {
let database_url = "sqlite://:memory:";
let app = main_app(database_url).await;

let db_pool = SqlitePoolOptions::new()
.max_connections(1)
.connect(database_url)
.await
.unwrap();

app.with_state(db_pool)
main_router(database_url).await
}

pub async fn create_draft(app: &mut Router, title: &str, body: &str) -> i64 {
Expand Down
20 changes: 6 additions & 14 deletions src/observability/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub(crate) fn create_prometheus_recorder() -> PrometheusHandle {
EXPONENTIAL_SECONDS,
)
.unwrap_or_else(|_| {
panic!("Could not initialise the bucket for '{REQUEST_DURATION_METRIC_NAME}'",)
panic!("Bucket values should not be an empty array (initialising the bucket for '{REQUEST_DURATION_METRIC_NAME}')",)
})
.install_recorder()
.expect("Could not install the Prometheus recorder, there might already be an instance running. It should only be started once.")
Expand Down Expand Up @@ -66,25 +66,17 @@ mod tests {
use metrics_exporter_prometheus::PrometheusHandle;
use once_cell::sync::Lazy;
use prometheus_parse::Scrape;
use sqlx::sqlite::SqlitePoolOptions;
use tower::ServiceExt;

use super::create_prometheus_recorder;
use crate::{
main_app, metrics_app, observability::tracing::create_tracing_subscriber_from_env,
observability::tracing::create_tracing_subscriber_from_env,
startup::{main_router, metrics_router},
};

async fn get_app() -> Router {
let database_url = "sqlite://:memory:";
let app = main_app(database_url).await;

let db_pool = SqlitePoolOptions::new()
.max_connections(1)
.connect(database_url)
.await
.unwrap();

app.with_state(db_pool)
main_router(database_url).await
}

static TRACING: Lazy<()> = Lazy::new(|| {
Expand All @@ -99,7 +91,7 @@ mod tests {
// Avoid re-initialising the tracing subscriber for each test
let recorder_handle = Lazy::force(&METRICS);
Lazy::force(&TRACING);
let metrics_app_instance = metrics_app(recorder_handle.clone());
let metrics_app_instance = metrics_router(recorder_handle.clone());

// act
let response = metrics_app_instance
Expand Down Expand Up @@ -147,7 +139,7 @@ mod tests {
Lazy::force(&TRACING);
std::env::set_var("OPENTELEMETRY_ENABLED", "true");
let main_app_instance = get_app().await;
let metrics_app_instance = metrics_app(recorder_handle.clone());
let metrics_app_instance = metrics_router(recorder_handle.clone());

// act
let _ = main_app_instance
Expand Down
12 changes: 2 additions & 10 deletions src/routes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,14 @@ mod tests {
};
use http_body_util::BodyExt;
use serde_json::{json, Value};
use sqlx::sqlite::SqlitePoolOptions;
use tower::util::ServiceExt;

use crate::main_app;
use crate::startup::main_router;

async fn get_app() -> Router {
let database_url = "sqlite://:memory:";
let app = main_app(database_url).await;

let db_pool = SqlitePoolOptions::new()
.max_connections(1)
.connect(database_url)
.await
.unwrap();

app.with_state(db_pool)
main_router(database_url).await
}

#[tokio::test]
Expand Down
Loading

0 comments on commit 7139252

Please sign in to comment.