diff --git a/.circleci/config.yml b/.circleci/config.yml index 8feb6ec..200709c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,17 +6,12 @@ jobs: working_directory: ~/repo steps: - checkout - - restore_cache: - keys: - - v1-dependencies-{{ checksum "Cargo.lock" }} - run: name: install dependencies command: | cargo build - - save_cache: - paths: - - ~/.cargo - key: v1-dependencies-{{ checksum "Cargo.lock" }} + cargo install diesel_cli + diesel migration run - run: name: run test suite command: | diff --git a/Cargo.lock b/Cargo.lock index 8ff3843..8508e37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -568,6 +568,7 @@ dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "diesel_derives 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libsqlite3-sys 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "r2d2 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1136,6 +1137,16 @@ dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "r2d2" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scheduled-thread-pool 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand" version = "0.6.5" @@ -1315,6 +1326,7 @@ dependencies = [ "askama 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "diesel 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "r2d2 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "slog-async 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "slog-term 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1327,6 +1339,14 @@ name = "ryu" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "scheduled-thread-pool" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "scoped_threadpool" version = "0.1.9" @@ -1992,6 +2012,7 @@ dependencies = [ "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" +"checksum r2d2 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bc42ce75d9f4447fb2a04bbe1ed5d18dd949104572850ec19b164e274919f81b" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" @@ -2012,6 +2033,7 @@ dependencies = [ "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" +"checksum scheduled-thread-pool 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bbecfcb36d47e0d6a4aefb198d475b13aa06e326770c1271171d44893766ae1c" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" diff --git a/Cargo.toml b/Cargo.toml index ad5dc47..505ea11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,8 @@ authors = ["Gonçalo Valério "] [dependencies] actix = "0.8.3" actix-web = "1.0.2" -diesel = { version = "1.4.2", features = ["sqlite"] } +r2d2 = "0.8.5" +diesel = { version = "1.4.2", features = ["sqlite", "r2d2"] } toml = "0.5.0" clap = "2.32.0" askama = "0.8" diff --git a/src/actions.rs b/src/actions.rs index d8be674..6f6def6 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -1,3 +1,65 @@ -use url::form_urlencoded::Parse; +use super::schema::subscriptions; +use diesel::prelude::*; +use models::NewSubscription; +use std::collections::HashMap; +use std::convert::TryFrom; +use std::time::{SystemTime, UNIX_EPOCH}; +use utils::{setup_logging, Pool}; -pub fn handle_subscription(data: Parse) {} +pub fn handle_subscription(db: &Pool, data: &HashMap) -> bool { + let log = setup_logging(); + let mode; + let callback; + let topic; + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("Invalid Time") + .as_secs(); + let now = i32::try_from(timestamp).ok().unwrap(); + let conn = db.get().unwrap(); + + match data.get("hub.mode") { + Some(value) => mode = value, + None => return false, + } + + match data.get("hub.callback") { + Some(value) => callback = value, + None => return false, + } + + match data.get("hub.topic") { + Some(value) => topic = value, + None => return false, + } + + debug!( + log, + "Mode: {}, Callback: {}, topic: {}", mode, callback, topic + ); + + if mode == &"subscribe" { + let subscription = NewSubscription { + callback: callback, + topic: topic, + sec: "", + created_at: &now, + expires_at: &now, + }; + + diesel::insert_into(subscriptions::table) + .values(&subscription) + .execute(&conn) + .expect("Error saving new subscription"); + debug!(log, "Subscription created."); + return true; + } else if mode == &"unsubscribe" { + return true; + } else { + debug!(log, "Wrong method."); + return false; + } +} + +#[cfg(test)] +mod tests {} diff --git a/src/controllers.rs b/src/controllers.rs index 2892f11..97f485c 100644 --- a/src/controllers.rs +++ b/src/controllers.rs @@ -1,9 +1,9 @@ use actions::handle_subscription; use actix_web::{http, web, HttpRequest, HttpResponse}; use askama::Template; +use std::collections::HashMap; use url::form_urlencoded; use utils::{validate_parsed_data, AppState}; -use std::collections::HashMap; #[derive(Template)] #[template(path = "index.html")] @@ -17,6 +17,7 @@ pub fn index(_state: web::Data, _req: HttpRequest) -> HttpResponse { pub fn hub(state: web::Data, _req: HttpRequest, params: String) -> HttpResponse { let log = &state.log; + let db = &state.db; info!(log, "Received Request"); debug!(log, "Content: {}", params); @@ -26,13 +27,14 @@ pub fn hub(state: web::Data, _req: HttpRequest, params: String) -> Htt parameters.insert(key.to_string(), value.to_string()); } - if !validate_parsed_data(parameters) { + if !validate_parsed_data(¶meters) { return HttpResponse::Ok() .status(http::StatusCode::BAD_REQUEST) .finish(); } - handle_subscription(parsed_data); + let result = handle_subscription(db, ¶meters); + debug!(log, "{}", result); return HttpResponse::Ok() .status(http::StatusCode::ACCEPTED) .finish(); @@ -41,21 +43,23 @@ pub fn hub(state: web::Data, _req: HttpRequest, params: String) -> Htt #[cfg(test)] mod tests { use super::*; - use actix::{SyncArbiter, System}; + use actix::System; use actix_web::{http, test, web}; use diesel::prelude::*; - use utils::{setup_logging, DbExecutor}; + use diesel::r2d2::{self, ConnectionManager}; + use utils::setup_logging; #[test] fn test_index() { let _sys = System::new("rusty-hub-test"); - let addr = SyncArbiter::start(1, || { - DbExecutor(SqliteConnection::establish("test.db").unwrap()) - }); + let manager = ConnectionManager::::new("test.db"); + let pool = r2d2::Pool::builder() + .build(manager) + .expect("Failed to create pool."); let data = web::Data::new(AppState { log: setup_logging(), - db: addr.clone(), + db: pool.clone(), }); let resp = index(data, test::TestRequest::get().to_http_request()); @@ -65,13 +69,14 @@ mod tests { #[test] fn test_hub_no_parameters() { let _sys = System::new("rusty-hub-test"); - let addr = SyncArbiter::start(1, || { - DbExecutor(SqliteConnection::establish("test.db").unwrap()) - }); + let manager = ConnectionManager::::new("test.db"); + let pool = r2d2::Pool::builder() + .build(manager) + .expect("Failed to create pool."); let data = web::Data::new(AppState { log: setup_logging(), - db: addr.clone(), + db: pool.clone(), }); let resp = hub( @@ -85,13 +90,14 @@ mod tests { #[test] fn test_hub_invalid_callback() { let _sys = System::new("rusty-hub-test"); - let addr = SyncArbiter::start(1, || { - DbExecutor(SqliteConnection::establish("test.db").unwrap()) - }); + let manager = ConnectionManager::::new("test.db"); + let pool = r2d2::Pool::builder() + .build(manager) + .expect("Failed to create pool."); let data = web::Data::new(AppState { log: setup_logging(), - db: addr.clone(), + db: pool.clone(), }); let resp = hub( @@ -105,13 +111,14 @@ mod tests { #[test] fn test_hub_invalid_topic() { let _sys = System::new("rusty-hub-test"); - let addr = SyncArbiter::start(1, || { - DbExecutor(SqliteConnection::establish("test.db").unwrap()) - }); + let manager = ConnectionManager::::new("test.db"); + let pool = r2d2::Pool::builder() + .build(manager) + .expect("Failed to create pool."); let data = web::Data::new(AppState { log: setup_logging(), - db: addr.clone(), + db: pool.clone(), }); let resp = hub( @@ -125,13 +132,14 @@ mod tests { #[test] fn test_hub_invalid_mode() { let _sys = System::new("rusty-hub-test"); - let addr = SyncArbiter::start(1, || { - DbExecutor(SqliteConnection::establish("test.db").unwrap()) - }); + let manager = ConnectionManager::::new("test.db"); + let pool = r2d2::Pool::builder() + .build(manager) + .expect("Failed to create pool."); let data = web::Data::new(AppState { log: setup_logging(), - db: addr.clone(), + db: pool.clone(), }); let resp = hub( @@ -145,13 +153,14 @@ mod tests { #[test] fn test_hub_subscribe_success() { let _sys = System::new("rusty-hub-test"); - let addr = SyncArbiter::start(1, || { - DbExecutor(SqliteConnection::establish("test.db").unwrap()) - }); + let manager = ConnectionManager::::new("test.db"); + let pool = r2d2::Pool::builder() + .build(manager) + .expect("Failed to create pool."); let data = web::Data::new(AppState { log: setup_logging(), - db: addr.clone(), + db: pool.clone(), }); let resp = hub( diff --git a/src/main.rs b/src/main.rs index 8f6efc8..96562f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,12 +9,13 @@ extern crate slog; extern crate slog_async; extern crate slog_term; extern crate url; -use actix::{SyncArbiter, System}; +use actix::{System}; use actix_web::{web, App, HttpServer}; use clap::Arg; use controllers::{hub, index}; use diesel::prelude::*; -use utils::{setup_logging, AppState, DbExecutor}; +use diesel::r2d2::{self, ConnectionManager}; +use utils::{setup_logging, AppState}; mod actions; mod controllers; @@ -50,13 +51,14 @@ fn main() { } let sys = System::new("rusty-hub"); - let addr = SyncArbiter::start(3, || { - DbExecutor(SqliteConnection::establish("local.db").unwrap()) - }); + let manager = ConnectionManager::::new("local.db"); + let pool = r2d2::Pool::builder() + .build(manager) + .expect("Failed to create pool."); let app_data = web::Data::new(AppState { log: setup_logging(), - db: addr.clone(), + db: pool.clone(), }); info!(log, "Starting server"); diff --git a/src/utils.rs b/src/utils.rs index 4af4f45..a577439 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,19 +1,15 @@ -use actix::{Actor, Addr, SyncContext}; use diesel::prelude::*; +use diesel::r2d2::{self, ConnectionManager}; use slog::Drain; use std::collections::HashMap; use url::Url; -pub struct DbExecutor(pub SqliteConnection); - -impl Actor for DbExecutor { - type Context = SyncContext; -} +pub type Pool = r2d2::Pool>; pub struct AppState { pub log: slog::Logger, - pub db: Addr, + pub db: Pool, } pub fn setup_logging() -> slog::Logger { @@ -23,7 +19,7 @@ pub fn setup_logging() -> slog::Logger { slog::Logger::root(drain, o!()) } -pub fn validate_parsed_data(parameters: HashMap) -> bool { +pub fn validate_parsed_data(parameters: &HashMap) -> bool { let callback; let mode; let topic;