- chore: ran cargo fix on the codebase
This commit is contained in:
@@ -3,6 +3,12 @@ fn main() {
|
||||
.build_server(true)
|
||||
.compile_well_known_types(true)
|
||||
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]")
|
||||
.compile_protos(&["../proto/user_db_api.proto", "../proto/character_db_api.proto"], &["../proto"])
|
||||
.compile_protos(
|
||||
&[
|
||||
"../proto/user_db_api.proto",
|
||||
"../proto/character_db_api.proto",
|
||||
],
|
||||
&["../proto"],
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{FromRow, Row};
|
||||
use utils::redis_cache::{Cache, RedisCache}; // Import RedisCache
|
||||
use serde::{Serialize, Deserialize};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use utils::redis_cache::{Cache, RedisCache}; // Import RedisCache
|
||||
|
||||
#[derive(Debug, FromRow, Serialize, Deserialize)]
|
||||
pub struct Character {
|
||||
@@ -34,7 +34,14 @@ impl CharacterRepository {
|
||||
let cache_key = format!("character:{}", character_id);
|
||||
|
||||
// Try fetching from Redis cache
|
||||
if let Some(character) = self.cache.lock().await.get::<Character>(&cache_key).await.map_err(|_| sqlx::Error::RowNotFound)? {
|
||||
if let Some(character) = self
|
||||
.cache
|
||||
.lock()
|
||||
.await
|
||||
.get::<Character>(&cache_key)
|
||||
.await
|
||||
.map_err(|_| sqlx::Error::RowNotFound)?
|
||||
{
|
||||
return Ok(character);
|
||||
}
|
||||
|
||||
@@ -49,11 +56,24 @@ impl CharacterRepository {
|
||||
.await?;
|
||||
|
||||
// Cache result
|
||||
self.cache.lock().await.set(&cache_key, &character, 300).await.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
self.cache
|
||||
.lock()
|
||||
.await
|
||||
.set(&cache_key, &character, 300)
|
||||
.await
|
||||
.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
Ok(character)
|
||||
}
|
||||
|
||||
pub async fn create_character(&self, user_id: i32, name: &str, inventory: serde_json::Value, stats: serde_json::Value, looks: serde_json::Value, position: serde_json::Value) -> Result<i32, sqlx::Error> {
|
||||
pub async fn create_character(
|
||||
&self,
|
||||
user_id: i32,
|
||||
name: &str,
|
||||
inventory: serde_json::Value,
|
||||
stats: serde_json::Value,
|
||||
looks: serde_json::Value,
|
||||
position: serde_json::Value,
|
||||
) -> Result<i32, sqlx::Error> {
|
||||
let result = sqlx::query(
|
||||
"INSERT INTO characters (user_id, name, inventory, stats, looks, position, created_at, updated_at, is_active) \
|
||||
VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW(), true) RETURNING id",
|
||||
@@ -69,35 +89,62 @@ impl CharacterRepository {
|
||||
|
||||
// Invalidate cache
|
||||
let cache_key = format!("character:user:{}", user_id);
|
||||
self.cache.lock().await.delete(&cache_key).await.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
self.cache
|
||||
.lock()
|
||||
.await
|
||||
.delete(&cache_key)
|
||||
.await
|
||||
.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
Ok(result.get("id"))
|
||||
}
|
||||
|
||||
pub async fn delete_character(&self, character_id: i32, delete_type: i32) -> Result<i64, sqlx::Error> {
|
||||
let mut query = "UPDATE characters SET updated_at = NOW(), deleted_at = NOW() + '24 hours' WHERE id = $1 RETURNING user_id, extract(epoch from (deleted_at - now()))::BIGINT as deleted_at";
|
||||
pub async fn delete_character(
|
||||
&self,
|
||||
character_id: i32,
|
||||
delete_type: i32,
|
||||
) -> Result<i64, sqlx::Error> {
|
||||
let mut query = "UPDATE characters SET updated_at = NOW(), deleted_at = NOW() + '24 hours' WHERE id = $1 RETURNING user_id, extract(epoch from (deleted_at - now()))::BIGINT as deleted_at";
|
||||
if 0 == delete_type {
|
||||
query = "UPDATE characters SET updated_at = NOW(), deleted_at = null WHERE id = $1 RETURNING user_id, 0::BIGINT as deleted_at";
|
||||
}
|
||||
let result = sqlx::query(
|
||||
query,
|
||||
)
|
||||
let result = sqlx::query(query)
|
||||
.bind(character_id)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
|
||||
// Invalidate cache
|
||||
let cache_key = format!("character:user:{}", result.get::<i32, &str>("user_id"));
|
||||
self.cache.lock().await.delete(&cache_key).await.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
self.cache
|
||||
.lock()
|
||||
.await
|
||||
.delete(&cache_key)
|
||||
.await
|
||||
.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
let cache_key = format!("character:{}", character_id);
|
||||
self.cache.lock().await.delete(&cache_key).await.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
self.cache
|
||||
.lock()
|
||||
.await
|
||||
.delete(&cache_key)
|
||||
.await
|
||||
.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
Ok(result.get::<i64, &str>("deleted_at"))
|
||||
}
|
||||
|
||||
pub async fn get_characters_by_user(&self, user_id: i32) -> Result<Vec<Character>, sqlx::Error> {
|
||||
pub async fn get_characters_by_user(
|
||||
&self,
|
||||
user_id: i32,
|
||||
) -> Result<Vec<Character>, sqlx::Error> {
|
||||
let cache_key = format!("character:user:{}", user_id);
|
||||
|
||||
// Try fetching from Redis cache
|
||||
if let Some(characters) = self.cache.lock().await.get::<Vec<Character>>(&cache_key).await.map_err(|_| sqlx::Error::RowNotFound)? {
|
||||
if let Some(characters) = self
|
||||
.cache
|
||||
.lock()
|
||||
.await
|
||||
.get::<Vec<Character>>(&cache_key)
|
||||
.await
|
||||
.map_err(|_| sqlx::Error::RowNotFound)?
|
||||
{
|
||||
return Ok(characters);
|
||||
}
|
||||
|
||||
@@ -110,7 +157,12 @@ impl CharacterRepository {
|
||||
.await?;
|
||||
|
||||
// Cache result
|
||||
self.cache.lock().await.set(&cache_key, &characters, 300).await.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
self.cache
|
||||
.lock()
|
||||
.await
|
||||
.set(&cache_key, &characters, 300)
|
||||
.await
|
||||
.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
Ok(characters)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::users::UserRepository;
|
||||
use crate::characters::CharacterRepository;
|
||||
use utils::redis_cache::RedisCache;
|
||||
use crate::users::UserRepository;
|
||||
use sqlx::PgPool;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use utils::redis_cache::RedisCache;
|
||||
|
||||
pub struct Database {
|
||||
pub user_repo: Arc<UserRepository>,
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use serde_json::Value::Null;
|
||||
use crate::grpc::{Character, CharacterRequest, CharacterListRequest, CharacterListResponse, CreateCharacterRequest, CreateCharacterResponse, DeleteCharacterRequest, DeleteCharacterResponse};
|
||||
use crate::grpc::character_db_service_server::CharacterDbService;
|
||||
use crate::grpc::database_service::MyDatabaseService;
|
||||
use crate::grpc::{
|
||||
Character, CharacterListRequest, CharacterListResponse, CharacterRequest,
|
||||
CreateCharacterRequest, CreateCharacterResponse, DeleteCharacterRequest,
|
||||
DeleteCharacterResponse,
|
||||
};
|
||||
use tonic::{Request, Response, Status};
|
||||
|
||||
#[tonic::async_trait]
|
||||
@@ -18,11 +21,11 @@ impl CharacterDbService for MyDatabaseService {
|
||||
.await
|
||||
.map_err(|_| Status::not_found("Character not found"))?;
|
||||
|
||||
let mut deleted_at= "".to_string();
|
||||
let mut deleted_at = "".to_string();
|
||||
if character.deleted_at.is_some() {
|
||||
deleted_at = character.deleted_at.unwrap().to_string();
|
||||
}
|
||||
|
||||
|
||||
let response = Character {
|
||||
id: character.id,
|
||||
user_id: character.user_id,
|
||||
@@ -52,12 +55,11 @@ impl CharacterDbService for MyDatabaseService {
|
||||
.get_characters_by_user(req.user_id)
|
||||
.await
|
||||
.map_err(|_| Status::not_found("Character not found"))?;
|
||||
|
||||
|
||||
let mut character_list: Vec<Character> = Vec::new();
|
||||
|
||||
|
||||
for character in characters {
|
||||
let mut deleted_at= "".to_string();
|
||||
let mut deleted_at = "".to_string();
|
||||
if character.deleted_at.is_some() {
|
||||
deleted_at = character.deleted_at.unwrap_or_default().to_string();
|
||||
}
|
||||
@@ -91,7 +93,7 @@ impl CharacterDbService for MyDatabaseService {
|
||||
) -> Result<Response<CreateCharacterResponse>, Status> {
|
||||
let req = request.into_inner();
|
||||
let repo = &self.db.character_repo;
|
||||
|
||||
|
||||
//todo: we need to check if the character name exists already
|
||||
|
||||
let character_id = repo
|
||||
@@ -126,7 +128,8 @@ impl CharacterDbService for MyDatabaseService {
|
||||
let req = request.into_inner();
|
||||
let repo = &self.db.character_repo;
|
||||
|
||||
let time_left_in_seconds = repo.delete_character(req.character_id, req.delete_type)
|
||||
let time_left_in_seconds = repo
|
||||
.delete_character(req.character_id, req.delete_type)
|
||||
.await
|
||||
.map_err(|_| Status::internal("Failed to delete character"))?;
|
||||
|
||||
|
||||
@@ -4,4 +4,4 @@ use std::sync::Arc;
|
||||
#[derive(Clone)]
|
||||
pub struct MyDatabaseService {
|
||||
pub db: Arc<Database>, // Use the Database struct from users.rs
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
mod character_service;
|
||||
pub mod database_service;
|
||||
pub mod user_service;
|
||||
mod character_service;
|
||||
|
||||
tonic::include_proto!("user_db_api");
|
||||
tonic::include_proto!("character_db_api");
|
||||
tonic::include_proto!("character_db_api");
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use crate::grpc::{CreateUserRequest, CreateUserResponse, GetUserByEmailRequest, GetUserByUsernameRequest, GetUserRequest, GetUserResponse};
|
||||
use crate::grpc::user_service_server::UserService;
|
||||
use crate::grpc::database_service::MyDatabaseService;
|
||||
use crate::grpc::user_service_server::UserService;
|
||||
use crate::grpc::{
|
||||
CreateUserRequest, CreateUserResponse, GetUserByEmailRequest, GetUserByUsernameRequest,
|
||||
GetUserRequest, GetUserResponse,
|
||||
};
|
||||
use tonic::{Request, Response, Status};
|
||||
|
||||
#[tonic::async_trait]
|
||||
@@ -11,7 +14,10 @@ impl UserService for MyDatabaseService {
|
||||
) -> Result<Response<GetUserResponse>, Status> {
|
||||
let req = request.into_inner();
|
||||
|
||||
let user = self.db.user_repo.get_user_by_id(req.user_id)
|
||||
let user = self
|
||||
.db
|
||||
.user_repo
|
||||
.get_user_by_id(req.user_id)
|
||||
.await
|
||||
.map_err(|_| Status::not_found("User not found"))?;
|
||||
|
||||
@@ -30,7 +36,10 @@ impl UserService for MyDatabaseService {
|
||||
) -> Result<Response<CreateUserResponse>, Status> {
|
||||
let req = request.into_inner();
|
||||
|
||||
let user_id = self.db.user_repo.create_user(&req.username, &req.email, &req.hashed_password)
|
||||
let user_id = self
|
||||
.db
|
||||
.user_repo
|
||||
.create_user(&req.username, &req.email, &req.hashed_password)
|
||||
.await
|
||||
.map_err(|_| Status::internal("Failed to create user"))?;
|
||||
|
||||
@@ -44,7 +53,10 @@ impl UserService for MyDatabaseService {
|
||||
) -> Result<Response<GetUserResponse>, Status> {
|
||||
let req = request.into_inner();
|
||||
|
||||
let user = self.db.user_repo.get_user_by_username(&req.username)
|
||||
let user = self
|
||||
.db
|
||||
.user_repo
|
||||
.get_user_by_username(&req.username)
|
||||
.await
|
||||
.map_err(|_| Status::not_found("User not found"))?;
|
||||
|
||||
@@ -63,7 +75,10 @@ impl UserService for MyDatabaseService {
|
||||
) -> Result<Response<GetUserResponse>, Status> {
|
||||
let req = request.into_inner();
|
||||
|
||||
let user = self.db.user_repo.get_user_by_email(&req.email)
|
||||
let user = self
|
||||
.db
|
||||
.user_repo
|
||||
.get_user_by_email(&req.email)
|
||||
.await
|
||||
.map_err(|_| Status::not_found("User not found"))?;
|
||||
|
||||
@@ -75,4 +90,4 @@ impl UserService for MyDatabaseService {
|
||||
roles: user.roles.unwrap_or_else(Vec::new),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub mod users;
|
||||
pub mod characters;
|
||||
pub mod db;
|
||||
pub mod grpc;
|
||||
pub mod grpc;
|
||||
pub mod users;
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
use std::collections::HashMap;
|
||||
use database_service::db::Database;
|
||||
use database_service::grpc::character_db_service_server::CharacterDbServiceServer;
|
||||
use database_service::grpc::database_service::MyDatabaseService;
|
||||
use database_service::grpc::user_service_server::UserServiceServer;
|
||||
use database_service::grpc::character_db_service_server::CharacterDbServiceServer;
|
||||
use utils::redis_cache::RedisCache;
|
||||
use dotenv::dotenv;
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use tokio::{select, signal};
|
||||
use tokio::sync::Mutex;
|
||||
use tonic::transport::Server;
|
||||
use tracing::{info, Level};
|
||||
use utils::consul_registration;
|
||||
use utils::redis_cache::RedisCache;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
dotenv().ok();
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(Level::from_str(&env::var("LOG_LEVEL").unwrap_or_else(|_| "info".to_string())).unwrap_or_else(|_| Level::INFO))
|
||||
.with_max_level(
|
||||
Level::from_str(&env::var("LOG_LEVEL").unwrap_or_else(|_| "info".to_string()))
|
||||
.unwrap_or_else(|_| Level::INFO),
|
||||
)
|
||||
.init();
|
||||
|
||||
let addr = env::var("LISTEN_ADDR").unwrap_or_else(|_| "0.0.0.0".to_string());
|
||||
@@ -27,14 +29,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let health_port = env::var("HEALTH_CHECK_PORT").unwrap_or_else(|_| "8080".to_string());
|
||||
|
||||
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||
let redis_url = std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
|
||||
let redis_url =
|
||||
std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
|
||||
|
||||
let consul_url = env::var("CONSUL_URL").unwrap_or_else(|_| "http://127.0.0.1:8500".to_string());
|
||||
let service_name = env::var("SERVICE_NAME").unwrap_or_else(|_| "database-service".to_string());
|
||||
let service_address = env::var("DATABASE_SERVICE_ADDR").unwrap_or_else(|_| "127.0.0.1".to_string());
|
||||
let service_address =
|
||||
env::var("DATABASE_SERVICE_ADDR").unwrap_or_else(|_| "127.0.0.1".to_string());
|
||||
let service_port = port.clone();
|
||||
let health_check_url = format!("http://{}:{}/health", service_address, health_port);
|
||||
|
||||
|
||||
// Register service with Consul
|
||||
let service_id = consul_registration::get_or_generate_service_id(env!("CARGO_PKG_NAME"));
|
||||
let version = env!("CARGO_PKG_VERSION").to_string();
|
||||
@@ -50,7 +54,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
meta,
|
||||
&health_check_url,
|
||||
)
|
||||
.await?;
|
||||
.await?;
|
||||
|
||||
consul_registration::start_health_check(addr.as_str()).await?;
|
||||
|
||||
@@ -62,20 +66,23 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.await
|
||||
.expect("Failed to create PostgreSQL connection pool");
|
||||
|
||||
|
||||
let redis_cache = Arc::new(Mutex::new(RedisCache::new(&redis_url)));
|
||||
let db = Arc::new(Database::new(pool, redis_cache));
|
||||
let my_service = MyDatabaseService { db };
|
||||
|
||||
// Pass `shared_cache` into services as needed
|
||||
info!("Database Service running on {}", address);
|
||||
tokio::spawn(Server::builder()
|
||||
.add_service(UserServiceServer::new(my_service.clone()))
|
||||
.add_service(CharacterDbServiceServer::new(my_service))
|
||||
.serve(address));
|
||||
tokio::spawn(
|
||||
Server::builder()
|
||||
.add_service(UserServiceServer::new(my_service.clone()))
|
||||
.add_service(CharacterDbServiceServer::new(my_service))
|
||||
.serve(address),
|
||||
);
|
||||
|
||||
utils::signal_handler::wait_for_signal().await;
|
||||
consul_registration::deregister_service(&consul_url, service_id.as_str()).await.expect("");
|
||||
consul_registration::deregister_service(&consul_url, service_id.as_str())
|
||||
.await
|
||||
.expect("");
|
||||
info!("service {} deregistered", service_name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{FromRow, Row};
|
||||
use utils::redis_cache::{RedisCache, Cache}; // Import RedisCache and Cache Trait
|
||||
use serde::{Serialize, Deserialize};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::{debug};
|
||||
use utils::redis_cache::{Cache, RedisCache}; // Import RedisCache and Cache Trait
|
||||
|
||||
#[derive(Debug, FromRow, Serialize, Deserialize)]
|
||||
pub struct User {
|
||||
@@ -29,7 +28,14 @@ impl UserRepository {
|
||||
pub async fn get_user_by_id(&self, user_id: i32) -> Result<User, sqlx::Error> {
|
||||
let cache_key = format!("user:{}", user_id);
|
||||
|
||||
if let Some(user) = self.cache.lock().await.get::<User>(&cache_key).await.map_err(|_| sqlx::Error::RowNotFound)? {
|
||||
if let Some(user) = self
|
||||
.cache
|
||||
.lock()
|
||||
.await
|
||||
.get::<User>(&cache_key)
|
||||
.await
|
||||
.map_err(|_| sqlx::Error::RowNotFound)?
|
||||
{
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
@@ -40,14 +46,26 @@ impl UserRepository {
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
|
||||
self.cache.lock().await.set(&cache_key, &user, 300).await.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
self.cache
|
||||
.lock()
|
||||
.await
|
||||
.set(&cache_key, &user, 300)
|
||||
.await
|
||||
.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn get_user_by_username(&self, username: &str) -> Result<User, sqlx::Error> {
|
||||
let cache_key = format!("user:username:{}", username);
|
||||
|
||||
if let Some(user) = self.cache.lock().await.get::<User>(&cache_key).await.map_err(|_| sqlx::Error::RowNotFound)? {
|
||||
if let Some(user) = self
|
||||
.cache
|
||||
.lock()
|
||||
.await
|
||||
.get::<User>(&cache_key)
|
||||
.await
|
||||
.map_err(|_| sqlx::Error::RowNotFound)?
|
||||
{
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
@@ -58,14 +76,26 @@ impl UserRepository {
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
|
||||
self.cache.lock().await.set(&cache_key, &user, 300).await.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
self.cache
|
||||
.lock()
|
||||
.await
|
||||
.set(&cache_key, &user, 300)
|
||||
.await
|
||||
.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn get_user_by_email(&self, email: &str) -> Result<User, sqlx::Error> {
|
||||
let cache_key = format!("user:email:{}", email);
|
||||
|
||||
if let Some(user) = self.cache.lock().await.get::<User>(&cache_key).await.map_err(|_| sqlx::Error::RowNotFound)? {
|
||||
if let Some(user) = self
|
||||
.cache
|
||||
.lock()
|
||||
.await
|
||||
.get::<User>(&cache_key)
|
||||
.await
|
||||
.map_err(|_| sqlx::Error::RowNotFound)?
|
||||
{
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
@@ -76,11 +106,21 @@ impl UserRepository {
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
|
||||
self.cache.lock().await.set(&cache_key, &user, 300).await.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
self.cache
|
||||
.lock()
|
||||
.await
|
||||
.set(&cache_key, &user, 300)
|
||||
.await
|
||||
.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn create_user(&self, username: &str, email: &str, hashed_password: &str) -> Result<i32, sqlx::Error> {
|
||||
pub async fn create_user(
|
||||
&self,
|
||||
username: &str,
|
||||
email: &str,
|
||||
hashed_password: &str,
|
||||
) -> Result<i32, sqlx::Error> {
|
||||
let row = sqlx::query(
|
||||
r#"
|
||||
INSERT INTO users (username, email, hashed_password)
|
||||
@@ -88,26 +128,33 @@ impl UserRepository {
|
||||
RETURNING id
|
||||
"#,
|
||||
)
|
||||
.bind(username)
|
||||
.bind(email)
|
||||
.bind(hashed_password)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
.bind(username)
|
||||
.bind(email)
|
||||
.bind(hashed_password)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(row.get(0))
|
||||
}
|
||||
|
||||
pub async fn update_user_email(&self, user_id: i32, new_email: &str) -> Result<(), sqlx::Error> {
|
||||
sqlx::query(
|
||||
"UPDATE users SET email = $1, updated_at = NOW() WHERE id = $2",
|
||||
)
|
||||
pub async fn update_user_email(
|
||||
&self,
|
||||
user_id: i32,
|
||||
new_email: &str,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query("UPDATE users SET email = $1, updated_at = NOW() WHERE id = $2")
|
||||
.bind(new_email)
|
||||
.bind(user_id)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
|
||||
let cache_key = format!("user:{}", user_id);
|
||||
self.cache.lock().await.delete(&cache_key).await.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
self.cache
|
||||
.lock()
|
||||
.await
|
||||
.delete(&cache_key)
|
||||
.await
|
||||
.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -118,7 +165,12 @@ impl UserRepository {
|
||||
.await?;
|
||||
|
||||
let cache_key = format!("user:{}", user_id);
|
||||
self.cache.lock().await.delete(&cache_key).await.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
self.cache
|
||||
.lock()
|
||||
.await
|
||||
.delete(&cache_key)
|
||||
.await
|
||||
.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use tokio;
|
||||
async fn test_get_user() {
|
||||
// // Set up a temporary in-memory PostgreSQL database
|
||||
// let pool = PgPool::connect("postgres://user:password@localhost/test_database").await.unwrap();
|
||||
//
|
||||
//
|
||||
// // Create the test table
|
||||
// pool.execute(
|
||||
// r#"
|
||||
@@ -20,7 +20,7 @@ async fn test_get_user() {
|
||||
// )
|
||||
// .await
|
||||
// .unwrap();
|
||||
//
|
||||
//
|
||||
// // Test the `get_user` function
|
||||
// let user = get_user(&pool, "123").await.unwrap();
|
||||
// assert_eq!(user.user_id, "123");
|
||||
|
||||
@@ -9,4 +9,4 @@ mod tests {
|
||||
// let db = Database::new(&database_url).await;
|
||||
// assert!(db.health_check().await);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ async fn test_redis_cache() {
|
||||
// dotenv().ok();
|
||||
// let redis_url = std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
|
||||
// let cache = RedisCache::new(&redis_url);
|
||||
//
|
||||
//
|
||||
// let key = &"test_key".to_string();
|
||||
// let value = "test_value";
|
||||
//
|
||||
//
|
||||
// // Test setting a value
|
||||
// cache.set(key, &value, 10).await.unwrap();
|
||||
//
|
||||
//
|
||||
// // Test getting the value
|
||||
// let cached_value: Option<String> = cache.get(key).await.unwrap();
|
||||
// assert_eq!(cached_value, Some("test_value".to_string()));
|
||||
|
||||
Reference in New Issue
Block a user