darkwing/database/team/
repository.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//! Repository implementations for team and settings-related database
//! operations.
//!
//! This module provides traits and implementations for accessing and managing
//! team and settings data in the database.

use std::sync::Arc;

use crate::database::Database;
use anyhow::Context;
use async_trait::async_trait;
use mockall::automock;
use sqlx::query_as;
use tracing::debug;

use super::{Setting, Team};

/// Type alias for a thread-safe reference-counted pointer to a TeamsRepository
/// implementation.
///
/// This allows sharing the repository across multiple threads while maintaining
/// proper connection pool management.
pub type DynTeamsRepository = Arc<dyn TeamsRepository + Send + Sync>;

/// Type alias for a thread-safe reference-counted pointer to a
/// SettingsRepository implementation.
///
/// This allows sharing the repository across multiple threads while maintaining
/// proper connection pool management.
pub type DynSettingsRepository = Arc<dyn SettingsRepository + Send + Sync>;

/// Repository trait defining operations for team data access.
#[automock]
#[async_trait]
pub trait TeamsRepository {
  /// Retrieves a team by its unique identifier.
  async fn get_team_by_id(&self, id: i64) -> anyhow::Result<Team>;
}

/// Implementation of TeamsRepository for the main Database type.
#[async_trait]
impl TeamsRepository for Database {
  async fn get_team_by_id(&self, id: i64) -> anyhow::Result<Team> {
    query_as!(Team, r#"
              SELECT id, userId, name, plan, usersLimit, browserProfilesLimit, subscriptionExpiration, syncEnabled
              FROM teams
              WHERE id = ?
              AND deleted_at IS NULL 
              LIMIT 1;
    "#, id)
    .fetch_one(&self.pool)
    .await
    .context("team was not found")
  }
}

/// Repository trait defining operations for settings data access.
#[automock]
#[async_trait]
pub trait SettingsRepository {
  /// Retrieves settings for a specific team and user combination.
  async fn get_by_team_id_and_user_id(
    &self,
    team_id: i64,
    user_id: i64,
  ) -> anyhow::Result<Vec<Setting>>;
}

/// Implementation of SettingsRepository for the main Database type.
#[async_trait]
impl SettingsRepository for Database {
  async fn get_by_team_id_and_user_id(
    &self,
    team_id: i64,
    user_id: i64,
  ) -> anyhow::Result<Vec<Setting>> {
    // TODO: what limit should be set here?
    let settings: Vec<Setting> = query_as(
      r#"
        SELECT level, category, param, deletedValue, valueCrypt, cryptoKeyId
        FROM settings
        WHERE ((teamId = ? and level = 'team') OR (userId = ? and level = 'user'))
        AND deleted_at IS NULL
        LIMIT 200;
      "#,
      )
      .bind(team_id)
      .bind(user_id)
      .fetch_all(&self.pool)
      .await
      .context("failed to fetch settings")?;

    debug!("fetched settings: {:?}", settings);

    Ok(settings)
  }
}