124 lines
5.2 KiB
Rust
124 lines
5.2 KiB
Rust
use futures::stream::TryStreamExt;
|
|
use mongodb::{
|
|
bson::{doc, oid::ObjectId},
|
|
options::FindOptions,
|
|
Database,
|
|
};
|
|
use crate::common::errors::AppError;
|
|
use crate::developers::models::Developer;
|
|
|
|
#[async_trait::async_trait]
|
|
pub trait DeveloperRepository {
|
|
async fn find_all_by_user(&self, user_id: &ObjectId) -> Result<Vec<Developer>, AppError>;
|
|
async fn find_by_id(&self, dev_id: &ObjectId, user_id: &ObjectId) -> Result<Option<Developer>, AppError>;
|
|
async fn search_by_name(&self, user_id: &ObjectId, query: &str) -> Result<Vec<Developer>, AppError>;
|
|
async fn create(&self, user_id: &ObjectId, name: &str, email: &str, skills: Vec<String>) -> Result<Developer, AppError>;
|
|
async fn update(&self, dev_id: &ObjectId, user_id: &ObjectId, name: &str, email: &str, skills: Vec<String>) -> Result<(), AppError>;
|
|
async fn delete(&self, dev_id: &ObjectId, user_id: &ObjectId) -> Result<(), AppError>;
|
|
async fn ensure_seeded(&self, user_id: &ObjectId) -> Result<(), AppError>;
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct MongoDeveloperRepository {
|
|
db: Database,
|
|
}
|
|
|
|
impl MongoDeveloperRepository {
|
|
pub fn new(db: Database) -> Self {
|
|
Self { db }
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl DeveloperRepository for MongoDeveloperRepository {
|
|
async fn find_all_by_user(&self, user_id: &ObjectId) -> Result<Vec<Developer>, AppError> {
|
|
let collection = self.db.collection::<Developer>("developers");
|
|
let filter = doc! { "user_id": user_id };
|
|
let find_options = FindOptions::builder().sort(doc! { "created_at": -1 }).build();
|
|
|
|
let mut cursor = collection.find(filter).with_options(find_options).await?;
|
|
let mut developers = Vec::new();
|
|
while let Some(dev) = cursor.try_next().await? {
|
|
developers.push(dev);
|
|
}
|
|
Ok(developers)
|
|
}
|
|
|
|
async fn find_by_id(&self, dev_id: &ObjectId, user_id: &ObjectId) -> Result<Option<Developer>, AppError> {
|
|
let collection = self.db.collection::<Developer>("developers");
|
|
let filter = doc! { "_id": dev_id, "user_id": user_id };
|
|
let dev = collection.find_one(filter).await?;
|
|
Ok(dev)
|
|
}
|
|
|
|
async fn search_by_name(&self, user_id: &ObjectId, query: &str) -> Result<Vec<Developer>, AppError> {
|
|
let collection = self.db.collection::<Developer>("developers");
|
|
// Regex search case-insensitive on developer name
|
|
let filter = doc! {
|
|
"user_id": user_id,
|
|
"name": { "$regex": query, "$options": "i" }
|
|
};
|
|
let find_options = FindOptions::builder().limit(10).build();
|
|
|
|
let mut cursor = collection.find(filter).with_options(find_options).await?;
|
|
let mut developers = Vec::new();
|
|
while let Some(dev) = cursor.try_next().await? {
|
|
developers.push(dev);
|
|
}
|
|
Ok(developers)
|
|
}
|
|
|
|
async fn create(&self, user_id: &ObjectId, name: &str, email: &str, skills: Vec<String>) -> Result<Developer, AppError> {
|
|
let collection = self.db.collection::<Developer>("developers");
|
|
let new_dev = Developer {
|
|
id: None,
|
|
user_id: *user_id,
|
|
name: name.to_string(),
|
|
email: email.to_string(),
|
|
skills,
|
|
created_at: chrono::Utc::now(),
|
|
};
|
|
let insert_result = collection.insert_one(new_dev.clone()).await?;
|
|
let mut dev = new_dev;
|
|
dev.id = Some(insert_result.inserted_id.as_object_id().expect("Inserted ID is ObjectId"));
|
|
Ok(dev)
|
|
}
|
|
|
|
async fn update(&self, dev_id: &ObjectId, user_id: &ObjectId, name: &str, email: &str, skills: Vec<String>) -> Result<(), AppError> {
|
|
let collection = self.db.collection::<Developer>("developers");
|
|
let filter = doc! { "_id": dev_id, "user_id": user_id };
|
|
let update = doc! {
|
|
"$set": {
|
|
"name": name,
|
|
"email": email,
|
|
"skills": skills
|
|
}
|
|
};
|
|
let result = collection.update_one(filter, update).await?;
|
|
if result.matched_count == 0 {
|
|
return Err(AppError::Unauthorized("Developer not found or not owned by user".to_string()));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
async fn delete(&self, dev_id: &ObjectId, user_id: &ObjectId) -> Result<(), AppError> {
|
|
let collection = self.db.collection::<Developer>("developers");
|
|
let filter = doc! { "_id": dev_id, "user_id": user_id };
|
|
let result = collection.delete_one(filter).await?;
|
|
if result.deleted_count == 0 {
|
|
return Err(AppError::Unauthorized("Developer not found or not owned by user".to_string()));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
async fn ensure_seeded(&self, user_id: &ObjectId) -> Result<(), AppError> {
|
|
let devs = self.find_all_by_user(user_id).await?;
|
|
if devs.is_empty() {
|
|
let _ = self.create(user_id, "Alice Vance", "alice@example.com", vec!["Rust".to_string(), "Axum".to_string()]).await;
|
|
let _ = self.create(user_id, "Bob Carter", "bob@example.com", vec!["Tailwind".to_string(), "JavaScript".to_string()]).await;
|
|
let _ = self.create(user_id, "Charlie Smith", "charlie@example.com", vec!["HTML".to_string(), "CSS".to_string()]).await;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|