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 |
# 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] |
mod postgres; |
||||||
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 user; |
mod user; |
||||||
|
|
||||||
use rocket::config::{Config, Environment, Value}; |
#[get("/users")] |
||||||
use rocket::fairing::AdHoc; |
async fn list_users(pool: web::Data<Pool>) -> HttpResponse { |
||||||
use rocket_contrib::json::Json; |
let client = match pool.get().await { |
||||||
use std::collections::HashMap; |
Ok(client) => client, |
||||||
use std::env; |
Err(err) => { |
||||||
|
log::debug!("unable to get postgres client: {:?}", err); |
||||||
// This macro from `diesel_migrations` defines an `embedded_migrations` module
|
return HttpResponse::InternalServerError().json("unable to get postgres client"); |
||||||
// 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, |
|
||||||
}; |
}; |
||||||
|
match user::User::all(&**client).await { |
||||||
Json(HelloMessage { |
Ok(list) => HttpResponse::Ok().json(list), |
||||||
message: format!("Hello with {} users", result), |
Err(err) => { |
||||||
}) |
log::debug!("unable to fetch users: {:?}", err); |
||||||
|
return HttpResponse::InternalServerError().json("unable to fetch users"); |
||||||
|
} |
||||||
|
} |
||||||
} |
} |
||||||
|
|
||||||
fn get_config() -> Config { |
fn address() -> String { |
||||||
let mut database_config = HashMap::new(); |
std::env::var("ADDRESS").unwrap_or_else(|_| "127.0.0.1:8000".into()) |
||||||
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 run_db_migrations(r: rocket::Rocket) -> Result<rocket::Rocket, rocket::Rocket> { |
#[actix_web::main] |
||||||
let conn = MyDBConn::get_one(&r).expect("database connection"); |
async fn main() -> std::io::Result<()> { |
||||||
match embedded_migrations::run(&*conn) { |
env_logger::init(); |
||||||
Ok(()) => Ok(r), |
|
||||||
Err(e) => { |
|
||||||
println!("Failed to run database migrations: {:?}", e); |
|
||||||
Err(r) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
fn main() { |
let pg_pool = postgres::create_pool(); |
||||||
let config = get_config(); |
postgres::migrate_up(&pg_pool).await; |
||||||
rocket::custom(config) |
|
||||||
.attach(MyDBConn::fairing()) |
let address = address(); |
||||||
.attach(AdHoc::on_attach("Database Migrations", run_db_migrations)) |
HttpServer::new(move || { |
||||||
.mount("/", routes![index]) |
App::new() |
||||||
.launch(); |
.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; |
#[derive(Debug, serde::Serialize)] |
||||||
use diesel::prelude::*; |
|
||||||
use super::schema::users; |
|
||||||
|
|
||||||
#[derive(Queryable, AsChangeset, Serialize, Deserialize)] |
|
||||||
#[table_name = "users"] |
|
||||||
pub struct User { |
pub struct User { |
||||||
pub id: i32, |
pub id: i32, |
||||||
pub login: String, |
pub login: String, |
||||||
} |
} |
||||||
|
|
||||||
|
impl From<Row> for User { |
||||||
|
fn from(row: Row) -> Self { |
||||||
|
Self { |
||||||
|
id: row.get(0), |
||||||
|
login: row.get(1), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
impl User { |
impl User { |
||||||
pub fn all(connection: &PgConnection) -> QueryResult<Vec<User>> { |
pub async fn all<C: GenericClient>(client: &C) -> Result<Vec<User>, Error> { |
||||||
users::table.load::<User>(&*connection) |
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