Coverage Report

Created: 2026-03-25 23:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/runner/work/tenet/tenet/tenet-aws/src/services/graph_layout.rs
Line
Count
Source
1
//! Graph layout service layer.
2
//!
3
//! Handles authorization and persistence for project graph layout overrides.
4
5
use crate::{
6
    ApiError,
7
    db::{TenetDatabase, graph_layout::GraphLayoutRepository},
8
    models::{
9
        graph_layout::{
10
            GraphLayoutView, GraphViewLevel, PatchGraphLayoutRequest, PutGraphLayoutRequest,
11
        },
12
        organisation::OrganisationId,
13
        profile::UserId,
14
        project::ProjectId,
15
    },
16
    services::project::ProjectService,
17
};
18
19
#[derive(Clone)]
20
pub struct GraphLayoutService {
21
    repo: GraphLayoutRepository,
22
    project_service: ProjectService,
23
}
24
25
impl GraphLayoutService {
26
6
    pub fn new(database: TenetDatabase) -> Self {
27
6
        Self {
28
6
            repo: GraphLayoutRepository::new(database.clone()),
29
6
            project_service: ProjectService::new(database),
30
6
        }
31
6
    }
32
33
0
    pub async fn get_layout(
34
0
        &self,
35
0
        _user_id: &UserId,
36
0
        organisation_id: &OrganisationId,
37
0
        project_id: &ProjectId,
38
0
        view_level: GraphViewLevel,
39
0
    ) -> Result<GraphLayoutView, ApiError> {
40
0
        self.project_service
41
0
            .get_project(organisation_id, project_id)
42
0
            .await?;
43
44
0
        Ok(self
45
0
            .repo
46
0
            .get_layout(organisation_id, project_id, view_level)
47
0
            .await?
48
0
            .unwrap_or_else(|| {
49
0
                GraphLayoutView::new(organisation_id.clone(), project_id.clone(), view_level)
50
0
            }))
51
0
    }
52
53
0
    pub async fn put_layout(
54
0
        &self,
55
0
        _user_id: &UserId,
56
0
        organisation_id: &OrganisationId,
57
0
        project_id: &ProjectId,
58
0
        view_level: GraphViewLevel,
59
0
        request: PutGraphLayoutRequest,
60
0
    ) -> Result<GraphLayoutView, ApiError> {
61
0
        self.project_service
62
0
            .get_project(organisation_id, project_id)
63
0
            .await?;
64
65
0
        request.validate()?;
66
0
        let mut layout =
67
0
            GraphLayoutView::new(organisation_id.clone(), project_id.clone(), view_level);
68
0
        layout.nodes = request.nodes;
69
0
        layout.edges = request.edges;
70
0
        layout.viewport = request.viewport;
71
72
0
        self.repo
73
0
            .put_layout(
74
0
                organisation_id,
75
0
                project_id,
76
0
                view_level,
77
0
                layout,
78
0
                request.expected_version,
79
0
            )
80
0
            .await
81
0
    }
82
83
0
    pub async fn patch_layout(
84
0
        &self,
85
0
        _user_id: &UserId,
86
0
        organisation_id: &OrganisationId,
87
0
        project_id: &ProjectId,
88
0
        view_level: GraphViewLevel,
89
0
        patch: PatchGraphLayoutRequest,
90
0
    ) -> Result<GraphLayoutView, ApiError> {
91
0
        self.project_service
92
0
            .get_project(organisation_id, project_id)
93
0
            .await?;
94
95
0
        self.repo
96
0
            .patch_layout(organisation_id, project_id, view_level, patch)
97
0
            .await
98
0
    }
99
100
0
    pub async fn reset_layout(
101
0
        &self,
102
0
        _user_id: &UserId,
103
0
        organisation_id: &OrganisationId,
104
0
        project_id: &ProjectId,
105
0
        view_level: GraphViewLevel,
106
0
    ) -> Result<(), ApiError> {
107
0
        self.project_service
108
0
            .get_project(organisation_id, project_id)
109
0
            .await?;
110
111
0
        self.repo
112
0
            .reset_layout(organisation_id, project_id, view_level)
113
0
            .await
114
0
    }
115
}