tutorial running
This commit is contained in:
parent
9dc56c9c7f
commit
89f8d1a23c
7
db.sql
Normal file
7
db.sql
Normal file
|
@ -0,0 +1,7 @@
|
|||
CREATE TABLE IF NOT EXISTS todo
|
||||
(
|
||||
id SERIAL PRIMARY KEY NOT NULL,
|
||||
name VARCHAR(255),
|
||||
created_at timestamp with time zone DEFAULT (now() at time zone 'utc'),
|
||||
checked boolean DEFAULT false
|
||||
);
|
32
src/db.rs
32
src/db.rs
|
@ -1,21 +1,45 @@
|
|||
use crate::{DBCon, DBPool};
|
||||
use mobc::{Pool};
|
||||
use mobc_postgres::{tokio_postgres, PgConnectionManager};
|
||||
use tokio_postgres::{Config, Error, NoTls};
|
||||
use std::fs;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use crate::error::Error::{*};
|
||||
use crate::error;
|
||||
|
||||
type Result<T> = std::result::Result<T, error::Error>;
|
||||
|
||||
const DB_POOL_MAX_OPEN: u64 = 32;
|
||||
const DB_POOL_MAX_IDLE: u64 = 8;
|
||||
const DB_POOL_TIMEOUT_SECONDS: u64 = 15;
|
||||
const INIT_SQL: &str = "./db.sql";
|
||||
|
||||
pub async fn init_db(db_pool: &DBPool) -> Result<()> {
|
||||
let init_file = fs::read_to_string(INIT_SQL)?;
|
||||
let con = get_db_con(db_pool).await?;
|
||||
con.batch_execute(init_file.as_str())
|
||||
.await
|
||||
.map_err(DBInitError)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_db_con(db_pool: &DBPool) -> Result<DBCon> {
|
||||
db_pool.get().await.map_err(DBPoolError)
|
||||
}
|
||||
|
||||
pub fn create_pool() -> std::result::Result<DBPool, mobc::Error<Error>> {
|
||||
let config = Config::from_str("postgres://postgres@127.0.0.1:7878/postgres")?;
|
||||
|
||||
let manager = PgConnectionManager::new(config, NoTls);
|
||||
Ok(Pool::builder()
|
||||
.max_open(DB_POOL_MAX_OPEN)
|
||||
.max_idle(DB_POOL_MAX_IDLE)
|
||||
.get_timeout(Some(Duration::from_secs(DB_POOL_TIMEOUT_SECONDS)))
|
||||
.build(manager))
|
||||
.max_open(DB_POOL_MAX_OPEN)
|
||||
.max_idle(DB_POOL_MAX_IDLE)
|
||||
.get_timeout(Some(Duration::from_secs(DB_POOL_TIMEOUT_SECONDS)))
|
||||
.build(manager))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
7
src/db.sql
Normal file
7
src/db.sql
Normal file
|
@ -0,0 +1,7 @@
|
|||
CREATE TABLE IF NOT EXISTS todo
|
||||
(
|
||||
id SERIAL PRIMARY KEY NOT NULL,
|
||||
name VARCHAR(255),
|
||||
created_at timestamp with time zone DEFAULT (now() at time zone 'utc'),
|
||||
checked boolean DEFAULT false
|
||||
);
|
64
src/error.rs
Normal file
64
src/error.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
use mobc_postgres::tokio_postgres;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use thiserror::Error;
|
||||
use warp::{http::StatusCode, Filter,Rejection,Reply};
|
||||
use crate::Infallible;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("error getting connection from DB pool: {0}")]
|
||||
DBPoolError(mobc::Error<tokio_postgres::Error>),
|
||||
#[error("error executing DB query: {0}")]
|
||||
DBQueryError(#[from] tokio_postgres::Error),
|
||||
#[error("error creating table: {0}")]
|
||||
DBInitError(tokio_postgres::Error),
|
||||
#[error("error reading file: {0}")]
|
||||
ReadFileError(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
impl warp::reject::Reject for Error {}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ErrorResponse {
|
||||
message: String,
|
||||
}
|
||||
|
||||
pub async fn handle_rejection(err: Rejection) -> std::result::Result<impl Reply, Infallible> {
|
||||
let code;
|
||||
let message;
|
||||
|
||||
if err.is_not_found() {
|
||||
code = StatusCode::NOT_FOUND;
|
||||
message = "Not Found";
|
||||
} else if let Some(_) = err.find::<warp::filters::body::BodyDeserializeError>() {
|
||||
code = StatusCode::BAD_REQUEST;
|
||||
message = "Invalid Body";
|
||||
} else if let Some(e) = err.find::<Error>() {
|
||||
match e {
|
||||
Error::DBQueryError(_) => {
|
||||
code = StatusCode::BAD_REQUEST;
|
||||
message = "Could not Execute request";
|
||||
}
|
||||
_ => {
|
||||
eprintln!("unhandled application error: {:?}", err);
|
||||
code = StatusCode::INTERNAL_SERVER_ERROR;
|
||||
message = "Internal Server Error";
|
||||
}
|
||||
}
|
||||
} else if let Some(_) = err.find::<warp::reject::MethodNotAllowed>() {
|
||||
code = StatusCode::METHOD_NOT_ALLOWED;
|
||||
message = "Method Not Allowed";
|
||||
} else {
|
||||
eprintln!("unhandled error: {:?}", err);
|
||||
code = StatusCode::INTERNAL_SERVER_ERROR;
|
||||
message = "Internal Server Error";
|
||||
}
|
||||
|
||||
let json = warp::reply::json(&ErrorResponse {
|
||||
message: message.into(),
|
||||
});
|
||||
|
||||
Ok(warp::reply::with_status(json, code))
|
||||
}
|
||||
|
||||
|
14
src/handler.rs
Normal file
14
src/handler.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use crate::{db, DBPool};
|
||||
use crate::error::Error::{*};
|
||||
use warp::{http::StatusCode, reject, Reply, Rejection};
|
||||
|
||||
pub async fn health_handler(db_pool: DBPool) -> std::result::Result<impl Reply, Rejection> {
|
||||
let db = db::get_db_con(&db_pool)
|
||||
.await
|
||||
.map_err(|e| reject::custom(e))?;
|
||||
|
||||
db.execute("SELECT 1", &[])
|
||||
.await
|
||||
.map_err(|e| reject::custom(DBQueryError(e)))?;
|
||||
Ok(StatusCode::OK)
|
||||
}
|
25
src/main.rs
25
src/main.rs
|
@ -1,21 +1,34 @@
|
|||
// mod data;
|
||||
// mod db;
|
||||
// mod error;
|
||||
// mod handler;
|
||||
mod db;
|
||||
mod error;
|
||||
mod handler;
|
||||
|
||||
use warp::{http::StatusCode, Filter};
|
||||
use warp::{http::StatusCode, Filter,Rejection};
|
||||
use mobc::{Connection, Pool};
|
||||
use mobc_postgres::{tokio_postgres, PgConnectionManager};
|
||||
use tokio_postgres::NoTls;
|
||||
use std::convert::Infallible;
|
||||
|
||||
type DBCon = Connection<PgConnectionManager<NoTls>>;
|
||||
type DBPool = Pool<PgConnectionManager<NoTls>>;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let db_pool = db::create_pool().expect("database pool can be created");
|
||||
|
||||
db::init_db(&db_pool)
|
||||
.await
|
||||
.expect("database can be initialized");
|
||||
|
||||
let health_route = warp::path!("health")
|
||||
.map(|| StatusCode::OK);
|
||||
.and(with_db(db_pool.clone()))
|
||||
.and_then(handler::health_handler);
|
||||
let routes = health_route
|
||||
.with(warp::cors().allow_any_origin());
|
||||
.with(warp::cors().allow_any_origin())
|
||||
.recover(error::handle_rejection);
|
||||
warp::serve(routes).run(([127, 0, 0, 1], 8000)).await;
|
||||
}
|
||||
|
||||
fn with_db(db_pool: DBPool) -> impl Filter<Extract = (DBPool,), Error = Infallible> + Clone {
|
||||
warp::any().map(move || db_pool.clone())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue