tutorial running

first_own_trials
Gandalf 2023-06-10 12:48:36 +02:00
parent 9dc56c9c7f
commit 89f8d1a23c
6 changed files with 139 additions and 10 deletions

7
db.sql Normal file
View 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
);

View File

@ -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
View 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
View 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
View 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)
}

View File

@ -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())
}