refacto(react-rust-postgres): replace rocket by actix-web (#153)
Signed-off-by: Jérémie Drouet <jeremie.drouet@gmail.com>pull/157/head
parent
0ae9d4cea7
commit
a13fabe604
@ -1,5 +0,0 @@ |
||||
# For documentation on how to configure this file, |
||||
# see diesel.rs/guides/configuring-diesel-cli |
||||
|
||||
[print_schema] |
||||
file = "src/schema.rs" |
@ -1,6 +0,0 @@ |
||||
-- This file was automatically created by Diesel to setup helper functions |
||||
-- and other internal bookkeeping. This file is safe to edit, any future |
||||
-- changes will be added to existing projects as new migrations. |
||||
|
||||
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass); |
||||
DROP FUNCTION IF EXISTS diesel_set_updated_at(); |
@ -1,36 +0,0 @@ |
||||
-- This file was automatically created by Diesel to setup helper functions |
||||
-- and other internal bookkeeping. This file is safe to edit, any future |
||||
-- changes will be added to existing projects as new migrations. |
||||
|
||||
|
||||
|
||||
|
||||
-- Sets up a trigger for the given table to automatically set a column called |
||||
-- `updated_at` whenever the row is modified (unless `updated_at` was included |
||||
-- in the modified columns) |
||||
-- |
||||
-- # Example |
||||
-- |
||||
-- ```sql |
||||
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW()); |
||||
-- |
||||
-- SELECT diesel_manage_updated_at('users'); |
||||
-- ``` |
||||
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$ |
||||
BEGIN |
||||
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s |
||||
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl); |
||||
END; |
||||
$$ LANGUAGE plpgsql; |
||||
|
||||
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$ |
||||
BEGIN |
||||
IF ( |
||||
NEW IS DISTINCT FROM OLD AND |
||||
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at |
||||
) THEN |
||||
NEW.updated_at := current_timestamp; |
||||
END IF; |
||||
RETURN NEW; |
||||
END; |
||||
$$ LANGUAGE plpgsql; |
@ -1,4 +1,4 @@ |
||||
# Backend |
||||
|
||||
This backend is made with Rust using [Rocket](https://rocket.rs/) as a web server and [Diesel](https://diesel.rs/) as an ORM. |
||||
This backend is made with Rust using [actix-web](https://actix.rs/) as a web server and [deadpool-postgres](https://crates.io/crates/deadpool-postgres) as a connection manager. |
||||
|
||||
|
@ -1,96 +1,45 @@ |
||||
#![feature(proc_macro_hygiene, decl_macro)] |
||||
use actix_web::{get, web, App, HttpResponse, HttpServer}; |
||||
use deadpool_postgres::Pool; |
||||
|
||||
#[macro_use] |
||||
extern crate diesel; |
||||
#[macro_use] |
||||
extern crate diesel_migrations; |
||||
#[macro_use] |
||||
extern crate rocket; |
||||
#[macro_use] |
||||
extern crate serde_derive; |
||||
#[macro_use] |
||||
extern crate rocket_contrib; |
||||
|
||||
mod schema; |
||||
mod postgres; |
||||
mod user; |
||||
|
||||
use rocket::config::{Config, Environment, Value}; |
||||
use rocket::fairing::AdHoc; |
||||
use rocket_contrib::json::Json; |
||||
use std::collections::HashMap; |
||||
use std::env; |
||||
|
||||
// This macro from `diesel_migrations` defines an `embedded_migrations` module
|
||||
// containing a function named `run`. This allows the example to be run and
|
||||
// tested without any outside setup of the database.
|
||||
embed_migrations!(); |
||||
|
||||
#[database("my_db")] |
||||
struct MyDBConn(diesel::PgConnection); |
||||
|
||||
#[derive(Serialize)] |
||||
struct HelloMessage { |
||||
message: String, |
||||
} |
||||
|
||||
#[get("/")] |
||||
fn index(conn: MyDBConn) -> Json<HelloMessage> { |
||||
let result = match user::User::all(&*conn) { |
||||
Ok(res) => res.len(), |
||||
Err(_) => 0, |
||||
#[get("/users")] |
||||
async fn list_users(pool: web::Data<Pool>) -> HttpResponse { |
||||
let client = match pool.get().await { |
||||
Ok(client) => client, |
||||
Err(err) => { |
||||
log::debug!("unable to get postgres client: {:?}", err); |
||||
return HttpResponse::InternalServerError().json("unable to get postgres client"); |
||||
} |
||||
}; |
||||
|
||||
Json(HelloMessage { |
||||
message: format!("Hello with {} users", result), |
||||
}) |
||||
match user::User::all(&**client).await { |
||||
Ok(list) => HttpResponse::Ok().json(list), |
||||
Err(err) => { |
||||
log::debug!("unable to fetch users: {:?}", err); |
||||
return HttpResponse::InternalServerError().json("unable to fetch users"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn get_config() -> Config { |
||||
let mut database_config = HashMap::new(); |
||||
let mut databases = HashMap::new(); |
||||
|
||||
let env_address = env::var("ROCKET_ADDRESS") |
||||
.or::<String>(Ok(String::from("localhost"))) |
||||
.unwrap(); |
||||
|
||||
let env_mode = env::var("ROCKET_ENV") |
||||
.or(Ok(String::from("development"))) |
||||
.and_then(|value| value.parse::<Environment>()) |
||||
.unwrap(); |
||||
|
||||
let database_url = match env::var("DATABASE_URL") { |
||||
Ok(value) => value, |
||||
Err(_) => String::from("postgres://localhost/postgres"), |
||||
}; |
||||
|
||||
database_config.insert("url", Value::from(database_url)); |
||||
databases.insert("my_db", Value::from(database_config)); |
||||
|
||||
let config = Config::build(env_mode) |
||||
.address(env_address) |
||||
.extra("databases", databases) |
||||
.finalize() |
||||
.unwrap(); |
||||
|
||||
config |
||||
fn address() -> String { |
||||
std::env::var("ADDRESS").unwrap_or_else(|_| "127.0.0.1:8000".into()) |
||||
} |
||||
|
||||
fn run_db_migrations(r: rocket::Rocket) -> Result<rocket::Rocket, rocket::Rocket> { |
||||
let conn = MyDBConn::get_one(&r).expect("database connection"); |
||||
match embedded_migrations::run(&*conn) { |
||||
Ok(()) => Ok(r), |
||||
Err(e) => { |
||||
println!("Failed to run database migrations: {:?}", e); |
||||
Err(r) |
||||
} |
||||
} |
||||
} |
||||
#[actix_web::main] |
||||
async fn main() -> std::io::Result<()> { |
||||
env_logger::init(); |
||||
|
||||
fn main() { |
||||
let config = get_config(); |
||||
rocket::custom(config) |
||||
.attach(MyDBConn::fairing()) |
||||
.attach(AdHoc::on_attach("Database Migrations", run_db_migrations)) |
||||
.mount("/", routes![index]) |
||||
.launch(); |
||||
let pg_pool = postgres::create_pool(); |
||||
postgres::migrate_up(&pg_pool).await; |
||||
|
||||
let address = address(); |
||||
HttpServer::new(move || { |
||||
App::new() |
||||
.app_data(web::Data::new(pg_pool.clone())) |
||||
.service(list_users) |
||||
}) |
||||
.bind(&address)? |
||||
.run() |
||||
.await |
||||
} |
||||
|
@ -0,0 +1,40 @@ |
||||
use deadpool_postgres::{Config, Pool}; |
||||
use tokio_postgres::NoTls; |
||||
use tokio_postgres_migration::Migration; |
||||
|
||||
const SCRIPTS_UP: [(&str, &str); 1] = [( |
||||
"0001_create-users", |
||||
include_str!("../migrations/0001_create-users_up.sql"), |
||||
)]; |
||||
|
||||
fn create_config() -> Config { |
||||
let mut cfg = Config::new(); |
||||
if let Ok(host) = std::env::var("PG_HOST") { |
||||
cfg.host = Some(host); |
||||
} |
||||
if let Ok(dbname) = std::env::var("PG_DBNAME") { |
||||
cfg.dbname = Some(dbname); |
||||
} |
||||
if let Ok(user) = std::env::var("PG_USER") { |
||||
cfg.user = Some(user); |
||||
} |
||||
if let Ok(password) = std::env::var("PG_PASSWORD") { |
||||
cfg.password = Some(password); |
||||
} |
||||
cfg |
||||
} |
||||
|
||||
pub fn create_pool() -> Pool { |
||||
create_config() |
||||
.create_pool(NoTls) |
||||
.expect("couldn't create postgres pool") |
||||
} |
||||
|
||||
pub async fn migrate_up(pool: &Pool) { |
||||
let mut client = pool.get().await.expect("couldn't get postgres client"); |
||||
let migration = Migration::new("migrations".to_string()); |
||||
migration |
||||
.up(&mut **client, &SCRIPTS_UP) |
||||
.await |
||||
.expect("couldn't run migrations"); |
||||
} |
@ -1,6 +0,0 @@ |
||||
table! { |
||||
users (id) { |
||||
id -> Int4, |
||||
login -> Text, |
||||
} |
||||
} |
@ -1,18 +1,25 @@ |
||||
#![allow(proc_macro_derive_resolution_fallback)] |
||||
use tokio_postgres::{Error, GenericClient, Row}; |
||||
|
||||
use diesel; |
||||
use diesel::prelude::*; |
||||
use super::schema::users; |
||||
|
||||
#[derive(Queryable, AsChangeset, Serialize, Deserialize)] |
||||
#[table_name = "users"] |
||||
#[derive(Debug, serde::Serialize)] |
||||
pub struct User { |
||||
pub id: i32, |
||||
pub login: String, |
||||
} |
||||
|
||||
impl From<Row> for User { |
||||
fn from(row: Row) -> Self { |
||||
Self { |
||||
id: row.get(0), |
||||
login: row.get(1), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl User { |
||||
pub fn all(connection: &PgConnection) -> QueryResult<Vec<User>> { |
||||
users::table.load::<User>(&*connection) |
||||
pub async fn all<C: GenericClient>(client: &C) -> Result<Vec<User>, Error> { |
||||
let stmt = client.prepare("SELECT id, login FROM users").await?; |
||||
let rows = client.query(&stmt, &[]).await?; |
||||
|
||||
Ok(rows.into_iter().map(User::from).collect()) |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue