use std::{collections::HashMap, sync::Arc}; use axum::async_trait; use como_core::projects::ProjectService; use como_domain::{ projects::{mutation::CreateProjectMutation, queries::GetProjectQuery, ProjectDto}, user::ContextUserExt, Context, }; use tokio::sync::Mutex; use crate::database::ConnectionPool; pub struct DefaultProjectService { pool: ConnectionPool, } impl DefaultProjectService { pub fn new(connection_pool: ConnectionPool) -> Self { Self { pool: connection_pool, } } } #[async_trait] impl ProjectService for DefaultProjectService { async fn get_project( &self, context: &Context, query: GetProjectQuery, ) -> anyhow::Result { let user_id = context.get_user_id().ok_or(anyhow::anyhow!("no user id"))?; let rec = sqlx::query!( r#" SELECT id, name, description, user_id FROM projects WHERE id = $1 and user_id = $2 "#, query.project_id, &user_id ) .fetch_one(&self.pool) .await?; Ok(ProjectDto { id: rec.id, name: rec.name, description: rec.description, user_id: rec.user_id, }) } async fn get_projects(&self, context: &Context) -> anyhow::Result> { let user_id = context.get_user_id().ok_or(anyhow::anyhow!("no user id"))?; let recs = sqlx::query!( r#" SELECT id, name, description, user_id FROM projects WHERE user_id = $1 LIMIT 500 "#, &user_id ) .fetch_all(&self.pool) .await?; Ok(recs .into_iter() .map(|rec| ProjectDto { id: rec.id, name: rec.name, description: rec.description, user_id: rec.user_id, }) .collect::<_>()) } async fn create_project( &self, context: &Context, request: CreateProjectMutation, ) -> anyhow::Result { let user_id = context.get_user_id().ok_or(anyhow::anyhow!("no user id"))?; let rec = sqlx::query!( r#" INSERT INTO projects (id, name, description, user_id, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id "#, uuid::Uuid::new_v4(), request.name, request.description, &user_id, chrono::Utc::now().naive_utc(), chrono::Utc::now().naive_utc(), ) .fetch_one(&self.pool) .await?; Ok(ProjectDto { id: rec.id, name: request.name, description: request.description, user_id: user_id.clone(), }) } } pub struct MemoryProjectService { project_store: Arc>>, } impl MemoryProjectService { pub fn new() -> Self { Self { project_store: Arc::new(Mutex::new(HashMap::new())), } } } #[async_trait] impl ProjectService for MemoryProjectService { async fn get_project( &self, _context: &Context, query: GetProjectQuery, ) -> anyhow::Result { let ps = self.project_store.lock().await; Ok(ps .get(&query.project_id.to_string()) .ok_or(anyhow::anyhow!("could not find project"))? .clone()) } async fn get_projects(&self, context: &Context) -> anyhow::Result> { let user_id = context.get_user_id().ok_or(anyhow::anyhow!("no user id"))?; Ok(self .project_store .lock() .await .values() .filter(|p| p.user_id == user_id) .cloned() .collect::<_>()) } async fn create_project( &self, context: &Context, mutation: CreateProjectMutation, ) -> anyhow::Result { let user_id = context.get_user_id().ok_or(anyhow::anyhow!("no user id"))?; let mut ps = self.project_store.lock().await; let project = ProjectDto { id: uuid::Uuid::new_v4(), name: mutation.name, description: None, user_id, }; ps.insert(project.id.to_string(), project.clone()); Ok(project) } }