Initial commit

Basic POST and GET callback have been implemented
This commit is contained in:
2019-03-03 14:05:50 +01:00
commit d3bac73679
16 changed files with 2317 additions and 0 deletions

39
src/bin/callmeback.rs Normal file
View File

@@ -0,0 +1,39 @@
//extern crate callmeback;
extern crate dotenv;
extern crate env_logger;
extern crate listenfd;
use actix::prelude::*;
use actix_web::{server::HttpServer, App};
use callmeback::db::DbExecutor;
use dotenv::dotenv;
use listenfd::ListenFd;
use std::env;
pub fn main() {
::std::env::set_var("RUST_LOG", "actix_web=info");
env_logger::init();
dotenv().ok();
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let mut listenfd = ListenFd::from_env();
let sys = actix::System::new("callmeback");
let addr = SyncArbiter::start(3, move || DbExecutor::new(&database_url));
let mut server = HttpServer::new(move || {
callmeback::configure_app(App::with_state(callmeback::AppState { db: addr.clone() }))
})
.keep_alive(60)
.shutdown_timeout(60);
server = if let Some(l) = listenfd.take_tcp_listener(0).unwrap() {
server.listen(l)
} else {
server.bind("127.0.0.1:3000").unwrap()
};
server.start();
let _ = sys.run();
}

50
src/controller.rs Normal file
View File

@@ -0,0 +1,50 @@
pub mod callback {
use super::super::db::callback::{CreateCallback, GetCallback};
use super::super::AppState;
use actix_web::{
error, AsyncResponder, Error, FutureResponse, HttpResponse, Json, Path, State,
};
use chrono::prelude::*;
use futures::Future;
#[derive(Deserialize)]
pub struct CallbackCall {
pub url: String,
}
pub fn create(
(params, callback, state): (Path<(String,)>, Json<CallbackCall>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
let date_param = params
.0
.parse::<DateTime<Utc>>()
.map_err(|_e| error::ErrorBadRequest("Bad date"))
.unwrap();
state
.db
.send(CreateCallback {
url: callback.url.to_owned(),
scheduled_date: date_param,
})
.from_err()
.and_then(|res| match res {
Ok(callback) => Ok(HttpResponse::Ok().json(callback)),
Err(_) => Ok(HttpResponse::InternalServerError().into()),
})
}
pub fn get((id, state): (Path<i32>, State<AppState>)) -> FutureResponse<HttpResponse> {
state
.db
.send(GetCallback {
id: id.into_inner(),
})
.from_err()
.and_then(|res| match res {
Ok(callback) => Ok(HttpResponse::Ok().json(callback)),
Err(_) => Ok(HttpResponse::InternalServerError().into()),
})
.responder()
}
}

76
src/db.rs Normal file
View File

@@ -0,0 +1,76 @@
use actix::prelude::*;
use diesel::pg::PgConnection;
use diesel::prelude::*;
pub struct DbExecutor(PgConnection);
impl Actor for DbExecutor {
type Context = SyncContext<Self>;
}
impl DbExecutor {
pub fn new(database_url: &str) -> Self {
DbExecutor(
PgConnection::establish(database_url)
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url)),
)
}
}
pub mod callback {
use super::super::db::DbExecutor;
use super::super::models;
use super::super::schema::*;
use actix::prelude::*;
use chrono::prelude::*;
use diesel::prelude::*;
use actix_web::Error;
pub struct CreateCallback {
pub url: String,
pub scheduled_date: DateTime<Utc>,
}
impl Message for CreateCallback {
type Result = Result<models::Callback, Error>;
}
impl Handler<CreateCallback> for DbExecutor {
type Result = Result<models::Callback, Error>;
fn handle(&mut self, msg: CreateCallback, _: &mut Self::Context) -> Self::Result {
let new_callback = models::NewCallback {
url: &msg.url,
scheduled_date: &msg.scheduled_date,
};
Ok(diesel::insert_into(callbacks::table)
.values(&new_callback)
.get_result(&self.0)
.expect("Error inserting callback"))
}
}
pub struct GetCallback {
pub id: i32,
}
impl Message for GetCallback {
type Result = Result<models::Callback, Error>;
}
impl Handler<GetCallback> for DbExecutor {
type Result = Result<models::Callback, Error>;
fn handle(&mut self, msg: GetCallback, _: &mut Self::Context) -> Self::Result {
Ok(callbacks::table
.filter(callbacks::id.eq(msg.id))
.limit(1)
.load::<models::Callback>(&self.0)
.expect("Error loading callbacks")
.pop()
.unwrap())
}
}
}

41
src/lib.rs Normal file
View File

@@ -0,0 +1,41 @@
#[macro_use]
extern crate diesel;
extern crate actix;
extern crate actix_web;
extern crate actix_web_requestid;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate chrono;
mod controller;
pub mod db;
mod models;
mod schema;
use self::db::DbExecutor;
use actix::prelude::*;
use actix_web::middleware::Logger;
use actix_web::{http, App};
use actix_web_requestid::RequestIDHeader;
pub struct AppState {
pub db: Addr<DbExecutor>,
}
pub fn configure_app(app: App<AppState>) -> App<AppState> {
app.middleware(RequestIDHeader)
.middleware(Logger::default())
.resource("/{datetime}", |r| {
r.method(http::Method::POST).with_async_config(
controller::callback::create,
|(json_cfg,)| {
json_cfg.1.limit(4096);
},
);
})
.resource("/callback/{id}", |r| {
r.method(http::Method::GET).with(controller::callback::get)
})
}

16
src/models.rs Normal file
View File

@@ -0,0 +1,16 @@
use super::schema::callbacks;
use chrono::prelude::*;
#[derive(Queryable, Serialize)]
pub struct Callback {
id: i32,
url: String,
scheduled_date: DateTime<Utc>,
}
#[derive(Insertable)]
#[table_name = "callbacks"]
pub struct NewCallback<'a> {
pub url: &'a str,
pub scheduled_date: &'a DateTime<Utc>,
}

7
src/schema.rs Normal file
View File

@@ -0,0 +1,7 @@
table! {
callbacks (id) {
id -> Int4,
url -> Varchar,
scheduled_date -> Timestamptz,
}
}