Moved more code from hotel_service to lib
Some checks failed
Build & test / build (push) Failing after 6s

This commit is contained in:
Martin Berg Alstad
2024-09-08 17:27:20 +02:00
parent 7e2df67fee
commit 80f4af9087
18 changed files with 254 additions and 15 deletions

View File

@ -152,11 +152,13 @@ impl AppBuilder {
let _ = fmt_trace(); // Allowed to fail
let listener = self.listener().await?;
if self.normalize_path.unwrap_or(true) {
let app = NormalizePathLayer::trim_trailing_slash().layer(self.build());
let should_normalize = self.normalize_path.unwrap_or(true);
let app = self.build();
if should_normalize {
let app = NormalizePathLayer::trim_trailing_slash().layer(app);
axum::serve(listener, ServiceExt::<Request>::into_make_service(app)).await?;
} else {
let app = self.build();
axum::serve(listener, app.into_make_service()).await?;
};
Ok(())

14
src/axum/builder.rs Normal file
View File

@ -0,0 +1,14 @@
use crate::axum::traits::BuildJson;
use axum::body::Body;
use axum::http::header::CONTENT_TYPE;
use axum::http::Request;
use mime::APPLICATION_JSON;
use serde::Serialize;
use serde_json::json;
impl BuildJson for axum::http::request::Builder {
fn json<T: Serialize>(self, body: T) -> Result<Request<Body>, axum::http::Error> {
self.header(CONTENT_TYPE, APPLICATION_JSON.as_ref())
.body(Body::new(json!(body).to_string()))
}
}

View File

@ -1,8 +1,11 @@
pub mod app;
#[cfg(feature = "serde")]
pub mod builder;
pub mod extractor;
pub mod load;
#[cfg(feature = "serde")]
pub mod response;
pub mod router;
pub mod traits;
#[cfg(feature = "serde")]
pub mod wrappers;

View File

@ -1,10 +1,15 @@
use {
crate::serde::response::BaseResponse,
crate::{serde::response::BaseResponse, serde::traits::DeserializeInto},
async_trait::async_trait,
axum::{
body::to_bytes,
response::{IntoResponse, Response},
Json,
},
serde::Serialize,
serde::{
de::{DeserializeOwned, Error},
Serialize,
},
};
impl<T: Serialize> IntoResponse for BaseResponse<T> {
@ -13,6 +18,16 @@ impl<T: Serialize> IntoResponse for BaseResponse<T> {
}
}
#[async_trait]
impl DeserializeInto for Response {
async fn deserialize_into<T: DeserializeOwned>(self) -> Result<T, serde_json::Error> {
let body = to_bytes(self.into_body(), usize::MAX).await.map_err(|e| {
serde_json::Error::custom(format!("Failed to read response body: {}", e))
})?;
serde_json::from_slice(&body)
}
}
#[cfg(test)]
mod tests {
use axum::http::header::CONTENT_TYPE;

7
src/axum/traits.rs Normal file
View File

@ -0,0 +1,7 @@
use axum::body::Body;
use axum::http::Request;
use serde::Serialize;
pub trait BuildJson {
fn json<T: Serialize>(self, body: T) -> Result<Request<Body>, axum::http::Error>;
}

View File

@ -0,0 +1,29 @@
use axum::async_trait;
use deadpool_diesel::Status;
use derive_more::From;
use diesel_async::pooled_connection::deadpool::{Object, PoolError};
use diesel_async::AsyncPgConnection;
use lib::diesel::pool::PgPool;
#[async_trait]
pub trait GetConnection: Clone + Send + Sync {
async fn get(&self) -> Result<Object<AsyncPgConnection>, GetConnectionError>;
fn status(&self) -> Status;
}
#[async_trait]
impl GetConnection for PgPool {
async fn get(&self) -> Result<Object<AsyncPgConnection>, GetConnectionError> {
self.get().await.map_err(Into::into)
}
#[inline]
fn status(&self) -> Status {
self.status()
}
}
#[derive(Debug, From)]
pub enum GetConnectionError {
PoolError(PoolError),
DieselError(diesel::result::Error),
}

9
src/diesel/migration.rs Normal file
View File

@ -0,0 +1,9 @@
use diesel_async::AsyncPgConnection;
use diesel_async_migrations::EmbeddedMigrations;
pub async fn run_migrations(
migrations: &EmbeddedMigrations,
conn: &mut AsyncPgConnection,
) -> Result<(), diesel::result::Error> {
migrations.run_pending_migrations(conn).await
}

View File

@ -1,3 +1,5 @@
pub mod get_connection;
pub mod migration;
pub mod pool;
/// Re-export diesel::result::Error as DieselError

View File

@ -19,6 +19,8 @@ pub mod io;
pub mod nom;
#[cfg(feature = "serde")]
pub mod serde;
#[cfg(feature = "test")]
pub mod test;
#[cfg(feature = "time")]
pub mod time;
pub mod traits;

View File

@ -1 +1,2 @@
pub mod response;
pub mod traits;

7
src/serde/traits.rs Normal file
View File

@ -0,0 +1,7 @@
use async_trait::async_trait;
use serde::de::DeserializeOwned;
#[async_trait]
pub trait DeserializeInto {
async fn deserialize_into<T: DeserializeOwned>(self) -> Result<T, serde_json::Error>;
}

45
src/test/diesel_pool.rs Normal file
View File

@ -0,0 +1,45 @@
use crate::diesel::get_connection::{GetConnection, GetConnectionError};
use crate::diesel::pool::PgPool;
use crate::diesel::DieselError;
use axum::async_trait;
use deadpool_diesel::postgres::BuildError;
use deadpool_diesel::Status;
use derive_more::From;
use diesel_async::pooled_connection::deadpool::Object;
use diesel_async::{AsyncConnection, AsyncPgConnection};
use lib::diesel::pool::create_pool_from_url_with_size;
#[derive(Clone)]
pub struct PoolStub(PgPool);
#[derive(Debug, PartialEq, From)]
pub enum Error {
Connection(diesel::ConnectionError),
Database(DieselError),
}
pub async fn setup_test_transaction(url: impl AsRef<str>) -> Result<AsyncPgConnection, Error> {
let mut conn = AsyncPgConnection::establish(url.as_ref()).await?;
conn.begin_test_transaction().await?;
Ok(conn)
}
pub async fn create_test_pool_url_with_size(
url: impl Into<String>,
size: usize,
) -> Result<PoolStub, BuildError> {
let pool = create_pool_from_url_with_size(url, size)?;
Ok(PoolStub(pool))
}
#[async_trait]
impl GetConnection for PoolStub {
async fn get(&self) -> Result<Object<AsyncPgConnection>, GetConnectionError> {
let mut conn = self.0.get().await?;
conn.begin_test_transaction().await?;
Ok(conn)
}
fn status(&self) -> Status {
unimplemented!("PoolStub does not support status")
}
}

3
src/test/mod.rs Normal file
View File

@ -0,0 +1,3 @@
#[cfg(feature = "diesel")]
pub mod diesel_pool;
pub mod test_containers;

View File

@ -0,0 +1,39 @@
use crate::diesel::pool::{create_pool_from_url, PgPool};
use deadpool_diesel::postgres::BuildError;
use derive_more::{Constructor, From};
use diesel_async::pooled_connection::deadpool::PoolError;
use lib::diesel::DieselError;
use testcontainers_modules::postgres::Postgres;
use testcontainers_modules::testcontainers::runners::AsyncRunner;
use testcontainers_modules::testcontainers::{ContainerAsync, TestcontainersError};
/// When the TestContainer is dropped, the container will be removed.
/// # Errors
/// If destructed and the container field is dropped, the container will be dropped, and using the pool will cause an error.
#[derive(Constructor)]
pub struct TestContainer {
pub container: ContainerAsync<Postgres>,
pub pool: PgPool,
}
pub async fn create_test_containers_pool<'a>() -> Result<TestContainer, ContainerError> {
let container = create_postgres_container().await?;
let connection_string = format!(
"postgres://postgres:postgres@127.0.0.1:{}/postgres",
container.get_host_port_ipv4(5432).await?
);
let pool = create_pool_from_url(connection_string)?;
Ok(TestContainer::new(container, pool))
}
pub async fn create_postgres_container() -> Result<ContainerAsync<Postgres>, TestcontainersError> {
Postgres::default().start().await
}
#[derive(Debug, From)]
pub enum ContainerError {
TestContainers(TestcontainersError),
BuildError(BuildError),
PoolError(PoolError),
DieselError(DieselError),
}

View File

@ -23,9 +23,8 @@
#[macro_export]
macro_rules! map {
() => { std::collections::HashMap::new() };
($default:ty; $($key:expr),* $(,)?) => {
($default:ty; $($key:expr),+ $(,)?) => {
{
#[allow(unused_mut)]
let mut temp_map = std::collections::HashMap::new();
$(
temp_map.insert($key, <$default>::default());
@ -76,8 +75,8 @@ mod tests {
}
#[test]
fn test_map_only_keys_0_keys() {
let map: HashMap<usize, usize> = map!(usize;);
assert_eq!(map.len(), 0);
fn test_map_only_keys_1_key() {
let map: HashMap<usize, usize> = map!(usize; 1);
assert_eq!(map.len(), 1);
}
}