chore: load app settings via app ctx and do away with global static loader

This commit is contained in:
Aravinth Manivannan 2022-04-27 10:52:24 +05:30
parent 624646f836
commit 68d63bba07
Signed by: realaravinth
GPG key ID: AD9F0F08E855ED88
7 changed files with 77 additions and 51 deletions

View file

@ -21,7 +21,7 @@ my-codegen = {package = "actix-web-codegen", git ="https://github.com/realaravin
config = "0.13"
git2 = "0.14.2"
serde = { version = "1", features = ["derive"]}
serde = { version = "1", features = ["derive", "rc"]}
serde_json = "1"
pretty_env_logger = "0.4"

30
src/ctx.rs Normal file
View file

@ -0,0 +1,30 @@
/*
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use std::sync::Arc;
use crate::settings::Settings;
#[derive(Clone)]
pub struct Ctx {
pub settings: Settings,
}
impl Ctx {
pub fn new(settings: Settings) -> Arc<Self> {
Arc::new(Self { settings })
}
}

View file

@ -19,7 +19,7 @@ use serde::{Deserialize, Serialize};
use tokio::sync::oneshot;
use crate::errors::*;
use crate::SETTINGS;
use crate::AppCtx;
pub mod routes {
pub struct Deploy {
@ -42,11 +42,12 @@ pub struct DeployEvent {
}
#[my_codegen::post(path = "crate::V1_API_ROUTES.deploy.update")]
async fn update(payload: web::Json<DeployEvent>) -> ServiceResult<impl Responder> {
for page in SETTINGS.pages.iter() {
async fn update(payload: web::Json<DeployEvent>, ctx: AppCtx) -> ServiceResult<impl Responder> {
for page in ctx.settings.pages.iter() {
if page.secret == payload.secret {
let (tx, rx) = oneshot::channel();
web::block(|| {
let page = page.clone();
web::block(move || {
tx.send(page.update()).unwrap();
})
.await
@ -65,18 +66,18 @@ pub fn services(cfg: &mut web::ServiceConfig) {
#[cfg(test)]
mod tests {
use actix_web::{http::StatusCode, test, App};
use actix_web::{http::StatusCode, test};
use crate::services;
use crate::tests;
use crate::*;
use super::*;
#[actix_rt::test]
async fn deploy_update_works() {
let app = test::init_service(App::new().configure(services)).await;
let page = SETTINGS.pages.get(0);
let ctx = tests::get_data().await;
let app = get_app!(ctx).await;
let page = ctx.settings.pages.get(0);
let page = page.unwrap();
let mut payload = DeployEvent {
@ -86,10 +87,7 @@ mod tests {
let resp = test::call_service(
&app,
test::TestRequest::post()
.uri(V1_API_ROUTES.deploy.update)
.set_json(&payload)
.to_request(),
post_request!(&payload, V1_API_ROUTES.deploy.update).to_request(),
)
.await;
assert_eq!(resp.status(), StatusCode::OK);
@ -98,10 +96,7 @@ mod tests {
let resp = test::call_service(
&app,
test::TestRequest::post()
.uri(V1_API_ROUTES.deploy.update)
.set_json(&payload)
.to_request(),
post_request!(&payload, V1_API_ROUTES.deploy.update).to_request(),
)
.await;
assert_eq!(resp.status(), StatusCode::NOT_FOUND);

View file

@ -17,6 +17,7 @@
//! represents all the ways a trait can fail using this crate
use std::convert::From;
use std::io::Error as FSErrorInner;
use std::sync::Arc;
use actix_web::{
error::ResponseError,
@ -90,7 +91,7 @@ pub enum ServiceError {
_0,
_1
)]
PathTaken(Page, Page),
PathTaken(Arc<Page>, Arc<Page>),
/// when the a Secret configured for a page is already taken
#[display(
@ -98,7 +99,7 @@ pub enum ServiceError {
_0,
_1
)]
SecretTaken(Page, Page),
SecretTaken(Arc<Page>, Arc<Page>),
/// when the a Repository URL configured for a page is already taken
#[display(
@ -106,7 +107,7 @@ pub enum ServiceError {
_0,
_1
)]
DuplicateRepositoryURL(Page, Page),
DuplicateRepositoryURL(Arc<Page>, Arc<Page>),
#[display(fmt = "File System Error {}", _0)]
FSError(FSError),

View file

@ -15,28 +15,27 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use std::env;
use std::sync::Arc;
use actix_web::{
error::InternalError, http::StatusCode, middleware as actix_middleware, web::JsonConfig, App,
HttpServer,
error::InternalError, http::StatusCode, middleware as actix_middleware, web::Data as WebData,
web::JsonConfig, App, HttpServer,
};
use lazy_static::lazy_static;
use log::info;
mod ctx;
mod deploy;
mod errors;
mod meta;
mod page;
mod routes;
mod settings;
#[cfg(test)]
mod tests;
pub use routes::ROUTES as V1_API_ROUTES;
pub use settings::Settings;
lazy_static! {
pub static ref SETTINGS: Settings = Settings::new().unwrap();
}
pub const CACHE_AGE: u32 = 604800;
pub const GIT_COMMIT_HASH: &str = env!("GIT_HASH");
@ -45,6 +44,8 @@ pub const PKG_NAME: &str = env!("CARGO_PKG_NAME");
pub const PKG_DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
pub const PKG_HOMEPAGE: &str = env!("CARGO_PKG_HOMEPAGE");
pub type AppCtx = WebData<Arc<ctx::Ctx>>;
#[cfg(not(tarpaulin_include))]
#[actix_web::main]
async fn main() -> std::io::Result<()> {
@ -55,6 +56,9 @@ async fn main() -> std::io::Result<()> {
}
}
let settings = Settings::new().unwrap();
let ctx = WebData::new(ctx::Ctx::new(settings.clone()));
pretty_env_logger::init();
info!(
@ -62,12 +66,13 @@ async fn main() -> std::io::Result<()> {
PKG_NAME, PKG_DESCRIPTION, PKG_HOMEPAGE, VERSION, GIT_COMMIT_HASH
);
println!("Starting server on: http://{}", SETTINGS.server.get_ip());
info!("Starting server on: http://{}", settings.server.get_ip());
HttpServer::new(move || {
App::new()
.wrap(actix_middleware::Logger::default())
.wrap(actix_middleware::Compress::default())
.app_data(ctx.clone())
.app_data(get_json_err())
.wrap(
actix_middleware::DefaultHeaders::new()
@ -78,8 +83,8 @@ async fn main() -> std::io::Result<()> {
))
.configure(services)
})
.workers(SETTINGS.server.workers.unwrap_or_else(num_cpus::get))
.bind(SETTINGS.server.get_ip())
.workers(settings.server.workers.unwrap_or_else(num_cpus::get))
.bind(settings.server.get_ip())
.unwrap()
.run()
.await

View file

@ -17,13 +17,13 @@
use actix_web::{web, HttpResponse, Responder};
use serde::{Deserialize, Serialize};
use crate::{GIT_COMMIT_HASH, VERSION};
use crate::{AppCtx, GIT_COMMIT_HASH, VERSION};
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct BuildDetails {
pub version: &'static str,
pub git_commit_hash: &'static str,
pub source_code: &'static str,
pub struct BuildDetails<'a> {
pub version: &'a str,
pub git_commit_hash: &'a str,
pub source_code: &'a str,
}
pub mod routes {
@ -44,11 +44,11 @@ pub mod routes {
/// emmits build details of the bninary
#[my_codegen::get(path = "crate::V1_API_ROUTES.meta.build_details")]
async fn build_details() -> impl Responder {
async fn build_details(ctx: AppCtx) -> impl Responder {
let build = BuildDetails {
version: VERSION,
git_commit_hash: GIT_COMMIT_HASH,
source_code: &crate::SETTINGS.source_code,
source_code: &ctx.settings.source_code,
};
HttpResponse::Ok().json(build)
}
@ -59,14 +59,14 @@ pub fn services(cfg: &mut web::ServiceConfig) {
#[cfg(test)]
mod tests {
use actix_web::{http::StatusCode, test, App};
use actix_web::{http::StatusCode, test};
use crate::services;
use crate::*;
#[actix_rt::test]
async fn build_details_works() {
let app = test::init_service(App::new().configure(services)).await;
let ctx = tests::get_data().await;
let app = get_app!(ctx).await;
let resp = test::call_service(
&app,

View file

@ -16,6 +16,7 @@
*/
use std::env;
use std::path::Path;
use std::sync::Arc;
use config::{Config, Environment, File};
use log::warn;
@ -43,7 +44,7 @@ impl Server {
pub struct Settings {
pub server: Server,
pub source_code: String,
pub pages: Vec<Page>,
pub pages: Vec<Arc<Page>>,
}
#[cfg(not(tarpaulin_include))]
@ -101,20 +102,14 @@ impl Settings {
continue;
}
if page.secret == page2.secret {
log::error!(
"{}",
ServiceError::SecretTaken(page.to_owned(), page2.to_owned())
);
log::error!("{}", ServiceError::SecretTaken(page.clone(), page2.clone()));
} else if page.repo == page2.repo {
log::error!(
"{}",
ServiceError::DuplicateRepositoryURL(page.to_owned(), page2.to_owned(),)
ServiceError::DuplicateRepositoryURL(page.clone(), page2.clone(),)
);
} else if page.path == page2.path {
log::error!(
"{}",
ServiceError::PathTaken(page.to_owned(), page2.to_owned())
);
log::error!("{}", ServiceError::PathTaken(page.clone(), page2.clone()));
}
}
if let Err(e) = page.update() {