• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geo-engine / geoengine / 11910714914

19 Nov 2024 10:06AM UTC coverage: 90.445% (-0.2%) from 90.687%
11910714914

push

github

web-flow
Merge pull request #994 from geo-engine/workspace-dependencies

use workspace dependencies, update toolchain, use global lock in expression

9 of 11 new or added lines in 6 files covered. (81.82%)

375 existing lines in 75 files now uncovered.

132867 of 146904 relevant lines covered (90.44%)

54798.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.79
/services/src/pro/projects/postgres_projectdb.rs
1
use crate::error::Result;
2
use crate::pro::contexts::ProPostgresDb;
3
use crate::pro::permissions::postgres_permissiondb::TxPermissionDb;
4
use crate::pro::permissions::Permission;
5
use crate::pro::users::UserId;
6
use crate::projects::error::ProjectNotFoundProjectDbError;
7
use crate::projects::error::{
8
    AccessFailedProjectDbError, Bb8ProjectDbError, PostgresProjectDbError, ProjectDbError,
9
};
10
use crate::projects::postgres_projectdb::{
11
    insert_project, load_plots, project_listings_from_rows, update_project,
12
};
13
use crate::projects::LoadVersion;
14
use crate::projects::ProjectLayer;
15
use crate::projects::{
16
    CreateProject, Project, ProjectDb, ProjectId, ProjectListOptions, ProjectListing,
17
    ProjectVersion, ProjectVersionId, UpdateProject,
18
};
19
use crate::workflows::workflow::WorkflowId;
20
use async_trait::async_trait;
21
use bb8_postgres::{
22
    tokio_postgres::tls::MakeTlsConnect, tokio_postgres::tls::TlsConnect, tokio_postgres::Socket,
23
};
24
use geoengine_datatypes::error::BoxedResultExt;
25
use snafu::{ensure, ResultExt};
26

27
#[async_trait]
28
impl<Tls> ProjectDb for ProPostgresDb<Tls>
29
where
30
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static + std::fmt::Debug,
31
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
32
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
33
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
34
{
35
    async fn list_projects(
36
        &self,
37
        options: ProjectListOptions,
38
    ) -> Result<Vec<ProjectListing>, ProjectDbError> {
3✔
39
        let mut conn = self.conn_pool.get().await.context(Bb8ProjectDbError)?;
3✔
40

41
        let trans = conn
3✔
42
            .build_transaction()
3✔
43
            .start()
3✔
44
            .await
3✔
45
            .context(PostgresProjectDbError)?;
3✔
46

47
        let stmt = trans
3✔
48
            .prepare(&format!(
3✔
49
                "
3✔
50
        SELECT p.id, p.project_id, p.name, p.description, p.changed 
3✔
51
        FROM user_permitted_projects u JOIN project_versions p ON (u.project_id = p.project_id)
3✔
52
        WHERE
3✔
53
            u.user_id = $1
3✔
54
            AND p.changed >= ALL (SELECT changed FROM project_versions WHERE project_id = p.project_id)
3✔
55
        ORDER BY p.{}
3✔
56
        LIMIT $2
3✔
57
        OFFSET $3;",
3✔
58
                options.order.to_sql_string()
3✔
59
            ))
3✔
60
            .await.context(PostgresProjectDbError)?;
3✔
61

62
        let project_rows = trans
3✔
63
            .query(
3✔
64
                &stmt,
3✔
65
                &[
3✔
66
                    &self.session.user.id,
3✔
67
                    &i64::from(options.limit),
3✔
68
                    &i64::from(options.offset),
3✔
69
                ],
3✔
70
            )
3✔
71
            .await
3✔
72
            .context(PostgresProjectDbError)?;
3✔
73

74
        let project_listings = project_listings_from_rows(&trans, project_rows).await?;
19✔
75

76
        trans.commit().await.context(PostgresProjectDbError)?;
3✔
77

78
        Ok(project_listings)
3✔
79
    }
6✔
80

81
    async fn create_project(&self, create: CreateProject) -> Result<ProjectId, ProjectDbError> {
22✔
82
        let mut conn = self.conn_pool.get().await.context(Bb8ProjectDbError)?;
22✔
83

84
        let project: Project = Project::from_create_project(create);
22✔
85

86
        let trans = conn
22✔
87
            .build_transaction()
22✔
88
            .start()
22✔
89
            .await
20✔
90
            .context(PostgresProjectDbError)?;
22✔
91

92
        let version_id = insert_project(&trans, &project).await?;
104✔
93

94
        let stmt = trans
22✔
95
            .prepare(
22✔
96
                "INSERT INTO 
22✔
97
                    project_version_authors (project_version_id, user_id) 
22✔
98
                VALUES 
22✔
99
                    ($1, $2);",
22✔
100
            )
22✔
101
            .await
20✔
102
            .context(PostgresProjectDbError)?;
22✔
103

104
        trans
22✔
105
            .execute(&stmt, &[&version_id, &self.session.user.id])
22✔
106
            .await
21✔
107
            .context(PostgresProjectDbError)?;
22✔
108

109
        let stmt = trans
22✔
110
            .prepare(
22✔
111
                "INSERT INTO permissions (role_id, permission, project_id) VALUES ($1, $2, $3);",
22✔
112
            )
22✔
113
            .await
23✔
114
            .context(PostgresProjectDbError)?;
22✔
115

116
        trans
22✔
117
            .execute(
22✔
118
                &stmt,
22✔
119
                &[&self.session.user.id, &Permission::Owner, &project.id],
22✔
120
            )
22✔
121
            .await
20✔
122
            .context(PostgresProjectDbError)?;
22✔
123

124
        trans.commit().await.context(PostgresProjectDbError)?;
22✔
125

126
        Ok(project.id)
22✔
127
    }
44✔
128

129
    async fn load_project(&self, project: ProjectId) -> Result<Project, ProjectDbError> {
13✔
130
        self.load_project_version(project, LoadVersion::Latest)
13✔
131
            .await
539✔
132
    }
26✔
133

134
    #[allow(clippy::too_many_lines)]
135
    async fn update_project(&self, update: UpdateProject) -> Result<(), ProjectDbError> {
11✔
136
        let mut conn = self.conn_pool.get().await.context(Bb8ProjectDbError)?;
12✔
137

138
        let trans = conn
11✔
139
            .build_transaction()
11✔
140
            .start()
11✔
141
            .await
13✔
142
            .context(PostgresProjectDbError)?;
11✔
143

144
        self.ensure_permission_in_tx(update.id.into(), Permission::Owner, &trans)
11✔
145
            .await
25✔
146
            .boxed_context(AccessFailedProjectDbError { project: update.id })?;
11✔
147

148
        let project = self.load_project(update.id).await?; // TODO: move inside transaction?
531✔
149

150
        let project = update_project(&trans, &project, update).await?;
66✔
151

152
        let stmt = trans
11✔
153
            .prepare(
11✔
154
                "INSERT INTO 
11✔
155
                    project_version_authors (project_version_id, user_id) 
11✔
156
                VALUES 
11✔
157
                    ($1, $2);",
11✔
158
            )
11✔
159
            .await
14✔
160
            .context(PostgresProjectDbError)?;
11✔
161

162
        trans
11✔
163
            .execute(&stmt, &[&project.version.id, &self.session.user.id])
11✔
164
            .await
12✔
165
            .context(PostgresProjectDbError)?;
11✔
166

167
        trans.commit().await.context(PostgresProjectDbError)?;
12✔
168

169
        Ok(())
11✔
170
    }
22✔
171

172
    async fn delete_project(&self, project: ProjectId) -> Result<(), ProjectDbError> {
2✔
173
        let mut conn = self.conn_pool.get().await.context(Bb8ProjectDbError)?;
2✔
174
        let trans = conn
2✔
175
            .build_transaction()
2✔
176
            .start()
2✔
177
            .await
2✔
178
            .context(PostgresProjectDbError)?;
2✔
179

180
        self.ensure_permission_in_tx(project.into(), Permission::Owner, &trans)
2✔
181
            .await
4✔
182
            .boxed_context(AccessFailedProjectDbError { project })?;
2✔
183

184
        let stmt = trans
2✔
185
            .prepare("DELETE FROM projects WHERE id = $1;")
2✔
186
            .await
2✔
187
            .context(PostgresProjectDbError)?;
2✔
188

189
        let rows_affected = trans
2✔
190
            .execute(&stmt, &[&project])
2✔
191
            .await
2✔
192
            .context(PostgresProjectDbError)?;
2✔
193

194
        trans.commit().await.context(PostgresProjectDbError)?;
2✔
195

196
        ensure!(
2✔
197
            rows_affected == 1,
2✔
UNCOV
198
            ProjectNotFoundProjectDbError { project }
×
199
        );
200

201
        Ok(())
2✔
202
    }
4✔
203

204
    #[allow(clippy::too_many_lines)]
205
    async fn load_project_version(
206
        &self,
207
        project: ProjectId,
208
        version: LoadVersion,
209
    ) -> Result<Project, ProjectDbError> {
15✔
210
        let mut conn = self.conn_pool.get().await.context(Bb8ProjectDbError)?;
23✔
211
        let trans = conn
15✔
212
            .build_transaction()
15✔
213
            .start()
15✔
214
            .await
16✔
215
            .context(PostgresProjectDbError)?;
15✔
216

217
        self.ensure_permission_in_tx(project.into(), Permission::Owner, &trans)
15✔
218
            .await
72✔
219
            .boxed_context(AccessFailedProjectDbError { project })?;
15✔
220

221
        let rows = if let LoadVersion::Version(version) = version {
13✔
UNCOV
222
            let stmt = trans
×
223
                .prepare(
×
224
                    "
×
225
            SELECT 
×
226
                p.project_id, 
×
227
                p.id, 
×
228
                p.name, 
×
229
                p.description,
×
230
                p.bounds,
×
231
                p.time_step,
×
232
                p.changed,
×
233
                a.user_id
×
234
            FROM 
×
235
                project_versions p JOIN project_version_authors a ON (p.id = a.project_version_id)
×
236
            WHERE p.project_id = $1 AND p.id = $2",
×
237
                )
×
UNCOV
238
                .await
×
UNCOV
239
                .context(PostgresProjectDbError)?;
×
240

UNCOV
241
            let rows = trans
×
242
                .query(&stmt, &[&project, &version])
×
UNCOV
243
                .await
×
UNCOV
244
                .context(PostgresProjectDbError)?;
×
245

UNCOV
246
            if rows.is_empty() {
×
UNCOV
247
                return Err(ProjectDbError::ProjectVersionNotFound { project, version });
×
UNCOV
248
            }
×
249

×
250
            rows
×
251
        } else {
252
            let stmt = trans
13✔
253
                .prepare(
13✔
254
                    "
13✔
255
            SELECT  
13✔
256
                p.project_id, 
13✔
257
                p.id, 
13✔
258
                p.name, 
13✔
259
                p.description,
13✔
260
                p.bounds,
13✔
261
                p.time_step,
13✔
262
                p.changed,
13✔
263
                a.user_id
13✔
264
            FROM 
13✔
265
                project_versions p JOIN project_version_authors a ON (p.id = a.project_version_id)
13✔
266
            WHERE project_id = $1 AND p.changed >= ALL(
13✔
267
                SELECT changed FROM project_versions WHERE project_id = $1
13✔
268
            )",
13✔
269
                )
13✔
270
                .await
134✔
271
                .context(PostgresProjectDbError)?;
13✔
272

273
            let rows = trans
13✔
274
                .query(&stmt, &[&project])
13✔
275
                .await
15✔
276
                .context(PostgresProjectDbError)?;
13✔
277

278
            if rows.is_empty() {
13✔
UNCOV
279
                return Err(ProjectDbError::ProjectNotFound { project });
×
280
            }
13✔
281

13✔
282
            rows
13✔
283
        };
284

285
        let row = &rows[0];
13✔
286

13✔
287
        let project_id = ProjectId(row.get(0));
13✔
288
        let version_id = ProjectVersionId(row.get(1));
13✔
289
        let name = row.get(2);
13✔
290
        let description = row.get(3);
13✔
291
        let bounds = row.get(4);
13✔
292
        let time_step = row.get(5);
13✔
293
        let changed = row.get(6);
13✔
294
        let _author_id = UserId(row.get(7));
13✔
295

296
        let stmt = trans
13✔
297
            .prepare(
13✔
298
                "
13✔
299
        SELECT  
13✔
300
            name, workflow_id, symbology, visibility
13✔
301
        FROM project_version_layers
13✔
302
        WHERE project_version_id = $1
13✔
303
        ORDER BY layer_index ASC",
13✔
304
            )
13✔
305
            .await
303✔
306
            .context(PostgresProjectDbError)?;
13✔
307

308
        let rows = trans
13✔
309
            .query(&stmt, &[&version_id])
13✔
310
            .await
18✔
311
            .context(PostgresProjectDbError)?;
13✔
312

313
        let mut layers = vec![];
13✔
314
        for row in rows {
21✔
315
            layers.push(ProjectLayer {
8✔
316
                workflow: WorkflowId(row.get(1)),
8✔
317
                name: row.get(0),
8✔
318
                symbology: row.get(2),
8✔
319
                visibility: row.get(3),
8✔
320
            });
8✔
321
        }
8✔
322

323
        let project = Project {
13✔
324
            id: project_id,
13✔
325
            version: ProjectVersion {
13✔
326
                id: version_id,
13✔
327
                changed,
13✔
328
            },
13✔
329
            name,
13✔
330
            description,
13✔
331
            layers,
13✔
332
            plots: load_plots(&trans, &version_id).await?,
31✔
333
            bounds,
13✔
334
            time_step,
13✔
335
        };
13✔
336

13✔
337
        trans.commit().await.context(PostgresProjectDbError)?;
14✔
338

339
        Ok(project)
13✔
340
    }
30✔
341

342
    async fn list_project_versions(
343
        &self,
344
        project: ProjectId,
345
    ) -> Result<Vec<ProjectVersion>, ProjectDbError> {
6✔
346
        let mut conn = self.conn_pool.get().await.context(Bb8ProjectDbError)?;
6✔
347
        let trans = conn
6✔
348
            .build_transaction()
6✔
349
            .start()
6✔
350
            .await
6✔
351
            .context(PostgresProjectDbError)?;
6✔
352

353
        self.ensure_permission_in_tx(project.into(), Permission::Read, &trans)
6✔
354
            .await
12✔
355
            .boxed_context(AccessFailedProjectDbError { project })?;
6✔
356

357
        let stmt = trans
6✔
358
            .prepare(
6✔
359
                "
6✔
360
                SELECT 
6✔
361
                    p.id, p.changed, a.user_id
6✔
362
                FROM 
6✔
363
                    project_versions p JOIN project_version_authors a ON (p.id = a.project_version_id)
6✔
364
                WHERE 
6✔
365
                    project_id = $1 
6✔
366
                ORDER BY 
6✔
367
                    p.changed DESC, a.user_id DESC",
6✔
368
            )
6✔
369
            .await.context(PostgresProjectDbError)?;
8✔
370

371
        let rows = trans
6✔
372
            .query(&stmt, &[&project])
6✔
373
            .await
6✔
374
            .context(PostgresProjectDbError)?;
6✔
375

376
        trans.commit().await.context(PostgresProjectDbError)?;
6✔
377

378
        Ok(rows
6✔
379
            .iter()
6✔
380
            .map(|row| ProjectVersion {
18✔
381
                id: ProjectVersionId(row.get(0)),
18✔
382
                changed: row.get(1),
18✔
383
            })
18✔
384
            .collect())
6✔
385
    }
12✔
386
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc