Turn static data within the database into an enum

This commit is contained in:
Z. Charles Dziura 2025-03-03 17:32:40 -05:00
parent 066b912e42
commit 3a93b7e12f
22 changed files with 546 additions and 104 deletions

View file

@ -1,7 +1,7 @@
[package]
name = "debt-pirate"
version = "0.1.0"
edition = "2021"
edition = "2024"
[dependencies]
argon2 = "0.5"
@ -11,8 +11,8 @@ axum = { version = "0.8", features = [
"ws",
] }
base64 = "0.22"
bb8-redis = "0.20"
blake3 = { version = "1.5", features = ["serde"] }
deadpool-redis = { version = "0.20", features = ["serde"] }
futures = "0.3"
http = "1.0"
humantime = "2.1"
@ -28,7 +28,7 @@ lettre = { version = "0.11", default-features = false, features = [
] }
num_cpus = "1.16"
pasetors = "0.7"
redis = { version = "0.28", features = ["aio"] }
rust_decimal = "1.36"
serde = { version = "1.0", features = ["derive", "rc", "std"] }
serde_json = "1.0"
serde_with = "3.9"
@ -37,6 +37,7 @@ sqlx = { version = "0.8", features = [
"time",
"postgres",
"runtime-tokio",
"rust_decimal",
] }
syslog-tracing = "0.3.1"
time = { version = "0.3", features = ["formatting", "macros"] }

View file

@ -1,12 +1,17 @@
DROP INDEX IF EXISTS status_name_uniq_idx;
DROP INDEX IF EXISTS user_username_uniq_idx;
DROP INDEX IF EXISTS user_email_uniq_idx;
DROP INDEX IF EXISTS permission_name_uniq_idx;
DROP INDEX IF EXISTS account_type_name_uniq_idx;
DROP INDEX IF EXISTS permission_category_idx;
DROP INDEX IF EXISTS permission_category_name_idx;
DROP TABLE IF EXISTS public.user_account_permission;
DROP TABLE IF EXISTS public.account;
DROP TABLE IF EXISTS public.permission;
DROP TABLE IF EXISTS public.user;
DROP TABLE IF EXISTS public.transaction_line_item;
DROP TABLE IF EXISTS public.transaction CASCADE;
DROP TABLE IF EXISTS public.budget CASCADE;
DROP TABLE IF EXISTS public.status CASCADE;
DROP TABLE IF EXISTS public.account_type CASCADE;
DROP TYPE public.entry_type;
DROP TYPE public.account_type;
DROP TYPE public.permission_category;
DROP TYPE public.status;

View file

@ -1,51 +1,29 @@
CREATE TABLE IF NOT EXISTS
public.status (
CREATE TYPE
public.status
AS ENUM
('Active', 'Unverified', 'Removed', 'Quarantined');
CREATE TYPE
public.account_type
AS ENUM
('Asset', 'Equity', 'Expense', 'Liability', 'Revenue');
CREATE TYPE
public.entry_type
AS ENUM
('Debit', 'Credit');
CREATE TYPE
public.permission_category
AS ENUM
('Account');
CREATE TABLE IF NOT EXISTS public.user (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS status_name_uniq_idx ON public.status(name);
INSERT INTO
public.status (
name
)
VALUES
('Active'),
('Unverified'),
('Removed'),
('Quarantined');
CREATE TABLE IF NOT EXISTS
public.account_type (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS account_type_name_uniq_idx ON public.account_type(name);
INSERT INTO
public.account_type (
name
)
VALUES
('Asset'),
('Equity'),
('Expense'),
('Liability'),
('Revenue');
CREATE TABLE IF NOT EXISTS
public.user (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email TEXT NOT NULL,
password TEXT NOT NULL,
name TEXT NOT NULL,
status_id INT NOT NULL REFERENCES status(id) DEFAULT 2,
status status NOT NULL DEFAULT 'Unverified',
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NULL
);
@ -55,33 +33,81 @@ CREATE UNIQUE INDEX IF NOT EXISTS user_email_uniq_idx ON public.user(email);
CREATE TABLE IF NOT EXISTS
public.permission (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
category permission_category NOT NULL,
name TEXT NOT NULL,
status_id INT NOT NULL REFERENCES status(id) DEFAULT 1,
value INT NOT NULL,
status status NOT NULL DEFAULT 'Active',
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS permission_name_uniq_idx ON public.permission(name);
CREATE INDEX IF NOT EXISTS permission_category_idx ON public.permission(category);
CREATE INDEX IF NOT EXISTS permission_category_name_idx ON public.permission(category, name);
INSERT INTO
public.permission (
category,
name,
value
)
VALUES
('Account', 'View', 1),
('Account', 'Edit', 2),
('Account', 'Delete', 4),
('Account', 'Grant', 8);
CREATE TABLE IF NOT EXISTS
public.account (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
account_type account_type NOT NULL,
name TEXT NOT NULL,
description TEXT NULL,
currency_code TEXT NOT NULL,
status status NOT NULL DEFAULT 'Active',
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NULL
);
CREATE TABLE IF NOT EXISTS
public.user_account_permission (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
user_id INT NOT NULL REFERENCES public.user(id),
account_id INT NOT NULL REFERENCES public.account(id),
permission_id INT NOT NULL REFERENCES public.permission(id),
status status NOT NULL DEFAULT 'Active',
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NULL
);
CREATE TABLE IF NOT EXISTS
public.budget (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name TEXT NOT NULL,
description TEXT NULL,
status_id INT NOT NULL REFERENCES status(id) DEFAULT 1,
icon TEXT NULL,
status status NOT NULL DEFAULT 'Active',
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NULL
);
CREATE TABLE IF NOT EXISTS
public.account (
public.transaction (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
account_type_id INT NOT NULL REFERENCES account_type(id),
budget_id INT NOT NULL REFERENCES budget(id),
name TEXT NOT NULL,
description TEXT NULL,
currency_code TEXT NOT NULL,
status_id INT NOT NULL REFERENCES status(id) DEFAULT 1,
description TEXT NOT NULL,
budget_id INT NOT NULL REFERENCES public.budget(id),
status status NOT NULL DEFAULT 'Active',
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NULL
);
CREATE TABLE IF NOT EXISTS
public.transaction_line_item (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
transaction_id INT NOT NULL REFERENCES public.transaction(id),
account_id INT NOT NULL REFERENCES public.account(id),
entry_type entry_type NOT NULL,
value NUMERIC(12, 2) NOT NULL,
status status NOT NULL DEFAULT 'Active',
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NULL
);

55
api/src/db/account.rs Normal file
View file

@ -0,0 +1,55 @@
use sqlx::prelude::*;
use tracing::error;
use crate::models::AppError;
use super::{
associate_account_with_user_as_owner, AccountType, DbPool, PermissionEntity, StatusType,
};
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct NewAccountEntity {
pub name: String,
pub description: Option<String>,
pub account_type: AccountType,
pub currency_code: String,
}
#[allow(dead_code)]
#[derive(Debug, FromRow)]
pub struct AccountEntity {
pub id: i32,
pub account_type: AccountType,
pub name: String,
pub description: Option<String>,
pub currency_code: String,
pub status: StatusType,
}
pub async fn insert_new_account_for_user(
pool: &DbPool,
new_account: NewAccountEntity,
user_id: i32,
) -> Result<(AccountEntity, Vec<PermissionEntity>), AppError> {
let NewAccountEntity {
name,
description,
account_type,
currency_code,
} = &new_account;
let new_account = sqlx::query_as::<_, AccountEntity>("INSERT INTO public.account (name, description, account_type, currency_code) VALUES ($1, $2, $3, $4) RETURNING id, account_type, name, description, currency_code, status;")
.bind(name)
.bind(description)
.bind(account_type)
.bind(currency_code)
.fetch_one(pool).await
.inspect_err(|err| error!(?err, record = ?new_account, "Cannot insert new account record"))
.map_err(AppError::from)?;
let account_permissions =
associate_account_with_user_as_owner(pool, new_account.id, user_id).await?;
Ok((new_account, account_permissions))
}

View file

@ -0,0 +1,13 @@
use serde::{Deserialize, Serialize};
#[derive(
Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize, sqlx::Type,
)]
#[sqlx(type_name = "account_type", rename_all = "PascalCase")]
pub enum AccountType {
Asset,
Equity,
Expense,
Liability,
Revenue,
}

View file

@ -1,7 +1,18 @@
mod account;
mod account_type;
mod permission;
mod status;
mod user;
mod user_account_permission;
pub use account::*;
pub use account_type::*;
pub use permission::*;
pub use status::*;
pub use user::*;
pub use user_account_permission::*;
use sqlx::{self, postgres::PgPoolOptions, Pool, Postgres};
pub use user::*;
use crate::models::AppError;

91
api/src/db/permission.rs Normal file
View file

@ -0,0 +1,91 @@
use serde::{Deserialize, Serialize};
use sqlx::prelude::*;
use tracing::error;
use crate::models::AppError;
use super::DbPool;
#[derive(
Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize, sqlx::Type,
)]
#[sqlx(type_name = "permission_category", rename_all = "PascalCase")]
pub enum PermissionCategoryType {
Account,
}
#[allow(dead_code)]
#[derive(Debug, FromRow)]
pub struct PermissionEntity {
pub id: i32,
pub category: PermissionCategoryType,
pub name: String,
pub value: i32,
}
pub async fn get_all_permissions_by_category(
pool: &DbPool,
category: PermissionCategoryType,
) -> Result<Vec<PermissionEntity>, AppError> {
sqlx::query_as::<_, PermissionEntity>(
"SELECT id, category, name value FROM public.permission WHERE category = $1;",
)
.bind(category)
.fetch_all(pool)
.await
.inspect_err(|err| {
error!(
?err,
?category,
"Unable to fetch permissions associated with category"
)
})
.map_err(From::from)
}
pub async fn _get_permission_by_id(pool: &DbPool, id: i32) -> Result<PermissionEntity, AppError> {
sqlx::query_as::<_, PermissionEntity>(
"SELECT id, category, name, value FROM public.permission WHERE id = $1;",
)
.bind(id)
.fetch_one(pool)
.await
.inspect_err(|err| error!(?err, ?id, "Unable to fetch permission"))
.map_err(From::from)
}
pub async fn get_many_permissions_by_id(
pool: &DbPool,
ids: &[i32],
) -> Result<Vec<PermissionEntity>, AppError> {
let ids = ids
.iter()
.map(|id| id.to_string())
.collect::<Vec<_>>()
.join(",");
sqlx::query_as::<_, PermissionEntity>(
"SELECT id, category, name, value FROM public.permission WHERE id IN ($1);",
)
.bind(ids.as_str())
.fetch_all(pool)
.await
.inspect_err(|err| error!(?err, ?ids, "Unable to fetch permissions"))
.map_err(From::from)
}
pub async fn _get_permission_by_category_and_name(
pool: &DbPool,
category: &str,
name: &str,
) -> Result<PermissionEntity, AppError> {
sqlx::query_as::<_, PermissionEntity>(
"SELECT id, category, name, value FROM public.permission WHERE category = $1 AND name = $2;"
)
.bind(category)
.bind(name)
.fetch_one(pool)
.await
.inspect_err(|err| error!(?err, ?category, ?name, "Unable to fetch permission"))
.map_err(From::from)
}

12
api/src/db/status.rs Normal file
View file

@ -0,0 +1,12 @@
use serde::{Deserialize, Serialize};
#[derive(
Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize, sqlx::Type,
)]
#[sqlx(type_name = "status", rename_all = "PascalCase")]
pub enum StatusType {
Active,
Unverified,
Removed,
Quarantined,
}

View file

@ -30,7 +30,6 @@ pub struct UserEntity {
pub id: i32,
pub name: String,
pub email: String,
pub status_id: i32,
}
pub async fn insert_new_user(
@ -43,16 +42,13 @@ pub async fn insert_new_user(
name,
} = new_user.clone();
sqlx::query_as::<_, UserEntity>("INSERT INTO public.user (email, password, name) VALUES ($1, $2, $3) RETURNING id, email, name, status_id;")
sqlx::query_as::<_, UserEntity>("INSERT INTO public.user (email, password, name) VALUES ($1, $2, $3) RETURNING id, email, name;")
.bind(email)
.bind(password)
.bind(name)
.fetch_one(pool).await
.map_err(|err| {
error!(?err, record = ?new_user, "Cannot insert new user record");
AppError::from(err)
})
.inspect_err(|err| error!(?err, record = ?new_user, "Cannot insert new user record"))
.map_err(From::from)
}
#[derive(Debug, FromRow)]
@ -71,20 +67,16 @@ pub async fn get_username_and_password_by_email(
.bind(email)
.fetch_one(pool)
.await
.map_err(|err| {
error!(?err, "Unable to find user");
AppError::from(err)
})
.inspect_err(|err| error!(?err, "Unable to find user"))
.map_err(From::from)
}
pub async fn verify_user(pool: &DbPool, user_id: i32) -> Result<(), AppError> {
sqlx::query("UPDATE public.user SET status_id = 1, updated_at = now() WHERE id = $1;")
sqlx::query("UPDATE public.user SET status = 'Active', updated_at = now() WHERE id = $1;")
.bind(user_id)
.execute(pool)
.await
.map_err(|err| {
error!(?err, user_id, "Error verifying user");
AppError::from(err)
})
.inspect_err(|err| error!(?err, user_id, "Error verifying user"))
.map_err(From::from)
.map(|_| ())
}

View file

@ -0,0 +1,61 @@
use sqlx::prelude::*;
use tracing::error;
use crate::models::AppError;
use super::{
get_all_permissions_by_category, get_many_permissions_by_id, DbPool, PermissionCategoryType,
PermissionEntity, StatusType,
};
#[allow(dead_code)]
#[derive(Clone)]
pub struct NewUserAccountPermissionEntity {
pub user_id: i32,
pub account_id: i32,
pub permission_id: i32,
}
#[allow(dead_code)]
#[derive(Debug, FromRow)]
pub struct UserAccountPermissionEntity {
pub id: i32,
pub user_id: i32,
pub account_id: i32,
pub permission_id: i32,
pub status: StatusType,
}
pub async fn associate_account_with_user_as_owner(
pool: &DbPool,
account_id: i32,
user_id: i32,
) -> Result<Vec<PermissionEntity>, AppError> {
let values = get_all_permissions_by_category(pool, PermissionCategoryType::Account)
.await?
.into_iter()
.map(|permission| format!("({user_id}, {account_id}, {})", permission.id))
.collect::<Vec<_>>()
.join(",");
let query = format!("INSERT INTO public.user_account_relation (user_id, account_id, permission_id) VALUES {values} RETURNING (id, user_id, account_id, permission_id, status);");
let permission_ids = sqlx::query_as::<_, UserAccountPermissionEntity>(query.as_str())
.fetch_all(pool)
.await
.inspect_err(|err| {
error!(
?err,
account_id, user_id, "Cannot associate account with user as owner"
)
})
.map_err(|err| AppError::from(err))
.map(|relations| {
relations
.into_iter()
.map(|relation| relation.permission_id)
.collect::<Vec<_>>()
})?;
get_many_permissions_by_id(pool, permission_ids.as_slice()).await
}

View file

@ -8,7 +8,7 @@ mod services;
use db::{create_db_connection_pool, run_migrations};
use requests::start_app;
use services::{
create_cache_connection_pool, initialize_logger, start_emailer_service, UserConfirmationMessage,
UserConfirmationMessage, create_cache_connection_pool, initialize_logger, start_emailer_service,
};
use tokio::runtime::Handle;
use tracing::{error, info};
@ -38,7 +38,6 @@ async fn main() {
info!("Initializing cache service connection pool...");
let cache_pool = create_cache_connection_pool(config.cache_connection_uri().to_string())
.await
.inspect_err(|err| error!(?err))
.unwrap();
info!("Cache service connection pool created successfully.");

View file

@ -6,10 +6,9 @@ use std::{
};
use axum::response::IntoResponse;
use bb8_redis::bb8::RunError;
use deadpool_redis::{PoolError, redis::RedisError};
use http::StatusCode;
use redis::RedisError;
use sqlx::{error::DatabaseError, migrate::MigrateError, Error as SqlxError};
use sqlx::{Error as SqlxError, error::DatabaseError, migrate::MigrateError};
use toml::{self, de::Error as TomlError};
use tracing::trace;
@ -105,8 +104,8 @@ impl From<RedisError> for AppError {
}
}
impl From<RunError<RedisError>> for AppError {
fn from(other: RunError<RedisError>) -> Self {
impl From<PoolError> for AppError {
fn from(other: PoolError) -> Self {
trace!(err = ?other, "Cache pool error");
Self::new(ErrorKind::Cache(other.to_string()))
}

View file

@ -1,9 +1,9 @@
use std::time::SystemTime;
use axum::extract::FromRequestParts;
use deadpool_redis::redis::ToRedisArgs;
use http::request::Parts;
use humantime::format_rfc3339;
use redis::ToRedisArgs;
use uuid::Uuid;
use crate::{

View file

@ -0,0 +1,53 @@
use axum::{
debug_handler,
extract::State,
response::{IntoResponse, Response},
Json,
};
use http::StatusCode;
use crate::{
db::{insert_new_account_for_user, DbPool, NewAccountEntity},
models::{AppError, Session},
requests::AppState,
};
use super::models::{AccountCreationRequest, AccountCreationResponse};
#[debug_handler]
pub async fn account_creation_post_handler(
State(state): State<AppState>,
session: Session,
Json(request): Json<AccountCreationRequest>,
) -> Result<Response, AppError> {
let pool = state.db_pool();
let user_id = session.user_id;
account_creation_request(request, user_id, pool).await
}
async fn account_creation_request(
request: AccountCreationRequest,
user_id: i32,
pool: &DbPool,
) -> Result<Response, AppError> {
let AccountCreationRequest {
r#type: account_type,
name,
description,
currency_code,
} = request;
let new_account = NewAccountEntity {
name,
description,
account_type: account_type.into(),
currency_code,
};
let response = insert_new_account_for_user(pool, new_account, user_id)
.await
.map(|response| AccountCreationResponse::from(response))?;
Ok((StatusCode::CREATED, Json(response)).into_response())
}

View file

@ -0,0 +1,4 @@
mod handler;
mod models;
pub use handler::*;

View file

@ -0,0 +1,42 @@
mod request;
mod response;
pub use request::*;
pub use response::*;
use serde::{Deserialize, Serialize};
use crate::db::AccountType as DbAccountType;
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum AccountType {
Asset,
Equity,
Expense,
Liability,
Revenue,
}
impl From<DbAccountType> for AccountType {
fn from(other: DbAccountType) -> Self {
match other {
DbAccountType::Asset => Self::Asset,
DbAccountType::Equity => Self::Equity,
DbAccountType::Expense => Self::Expense,
DbAccountType::Liability => Self::Liability,
DbAccountType::Revenue => Self::Revenue,
}
}
}
impl Into<DbAccountType> for AccountType {
fn into(self) -> DbAccountType {
match self {
Self::Asset => DbAccountType::Asset,
Self::Equity => DbAccountType::Equity,
Self::Expense => DbAccountType::Expense,
Self::Liability => DbAccountType::Liability,
Self::Revenue => DbAccountType::Revenue,
}
}
}

View file

@ -0,0 +1,11 @@
use serde::Deserialize;
use crate::db::AccountType;
#[derive(Debug, Deserialize)]
pub struct AccountCreationRequest {
pub r#type: AccountType,
pub name: String,
pub description: Option<String>,
pub currency_code: String,
}

View file

@ -0,0 +1,44 @@
use serde::Serialize;
use crate::db::{AccountEntity, PermissionEntity};
use super::AccountType;
#[derive(Debug, Serialize)]
pub struct AccountCreationResponse {
pub id: i32,
pub r#type: AccountType,
pub name: String,
pub description: Option<String>,
pub currency_code: String,
pub permissions: i32,
}
impl From<(AccountEntity, Vec<PermissionEntity>)> for AccountCreationResponse {
fn from(other: (AccountEntity, Vec<PermissionEntity>)) -> Self {
let (account_entity, permissions) = other;
let AccountEntity {
id,
account_type,
name,
description,
currency_code,
..
} = account_entity;
let permissions = permissions
.into_iter()
.map(|permission| permission.value)
.sum();
Self {
id,
r#type: AccountType::from(account_type),
name,
description,
currency_code,
permissions,
}
}
}

View file

@ -0,0 +1,15 @@
mod create;
use axum::{routing::post, Router};
use create::account_creation_post_handler;
use super::AppState;
pub fn requests(state: AppState) -> Router {
Router::new().nest(
"/account",
Router::new()
.route("/", post(account_creation_post_handler))
.with_state(state),
)
}

View file

@ -1,3 +1,4 @@
mod account;
mod auth;
mod user;
@ -94,6 +95,7 @@ pub async fn start_app(
let app = Router::new()
.merge(user::requests(state.clone()))
.merge(auth::requests(state.clone()))
.merge(account::requests(state.clone()))
.layer(logging_layer);
info!("API started successfully.");

View file

@ -1,23 +1,27 @@
use std::{collections::HashMap, fmt::Debug, hash::Hash, time::Duration};
use std::{collections::HashMap, hash::Hash, time::Duration};
use bb8_redis::{
bb8::{Pool, PooledConnection},
RedisConnectionManager,
use deadpool_redis::{
Config, Connection, Pool,
redis::{AsyncCommands, FromRedisValue, IntoConnectionInfo, ToRedisArgs, Value},
};
use redis::{AsyncCommands, FromRedisValue, IntoConnectionInfo, ToRedisArgs, Value};
use tracing::error;
use crate::models::AppError;
pub type CachePool = Pool<RedisConnectionManager>;
pub type CachePool = Pool;
pub async fn create_cache_connection_pool(
connection_info: impl IntoConnectionInfo + Clone + Debug,
pub fn create_cache_connection_pool(
connection_info: impl IntoConnectionInfo,
) -> Result<CachePool, AppError> {
let manager = RedisConnectionManager::new(connection_info)
.map_err(|_| AppError::connection_info("cache"))?;
let pool = Pool::builder().build(manager).await.unwrap();
let config = Config::from_connection_info(
connection_info
.into_connection_info()
.map_err(|_| AppError::connection_info("cache"))?,
);
Ok(pool)
config
.create_pool(Some(deadpool_redis::Runtime::Tokio1))
.map_err(|_| AppError::connection_info("cache"))
}
pub async fn store_object<
@ -90,8 +94,10 @@ pub async fn exists(cache_pool: &CachePool, key: &str) -> Result<bool, AppError>
conn.exists(key).await.map_err(Into::into)
}
async fn get_connection_from_pool(
cache_pool: &CachePool,
) -> Result<PooledConnection<'_, RedisConnectionManager>, AppError> {
cache_pool.get().await.map_err(From::from)
async fn get_connection_from_pool(cache_pool: &CachePool) -> Result<Connection, AppError> {
cache_pool
.get()
.await
.inspect_err(|err| error!(?err))
.map_err(From::from)
}

View file

@ -1,7 +1,7 @@
use std::time::{Duration, SystemTime};
use deadpool_redis::redis::FromRedisValue;
use humantime::parse_rfc3339;
use redis::FromRedisValue;
use uuid::Uuid;
use crate::{