Skip to content

Commit

Permalink
Update to Rocket 0.5.1
Browse files Browse the repository at this point in the history
Start using async diesel
  • Loading branch information
threecgreen committed Jun 20, 2024
1 parent 029a0f2 commit 34124b3
Show file tree
Hide file tree
Showing 32 changed files with 2,204 additions and 1,870 deletions.
1,881 changes: 1,133 additions & 748 deletions Cargo.lock

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ bcrypt = "0.15.0"
# Datetime functionality
chrono = { version = "0.4.31", features = ["serde"] }
# basic ORM
diesel = { version = "2.1.4", features = ["postgres", "chrono"] }
diesel = { version = "2.1.4", features = ["chrono", "postgres"] }
diesel-async = { version = "0.4.1", features = ["async-connection-wrapper"] }
diesel_migrations = "2.1.0"
# Image handling
image = "0.24.7"
Expand All @@ -34,14 +35,14 @@ validator = "0.16.1"
validator_derive = "0.16.0"

[dependencies.rocket]
version = "0.5.0"
default_features = false
version = "0.5.1"
default-features = false
features = ["secrets", "json"]

[dependencies.rocket_sync_db_pools]
version = "0.1.0"
[dependencies.rocket_db_pools]
version = "0.2.0"
default-features = false
features = ["diesel_postgres_pool"]
features = ["diesel_postgres"]

# Generate typescript type definitions from rust structs
[dependencies.typescript-definitions]
Expand Down
13 changes: 7 additions & 6 deletions src/cached_static.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::borrow::Cow;
use std::path::{Path, PathBuf};

use chrono::{DateTime, Utc};
use rocket::http::{uncased::Uncased, ContentType, Header, Method, Status};
use rocket::response::{self, Responder, Response};
Expand All @@ -9,8 +12,6 @@ use rocket::{
route::Handler,
};
use rocket::{Data, Request, Route};
use std::borrow::Cow;
use std::path::{Path, PathBuf};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NotModified<R>(pub R);
Expand Down Expand Up @@ -139,15 +140,15 @@ impl Handler for CachedStaticFiles {
let current_route = req.route().expect("route while handling");
let is_segments_route = current_route.uri.path().ends_with('>');
if !is_segments_route {
return Outcome::forward(data);
return Outcome::forward(data, Status::Ok);
};
let path = req
.routed_segments(0..)
.to_path_buf(false /* allow dotfiles */)
.ok()
.map(|pb| self.root.join(pb));
match path {
Some(path) if path.is_dir() => Outcome::forward(data),
Some(path) if path.is_dir() => Outcome::forward(data, Status::Ok),
Some(path) if path.exists() => {
let gz_path = PathBuf::from(&format!("{}.gz", path.to_string_lossy()));
let accept_encoding: Option<std::collections::HashSet<String>> = req
Expand All @@ -170,9 +171,9 @@ impl Handler for CachedStaticFiles {
"Request received for static file that doesn't exist at path {:?}. Root: {:?}",
path, self.root
);
Outcome::failure(Status::NotFound)
Outcome::error(Status::NotFound)
}
None => Outcome::forward(data),
None => Outcome::forward(data, Status::BadRequest),
}
}
}
47 changes: 25 additions & 22 deletions src/colors.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,38 @@
use diesel::dsl::sql;
use diesel::prelude::*;
use diesel_async::RunQueryDsl;
use rocket::serde::json::Json;
use rocket_db_pools::Connection;

use crate::error::{RestResult, VinotecaError};
use crate::models::{generic, Color};
use crate::schema::{colors, purchases, wines};
use crate::users::Auth;
use crate::DbConn;

use diesel::dsl::sql;
use diesel::prelude::*;
use rocket::serde::json::Json;
use crate::Db;

#[get("/colors?<id>&<name>")]
pub async fn get(id: Option<i32>, name: Option<String>, conn: DbConn) -> RestResult<Vec<Color>> {
conn.run(move |c| {
let mut query = colors::table.into_boxed();
if let Some(id) = id {
query = query.filter(colors::id.eq(id));
}
if let Some(name) = name {
query = query.filter(colors::name.eq(name));
}
pub async fn get(
id: Option<i32>,
name: Option<String>,
mut conn: Connection<Db>,
) -> RestResult<Vec<Color>> {
let mut query = colors::table.into_boxed();
if let Some(id) = id {
query = query.filter(colors::id.eq(id));
}
if let Some(name) = name {
query = query.filter(colors::name.eq(name));
}

query
.load::<Color>(c)
.map(Json)
.map_err(VinotecaError::from)
})
.await
query
.load::<Color>(&mut **conn)
.await
.map(Json)
.map_err(VinotecaError::from)
}

#[get("/colors/top")]
pub async fn top(auth: Auth, conn: DbConn) -> RestResult<Vec<generic::TopEntity>> {
pub async fn top(auth: Auth, mut conn: Connection<Db>) -> RestResult<Vec<generic::TopEntity>> {
let limit = 20;
top_table!(
colors::table
Expand All @@ -39,5 +43,4 @@ pub async fn top(auth: Auth, conn: DbConn) -> RestResult<Vec<generic::TopEntity>
limit,
conn
)
.await
}
4 changes: 2 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rocket::http::{uncased::Uncased, Header, Status};
use rocket::request::Request;
use rocket::response::{self, Responder};
use rocket::serde::json::Json;
use s3::S3Error;
use s3::error::S3Error;
use serde::Serialize;
use std::borrow::Cow;
use std::convert::From;
Expand Down Expand Up @@ -68,7 +68,7 @@ impl From<BcryptError> for VinotecaError {
fn from(bcrypt_error: BcryptError) -> Self {
let msg = "Error hashing password".to_owned();
match bcrypt_error {
BcryptError::InvalidPassword => VinotecaError::Forbidden("Bad password".to_owned()),
BcryptError::InvalidHash(_) => VinotecaError::Forbidden("Bad password".to_owned()),
e => {
error!("Error performing password hash: {:?}", e);
VinotecaError::Internal(msg)
Expand Down
122 changes: 58 additions & 64 deletions src/grapes.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,47 @@
use rocket::serde::json::Json;
use rocket_db_pools::diesel::{dsl::count_star, prelude::*};
use rocket_db_pools::Connection;
use validator::Validate;

use crate::error::{RestResult, VinotecaError};
use crate::models::{generic, Grape, GrapeForm, NewGrape};
use crate::query_utils::IntoFirst;
use crate::schema::{grapes, purchases, wine_grapes, wines};
use crate::users::Auth;
use crate::DbConn;

use diesel::dsl::sql;
use diesel::prelude::*;
use diesel::sql_types::BigInt;
use rocket::serde::json::Json;
use validator::Validate;
use crate::Db;

#[get("/grapes?<id>&<name>")]
pub async fn get(
auth: Auth,
id: Option<i32>,
name: Option<String>,
conn: DbConn,
mut conn: Connection<Db>,
) -> RestResult<Vec<Grape>> {
conn.run(move |c| {
let mut query = grapes::table
// Left to include grapes with no wine
.left_join(wine_grapes::table.inner_join(wines::table))
.filter(grapes::user_id.eq(auth.id))
.group_by((grapes::id, grapes::name))
.into_boxed();
if let Some(id) = id {
query = query.filter(grapes::id.eq(id));
}
if let Some(name) = name {
query = query.filter(grapes::name.eq(name));
}
query
.select((grapes::id, grapes::name, sql::<BigInt>("count(wines.id)")))
.load::<Grape>(c)
.map(Json)
.map_err(VinotecaError::from)
})
.await
let mut query = grapes::table
// Left to include grapes with no wine
.left_outer_join(wine_grapes::table.inner_join(wines::table))
.filter(grapes::user_id.eq(auth.id))
.group_by((grapes::id, grapes::name))
.select((grapes::id, grapes::name, count_star()))
.into_boxed();
if let Some(id) = id {
query = query.filter(grapes::id.eq(id));
}
if let Some(name) = name {
query = query.filter(grapes::name.eq(name));
}
query
.load::<Grape>(&mut **conn)
.await
.map(Json)
.map_err(VinotecaError::from)
}

#[get("/grapes/top?<limit>")]
pub async fn top(
auth: Auth,
limit: Option<usize>,
conn: DbConn,
mut conn: Connection<Db>,
) -> RestResult<Vec<generic::TopEntity>> {
let limit = limit.unwrap_or(10);
top_table!(
Expand All @@ -56,23 +53,23 @@ pub async fn top(
limit,
conn
)
.await
}

#[post("/grapes", format = "json", data = "<grape_form>")]
pub async fn post(auth: Auth, grape_form: Json<GrapeForm>, conn: DbConn) -> RestResult<Grape> {
pub async fn post(
auth: Auth,
grape_form: Json<GrapeForm>,
mut conn: Connection<Db>,
) -> RestResult<Grape> {
let grape_form = grape_form.into_inner();
grape_form.validate()?;

let grape_id = conn
.run(move |c| {
diesel::insert_into(grapes::table)
.values(NewGrape::from((auth, grape_form)))
.returning(grapes::id)
.get_result(c)
.map_err(VinotecaError::from)
})
.await?;
let grape_id = diesel::insert_into(grapes::table)
.values(NewGrape::from((auth, grape_form)))
.returning(grapes::id)
.get_result(&mut **conn)
.await
.map_err(VinotecaError::from)?;

get(auth, Some(grape_id), None, conn)
.await?
Expand All @@ -84,41 +81,38 @@ pub async fn put<'r>(
auth: Auth,
id: i32,
grape_form: Json<GrapeForm>,
conn: DbConn,
mut conn: Connection<Db>,
) -> RestResult<Grape> {
let grape_form = grape_form.into_inner();
grape_form.validate()?;

conn.run(move |c| {
grapes::table
.filter(grapes::id.eq(id))
.filter(grapes::user_id.eq(auth.id))
.select(grapes::id)
.first::<i32>(c)?;
grapes::table
.filter(grapes::id.eq(id))
.filter(grapes::user_id.eq(auth.id))
.select(grapes::id)
.first::<i32>(&mut **conn)
.await?;

diesel::update(grapes::table.filter(grapes::id.eq(id)))
.set(grapes::name.eq(grape_form.name))
.execute(c)
.map_err(VinotecaError::from)
})
.await?;
diesel::update(grapes::table.filter(grapes::id.eq(id)))
.set(grapes::name.eq(grape_form.name))
.execute(&mut **conn)
.await
.map_err(VinotecaError::from)?;

get(auth, Some(id), None, conn)
.await?
.into_first("Edited grape")
}

#[delete("/grapes/<id>")]
pub async fn delete(auth: Auth, id: i32, conn: DbConn) -> RestResult<()> {
conn.run(move |c| {
diesel::delete(
grapes::table
.filter(grapes::id.eq(id))
.filter(grapes::user_id.eq(auth.id)),
)
.execute(c)
.map(|_| Json(()))
.map_err(VinotecaError::from)
})
pub async fn delete(auth: Auth, id: i32, mut conn: Connection<Db>) -> RestResult<()> {
diesel::delete(
grapes::table
.filter(grapes::id.eq(id))
.filter(grapes::user_id.eq(auth.id)),
)
.execute(&mut **conn)
.await
.map(|_| Json(()))
.map_err(VinotecaError::from)
}
Loading

0 comments on commit 34124b3

Please sign in to comment.