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

geo-engine / geoengine / 6510717572

13 Oct 2023 03:18PM UTC coverage: 89.501% (-0.03%) from 89.532%
6510717572

push

github

web-flow
Merge pull request #885 from geo-engine/permission_txs

check permissions in the same transaction as queries

216 of 216 new or added lines in 5 files covered. (100.0%)

109513 of 122359 relevant lines covered (89.5%)

59349.76 hits per line

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

95.03
/services/src/pro/layers/postgres_layer_db.rs
1
use crate::error;
2
use crate::layers::external::TypedDataProviderDefinition;
3
use crate::layers::layer::Property;
4
use crate::pro::contexts::ProPostgresDb;
5
use crate::pro::datasets::TypedProDataProviderDefinition;
6
use crate::pro::permissions::postgres_permissiondb::TxPermissionDb;
7
use crate::pro::permissions::{Permission, RoleId};
8
use crate::workflows::workflow::WorkflowId;
9
use crate::{
10
    error::Result,
11
    layers::{
12
        external::{DataProvider, DataProviderDefinition},
13
        layer::{
14
            AddLayer, AddLayerCollection, CollectionItem, Layer, LayerCollection,
15
            LayerCollectionListOptions, LayerCollectionListing, LayerListing,
16
            ProviderLayerCollectionId, ProviderLayerId,
17
        },
18
        listing::{LayerCollectionId, LayerCollectionProvider},
19
        storage::{
20
            LayerDb, LayerProviderDb, LayerProviderListing, LayerProviderListingOptions,
21
            INTERNAL_LAYER_DB_ROOT_COLLECTION_ID, INTERNAL_PROVIDER_ID,
22
        },
23
        LayerDbError,
24
    },
25
};
26
use async_trait::async_trait;
27
use bb8_postgres::tokio_postgres::{
28
    tls::{MakeTlsConnect, TlsConnect},
29
    Socket,
30
};
31
use geoengine_datatypes::dataset::{DataProviderId, LayerId};
32
use geoengine_datatypes::util::HashMapTextTextDbType;
33
use snafu::{ensure, ResultExt};
34
use std::str::FromStr;
35
use uuid::Uuid;
36

37
/// delete all collections without parent collection
38
async fn _remove_collections_without_parent_collection(
3✔
39
    transaction: &tokio_postgres::Transaction<'_>,
3✔
40
) -> Result<()> {
3✔
41
    // HINT: a recursive delete statement seems reasonable, but hard to implement in postgres
42
    //       because you have a graph with potential loops
43

44
    let remove_layer_collections_without_parents_stmt = transaction
3✔
45
        .prepare(
3✔
46
            "DELETE FROM layer_collections
3✔
47
                 WHERE  id <> $1 -- do not delete root collection
3✔
48
                 AND    id NOT IN (
3✔
49
                    SELECT child FROM collection_children
3✔
50
                 );",
3✔
51
        )
3✔
52
        .await?;
3✔
53
    while 0 < transaction
5✔
54
        .execute(
5✔
55
            &remove_layer_collections_without_parents_stmt,
5✔
56
            &[&INTERNAL_LAYER_DB_ROOT_COLLECTION_ID],
5✔
57
        )
5✔
58
        .await?
5✔
59
    {
2✔
60
        // whenever one collection is deleted, we have to check again if there are more
2✔
61
        // collections without parents
2✔
62
    }
2✔
63

64
    Ok(())
3✔
65
}
3✔
66

67
/// delete all layers without parent collection
68
async fn _remove_layers_without_parent_collection(
5✔
69
    transaction: &tokio_postgres::Transaction<'_>,
5✔
70
) -> Result<()> {
5✔
71
    let remove_layers_without_parents_stmt = transaction
5✔
72
        .prepare(
5✔
73
            "DELETE FROM layers
5✔
74
                 WHERE id NOT IN (
5✔
75
                    SELECT layer FROM collection_layers
5✔
76
                 );",
5✔
77
        )
5✔
78
        .await?;
5✔
79
    transaction
5✔
80
        .execute(&remove_layers_without_parents_stmt, &[])
5✔
81
        .await?;
5✔
82

83
    Ok(())
5✔
84
}
5✔
85

86
#[async_trait]
87
impl<Tls> LayerDb for ProPostgresDb<Tls>
88
where
89
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static,
90
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
91
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
92
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
93
{
94
    async fn add_layer(&self, layer: AddLayer, collection: &LayerCollectionId) -> Result<LayerId> {
6✔
95
        let layer_id = Uuid::new_v4();
6✔
96
        let layer_id = LayerId(layer_id.to_string());
6✔
97

6✔
98
        self.add_layer_with_id(&layer_id, layer, collection).await?;
208✔
99

100
        Ok(layer_id)
6✔
101
    }
12✔
102

103
    async fn add_layer_with_id(
6✔
104
        &self,
6✔
105
        id: &LayerId,
6✔
106
        layer: AddLayer,
6✔
107
        collection: &LayerCollectionId,
6✔
108
    ) -> Result<()> {
6✔
109
        let mut conn = self.conn_pool.get().await?;
6✔
110
        let trans = conn.build_transaction().start().await?;
6✔
111

112
        ensure!(
6✔
113
            self.has_permission_in_tx(collection.clone(), Permission::Owner, &trans)
6✔
114
                .await?,
17✔
115
            error::PermissionDenied
×
116
        );
117

118
        let layer_id =
6✔
119
            Uuid::from_str(&id.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
6✔
120
                found: collection.0.clone(),
×
121
            })?;
6✔
122

123
        let collection_id =
6✔
124
            Uuid::from_str(&collection.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
6✔
125
                found: collection.0.clone(),
×
126
            })?;
6✔
127

128
        let layer = layer;
6✔
129

6✔
130
        let workflow_id = WorkflowId::from_hash(&layer.workflow);
6✔
131

132
        let stmt = trans
6✔
133
            .prepare(
6✔
134
                "INSERT INTO workflows (id, workflow) VALUES ($1, $2) 
6✔
135
            ON CONFLICT DO NOTHING;",
6✔
136
            )
6✔
137
            .await?;
6✔
138

139
        trans
140
            .execute(
141
                &stmt,
6✔
142
                &[
6✔
143
                    &workflow_id,
6✔
144
                    &serde_json::to_value(&layer.workflow).context(error::SerdeJson)?,
6✔
145
                ],
146
            )
147
            .await?;
6✔
148

149
        let stmt = trans
6✔
150
            .prepare(
6✔
151
                "
6✔
152
            INSERT INTO layers (id, name, description, workflow_id, symbology, properties, metadata)
6✔
153
            VALUES ($1, $2, $3, $4, $5, $6, $7);",
6✔
154
            )
6✔
155
            .await?;
133✔
156

157
        trans
6✔
158
            .execute(
6✔
159
                &stmt,
6✔
160
                &[
6✔
161
                    &layer_id,
6✔
162
                    &layer.name,
6✔
163
                    &layer.description,
6✔
164
                    &workflow_id,
6✔
165
                    &layer.symbology,
6✔
166
                    &layer.properties,
6✔
167
                    &HashMapTextTextDbType::from(&layer.metadata),
6✔
168
                ],
6✔
169
            )
6✔
170
            .await?;
6✔
171

172
        let stmt = trans
6✔
173
            .prepare(
6✔
174
                "
6✔
175
            INSERT INTO collection_layers (collection, layer)
6✔
176
            VALUES ($1, $2) ON CONFLICT DO NOTHING;",
6✔
177
            )
6✔
178
            .await?;
6✔
179

180
        trans.execute(&stmt, &[&collection_id, &layer_id]).await?;
6✔
181

182
        // TODO: `ON CONFLICT DO NOTHING` means, we do not get an error if the permission already exists.
183
        //       Do we want that, or should we report an error and let the caller decide whether to ignore it?
184
        //       We should decide that and adjust all places where `ON CONFLICT DO NOTHING` is used.
185
        let stmt = trans
6✔
186
            .prepare(
6✔
187
                "
6✔
188
            INSERT INTO permissions (role_id, permission, layer_id)
6✔
189
            VALUES ($1, $2, $3) ON CONFLICT DO NOTHING;",
6✔
190
            )
6✔
191
            .await?;
6✔
192

193
        trans
6✔
194
            .execute(
6✔
195
                &stmt,
6✔
196
                &[
6✔
197
                    &RoleId::from(self.session.user.id),
6✔
198
                    &Permission::Owner,
6✔
199
                    &layer_id,
6✔
200
                ],
6✔
201
            )
6✔
202
            .await?;
6✔
203

204
        trans.commit().await?;
6✔
205

206
        Ok(())
6✔
207
    }
12✔
208

209
    async fn add_layer_to_collection(
2✔
210
        &self,
2✔
211
        layer: &LayerId,
2✔
212
        collection: &LayerCollectionId,
2✔
213
    ) -> Result<()> {
2✔
214
        let mut conn = self.conn_pool.get().await?;
2✔
215
        let tx = conn.build_transaction().start().await?;
2✔
216

217
        ensure!(
2✔
218
            self.has_permission_in_tx(collection.clone(), Permission::Owner, &tx)
2✔
219
                .await?,
4✔
220
            error::PermissionDenied
×
221
        );
222

223
        let layer_id =
1✔
224
            Uuid::from_str(&layer.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
2✔
225
                found: layer.0.clone(),
1✔
226
            })?;
2✔
227

228
        let collection_id =
1✔
229
            Uuid::from_str(&collection.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
1✔
230
                found: collection.0.clone(),
×
231
            })?;
1✔
232

233
        let stmt = tx
1✔
234
            .prepare(
1✔
235
                "
1✔
236
            INSERT INTO collection_layers (collection, layer)
1✔
237
            VALUES ($1, $2) ON CONFLICT DO NOTHING;",
1✔
238
            )
1✔
239
            .await?;
1✔
240

241
        tx.execute(&stmt, &[&collection_id, &layer_id]).await?;
1✔
242

243
        tx.commit().await?;
1✔
244

245
        Ok(())
1✔
246
    }
4✔
247

248
    async fn add_layer_collection(
12✔
249
        &self,
12✔
250
        collection: AddLayerCollection,
12✔
251
        parent: &LayerCollectionId,
12✔
252
    ) -> Result<LayerCollectionId> {
12✔
253
        let collection_id = Uuid::new_v4();
12✔
254
        let collection_id = LayerCollectionId(collection_id.to_string());
12✔
255

12✔
256
        self.add_layer_collection_with_id(&collection_id, collection, parent)
12✔
257
            .await?;
161✔
258

259
        Ok(collection_id)
10✔
260
    }
24✔
261

262
    async fn add_layer_collection_with_id(
12✔
263
        &self,
12✔
264
        id: &LayerCollectionId,
12✔
265
        collection: AddLayerCollection,
12✔
266
        parent: &LayerCollectionId,
12✔
267
    ) -> Result<()> {
12✔
268
        let mut conn = self.conn_pool.get().await?;
12✔
269
        let trans = conn.build_transaction().start().await?;
12✔
270

271
        ensure!(
12✔
272
            self.has_permission_in_tx(parent.clone(), Permission::Owner, &trans)
12✔
273
                .await?,
49✔
274
            error::PermissionDenied
2✔
275
        );
276

277
        let collection_id =
10✔
278
            Uuid::from_str(&id.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
10✔
279
                found: id.0.clone(),
×
280
            })?;
10✔
281

282
        let parent =
10✔
283
            Uuid::from_str(&parent.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
10✔
284
                found: parent.0.clone(),
×
285
            })?;
10✔
286

287
        let stmt = trans
10✔
288
            .prepare(
10✔
289
                "
10✔
290
            INSERT INTO layer_collections (id, name, description, properties)
10✔
291
            VALUES ($1, $2, $3, $4);",
10✔
292
            )
10✔
293
            .await?;
30✔
294

295
        trans
10✔
296
            .execute(
10✔
297
                &stmt,
10✔
298
                &[
10✔
299
                    &collection_id,
10✔
300
                    &collection.name,
10✔
301
                    &collection.description,
10✔
302
                    &collection.properties,
10✔
303
                ],
10✔
304
            )
10✔
305
            .await?;
10✔
306

307
        let stmt = trans
10✔
308
            .prepare(
10✔
309
                "
10✔
310
            INSERT INTO collection_children (parent, child)
10✔
311
            VALUES ($1, $2) ON CONFLICT DO NOTHING;",
10✔
312
            )
10✔
313
            .await?;
10✔
314

315
        trans.execute(&stmt, &[&parent, &collection_id]).await?;
10✔
316

317
        let stmt = trans
10✔
318
            .prepare(
10✔
319
                "
10✔
320
            INSERT INTO permissions (role_id, permission, layer_collection_id)
10✔
321
            VALUES ($1, $2, $3) ON CONFLICT DO NOTHING;",
10✔
322
            )
10✔
323
            .await?;
10✔
324

325
        trans
10✔
326
            .execute(
10✔
327
                &stmt,
10✔
328
                &[
10✔
329
                    &RoleId::from(self.session.user.id),
10✔
330
                    &Permission::Owner,
10✔
331
                    &collection_id,
10✔
332
                ],
10✔
333
            )
10✔
334
            .await?;
10✔
335

336
        trans.commit().await?;
10✔
337

338
        Ok(())
10✔
339
    }
24✔
340

341
    async fn add_collection_to_parent(
1✔
342
        &self,
1✔
343
        collection: &LayerCollectionId,
1✔
344
        parent: &LayerCollectionId,
1✔
345
    ) -> Result<()> {
1✔
346
        let mut conn = self.conn_pool.get().await?;
1✔
347
        let tx = conn.build_transaction().start().await?;
1✔
348

349
        ensure!(
1✔
350
            self.has_permission_in_tx(collection.clone(), Permission::Owner, &tx)
1✔
351
                .await?,
2✔
352
            error::PermissionDenied
×
353
        );
354

355
        let collection =
1✔
356
            Uuid::from_str(&collection.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
1✔
357
                found: collection.0.clone(),
×
358
            })?;
1✔
359

360
        let parent =
1✔
361
            Uuid::from_str(&parent.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
1✔
362
                found: parent.0.clone(),
×
363
            })?;
1✔
364

365
        let stmt = tx
1✔
366
            .prepare(
1✔
367
                "
1✔
368
            INSERT INTO collection_children (parent, child)
1✔
369
            VALUES ($1, $2) ON CONFLICT DO NOTHING;",
1✔
370
            )
1✔
371
            .await?;
1✔
372

373
        tx.execute(&stmt, &[&parent, &collection]).await?;
1✔
374

375
        Ok(())
1✔
376
    }
2✔
377

378
    async fn remove_layer_collection(&self, collection: &LayerCollectionId) -> Result<()> {
3✔
379
        let mut conn = self.conn_pool.get().await?;
3✔
380
        let transaction = conn.build_transaction().start().await?;
3✔
381

382
        ensure!(
3✔
383
            self.has_permission_in_tx(collection.clone(), Permission::Owner, &transaction)
3✔
384
                .await?,
6✔
385
            error::PermissionDenied
×
386
        );
387

388
        let collection =
3✔
389
            Uuid::from_str(&collection.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
3✔
390
                found: collection.0.clone(),
×
391
            })?;
3✔
392

393
        if collection == INTERNAL_LAYER_DB_ROOT_COLLECTION_ID {
3✔
394
            return Err(LayerDbError::CannotRemoveRootCollection.into());
1✔
395
        }
2✔
396

397
        // delete the collection!
398
        // on delete cascade removes all entries from `collection_children` and `collection_layers`
399

400
        let remove_layer_collection_stmt = transaction
2✔
401
            .prepare(
2✔
402
                "DELETE FROM layer_collections
2✔
403
                 WHERE id = $1;",
2✔
404
            )
2✔
405
            .await?;
2✔
406
        transaction
2✔
407
            .execute(&remove_layer_collection_stmt, &[&collection])
2✔
408
            .await?;
2✔
409

410
        _remove_collections_without_parent_collection(&transaction).await?;
4✔
411

412
        _remove_layers_without_parent_collection(&transaction).await?;
4✔
413

414
        transaction.commit().await.map_err(Into::into)
2✔
415
    }
6✔
416

417
    async fn remove_layer_from_collection(
2✔
418
        &self,
2✔
419
        layer: &LayerId,
2✔
420
        collection: &LayerCollectionId,
2✔
421
    ) -> Result<()> {
2✔
422
        let mut conn = self.conn_pool.get().await?;
2✔
423
        let transaction = conn.build_transaction().start().await?;
2✔
424

425
        ensure!(
2✔
426
            self.has_permission_in_tx(layer.clone(), Permission::Owner, &transaction)
2✔
427
                .await?,
4✔
428
            error::PermissionDenied
×
429
        );
430

431
        let collection_uuid =
2✔
432
            Uuid::from_str(&collection.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
2✔
433
                found: collection.0.clone(),
×
434
            })?;
2✔
435

436
        let layer_uuid =
2✔
437
            Uuid::from_str(&layer.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
2✔
438
                found: layer.0.clone(),
×
439
            })?;
2✔
440

441
        let remove_layer_collection_stmt = transaction
2✔
442
            .prepare(
2✔
443
                "DELETE FROM collection_layers
2✔
444
                 WHERE collection = $1
2✔
445
                 AND layer = $2;",
2✔
446
            )
2✔
447
            .await?;
2✔
448
        let num_results = transaction
2✔
449
            .execute(
2✔
450
                &remove_layer_collection_stmt,
2✔
451
                &[&collection_uuid, &layer_uuid],
2✔
452
            )
2✔
453
            .await?;
2✔
454

455
        if num_results == 0 {
2✔
456
            return Err(LayerDbError::NoLayerForGivenIdInCollection {
×
457
                layer: layer.clone(),
×
458
                collection: collection.clone(),
×
459
            }
×
460
            .into());
×
461
        }
2✔
462

2✔
463
        _remove_layers_without_parent_collection(&transaction).await?;
4✔
464

465
        transaction.commit().await.map_err(Into::into)
2✔
466
    }
4✔
467

468
    async fn remove_layer_collection_from_parent(
1✔
469
        &self,
1✔
470
        collection: &LayerCollectionId,
1✔
471
        parent: &LayerCollectionId,
1✔
472
    ) -> Result<()> {
1✔
473
        let mut conn = self.conn_pool.get().await?;
1✔
474
        let transaction = conn.build_transaction().start().await?;
1✔
475

476
        ensure!(
1✔
477
            self.has_permission_in_tx(collection.clone(), Permission::Owner, &transaction)
1✔
478
                .await?,
2✔
479
            error::PermissionDenied
×
480
        );
481

482
        let collection_uuid =
1✔
483
            Uuid::from_str(&collection.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
1✔
484
                found: collection.0.clone(),
×
485
            })?;
1✔
486

487
        let parent_collection_uuid =
1✔
488
            Uuid::from_str(&parent.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
1✔
489
                found: parent.0.clone(),
×
490
            })?;
1✔
491

492
        let remove_layer_collection_stmt = transaction
1✔
493
            .prepare(
1✔
494
                "DELETE FROM collection_children
1✔
495
                 WHERE child = $1
1✔
496
                 AND parent = $2;",
1✔
497
            )
1✔
498
            .await?;
1✔
499
        let num_results = transaction
1✔
500
            .execute(
1✔
501
                &remove_layer_collection_stmt,
1✔
502
                &[&collection_uuid, &parent_collection_uuid],
1✔
503
            )
1✔
504
            .await?;
1✔
505

506
        if num_results == 0 {
1✔
507
            return Err(LayerDbError::NoCollectionForGivenIdInCollection {
×
508
                collection: collection.clone(),
×
509
                parent: parent.clone(),
×
510
            }
×
511
            .into());
×
512
        }
1✔
513

1✔
514
        _remove_collections_without_parent_collection(&transaction).await?;
4✔
515

516
        _remove_layers_without_parent_collection(&transaction).await?;
2✔
517

518
        transaction.commit().await.map_err(Into::into)
1✔
519
    }
2✔
520
}
521

522
#[async_trait]
523
impl<Tls> LayerCollectionProvider for ProPostgresDb<Tls>
524
where
525
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static,
526
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
527
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
528
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
529
{
530
    #[allow(clippy::too_many_lines)]
531
    async fn load_layer_collection(
15✔
532
        &self,
15✔
533
        collection_id: &LayerCollectionId,
15✔
534
        options: LayerCollectionListOptions,
15✔
535
    ) -> Result<LayerCollection> {
15✔
536
        let mut conn = self.conn_pool.get().await?;
15✔
537
        let tx = conn.build_transaction().start().await?;
15✔
538

539
        ensure!(
15✔
540
            self.has_permission_in_tx(collection_id.clone(), Permission::Read, &tx)
15✔
541
                .await?,
30✔
542
            error::PermissionDenied
3✔
543
        );
544
        let collection = Uuid::from_str(&collection_id.0).map_err(|_| {
12✔
545
            crate::error::Error::IdStringMustBeUuid {
×
546
                found: collection_id.0.clone(),
×
547
            }
×
548
        })?;
12✔
549

550
        let stmt = tx
12✔
551
            .prepare(
12✔
552
                "
12✔
553
        SELECT DISTINCT name, description, properties
12✔
554
        FROM user_permitted_layer_collections p 
12✔
555
            JOIN layer_collections c ON (p.layer_collection_id = c.id) 
12✔
556
        WHERE p.user_id = $1 AND layer_collection_id = $2;",
12✔
557
            )
12✔
558
            .await?;
12✔
559

560
        let row = tx
12✔
561
            .query_one(&stmt, &[&self.session.user.id, &collection])
12✔
562
            .await?;
12✔
563

564
        let name: String = row.get(0);
12✔
565
        let description: String = row.get(1);
12✔
566
        let properties: Vec<Property> = row.get(2);
12✔
567

568
        let stmt = tx
12✔
569
            .prepare(
12✔
570
                "
12✔
571
        SELECT DISTINCT id, name, description, properties, is_layer
12✔
572
        FROM (
12✔
573
            SELECT 
12✔
574
                concat(id, '') AS id, 
12✔
575
                name, 
12✔
576
                description, 
12✔
577
                properties, 
12✔
578
                FALSE AS is_layer
12✔
579
            FROM user_permitted_layer_collections u 
12✔
580
                JOIN layer_collections lc ON (u.layer_collection_id = lc.id)
12✔
581
                JOIN collection_children cc ON (layer_collection_id = cc.child)
12✔
582
            WHERE u.user_id = $4 AND cc.parent = $1
12✔
583
        ) u UNION (
12✔
584
            SELECT 
12✔
585
                concat(id, '') AS id, 
12✔
586
                name, 
12✔
587
                description, 
12✔
588
                properties, 
12✔
589
                TRUE AS is_layer
12✔
590
            FROM user_permitted_layers ul
12✔
591
                JOIN layers uc ON (ul.layer_id = uc.id) 
12✔
592
                JOIN collection_layers cl ON (layer_id = cl.layer)
12✔
593
            WHERE ul.user_id = $4 AND cl.collection = $1
12✔
594
        )
12✔
595
        ORDER BY is_layer ASC, name ASC
12✔
596
        LIMIT $2 
12✔
597
        OFFSET $3;            
12✔
598
        ",
12✔
599
            )
12✔
600
            .await?;
12✔
601

602
        let rows = tx
12✔
603
            .query(
12✔
604
                &stmt,
12✔
605
                &[
12✔
606
                    &collection,
12✔
607
                    &i64::from(options.limit),
12✔
608
                    &i64::from(options.offset),
12✔
609
                    &self.session.user.id,
12✔
610
                ],
12✔
611
            )
12✔
612
            .await?;
12✔
613

614
        let items = rows
12✔
615
            .into_iter()
12✔
616
            .map(|row| {
15✔
617
                let is_layer: bool = row.get(4);
15✔
618

15✔
619
                if is_layer {
15✔
620
                    Ok(CollectionItem::Layer(LayerListing {
5✔
621
                        id: ProviderLayerId {
5✔
622
                            provider_id: INTERNAL_PROVIDER_ID,
5✔
623
                            layer_id: LayerId(row.get(0)),
5✔
624
                        },
5✔
625
                        name: row.get(1),
5✔
626
                        description: row.get(2),
5✔
627
                        properties: row.get(3),
5✔
628
                    }))
5✔
629
                } else {
630
                    Ok(CollectionItem::Collection(LayerCollectionListing {
10✔
631
                        id: ProviderLayerCollectionId {
10✔
632
                            provider_id: INTERNAL_PROVIDER_ID,
10✔
633
                            collection_id: LayerCollectionId(row.get(0)),
10✔
634
                        },
10✔
635
                        name: row.get(1),
10✔
636
                        description: row.get(2),
10✔
637
                        properties: row.get(3),
10✔
638
                    }))
10✔
639
                }
640
            })
15✔
641
            .collect::<Result<Vec<CollectionItem>>>()?;
12✔
642

643
        tx.commit().await?;
12✔
644

645
        Ok(LayerCollection {
12✔
646
            id: ProviderLayerCollectionId {
12✔
647
                provider_id: INTERNAL_PROVIDER_ID,
12✔
648
                collection_id: collection_id.clone(),
12✔
649
            },
12✔
650
            name,
12✔
651
            description,
12✔
652
            items,
12✔
653
            entry_label: None,
12✔
654
            properties,
12✔
655
        })
12✔
656
    }
30✔
657

658
    async fn get_root_layer_collection_id(&self) -> Result<LayerCollectionId> {
6✔
659
        Ok(LayerCollectionId(
6✔
660
            INTERNAL_LAYER_DB_ROOT_COLLECTION_ID.to_string(),
6✔
661
        ))
6✔
662
    }
6✔
663

664
    async fn load_layer(&self, id: &LayerId) -> Result<Layer> {
6✔
665
        let mut conn = self.conn_pool.get().await?;
6✔
666
        let tx = conn.build_transaction().start().await?;
6✔
667

668
        ensure!(
6✔
669
            self.has_permission_in_tx(id.clone(), Permission::Read, &tx)
6✔
670
                .await?,
12✔
671
            error::PermissionDenied
3✔
672
        );
673

674
        let layer_id =
3✔
675
            Uuid::from_str(&id.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
3✔
676
                found: id.0.clone(),
×
677
            })?;
3✔
678

679
        let stmt = tx
3✔
680
            .prepare(
3✔
681
                "
3✔
682
            SELECT 
3✔
683
                l.name,
3✔
684
                l.description,
3✔
685
                w.workflow,
3✔
686
                l.symbology,
3✔
687
                l.properties,
3✔
688
                l.metadata
3✔
689
            FROM 
3✔
690
                layers l JOIN workflows w ON (l.workflow_id = w.id)
3✔
691
            WHERE 
3✔
692
                l.id = $1;",
3✔
693
            )
3✔
694
            .await?;
3✔
695

696
        let row = tx
3✔
697
            .query_one(&stmt, &[&layer_id])
3✔
698
            .await
3✔
699
            .map_err(|_error| LayerDbError::NoLayerForGivenId { id: id.clone() })?;
3✔
700

701
        tx.commit().await?;
3✔
702

703
        Ok(Layer {
704
            id: ProviderLayerId {
3✔
705
                provider_id: INTERNAL_PROVIDER_ID,
3✔
706
                layer_id: id.clone(),
3✔
707
            },
3✔
708
            name: row.get(0),
3✔
709
            description: row.get(1),
3✔
710
            workflow: serde_json::from_value(row.get(2)).context(crate::error::SerdeJson)?,
3✔
711
            symbology: row.get(3),
3✔
712
            properties: row.get(4),
3✔
713
            metadata: row.get::<_, HashMapTextTextDbType>(5).into(),
3✔
714
        })
715
    }
12✔
716
}
717

718
#[async_trait]
719
impl<Tls> LayerProviderDb for ProPostgresDb<Tls>
720
where
721
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static,
722
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
723
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
724
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
725
{
726
    async fn add_layer_provider(
1✔
727
        &self,
1✔
728
        provider: TypedDataProviderDefinition,
1✔
729
    ) -> Result<DataProviderId> {
1✔
730
        ensure!(self.session.is_admin(), error::PermissionDenied);
1✔
731

732
        let conn = self.conn_pool.get().await?;
1✔
733

734
        let stmt = conn
1✔
735
            .prepare(
1✔
736
                "
1✔
737
              INSERT INTO layer_providers (
1✔
738
                  id, 
1✔
739
                  type_name, 
1✔
740
                  name,
1✔
741
                  definition
1✔
742
              )
1✔
743
              VALUES ($1, $2, $3, $4)",
1✔
744
            )
1✔
745
            .await?;
28✔
746

747
        let id = provider.id();
1✔
748
        conn.execute(
1✔
749
            &stmt,
1✔
750
            &[&id, &provider.type_name(), &provider.name(), &provider],
1✔
751
        )
1✔
752
        .await?;
1✔
753
        Ok(id)
1✔
754
    }
2✔
755

756
    async fn list_layer_providers(
2✔
757
        &self,
2✔
758
        options: LayerProviderListingOptions,
2✔
759
    ) -> Result<Vec<LayerProviderListing>> {
2✔
760
        // TODO: permission
761
        let conn = self.conn_pool.get().await?;
2✔
762

763
        let stmt = conn
2✔
764
            .prepare(
2✔
765
                "(
2✔
766
                    SELECT 
2✔
767
                        id, 
2✔
768
                        name,
2✔
769
                        type_name
2✔
770
                    FROM 
2✔
771
                        layer_providers
2✔
772
                    UNION ALL
2✔
773
                    SELECT 
2✔
774
                        id, 
2✔
775
                        name,
2✔
776
                        type_name
2✔
777
                    FROM 
2✔
778
                        pro_layer_providers
2✔
779
                )
2✔
780
                ORDER BY name ASC
2✔
781
                LIMIT $1 
2✔
782
                OFFSET $2;",
2✔
783
            )
2✔
784
            .await?;
2✔
785

786
        let rows = conn
2✔
787
            .query(
2✔
788
                &stmt,
2✔
789
                &[&i64::from(options.limit), &i64::from(options.offset)],
2✔
790
            )
2✔
791
            .await?;
2✔
792

793
        Ok(rows
2✔
794
            .iter()
2✔
795
            .map(|row| LayerProviderListing {
2✔
796
                id: row.get(0),
2✔
797
                name: row.get(1),
2✔
798
                description: row.get(2),
2✔
799
            })
2✔
800
            .collect())
2✔
801
    }
4✔
802

803
    async fn load_layer_provider(&self, id: DataProviderId) -> Result<Box<dyn DataProvider>> {
2✔
804
        // TODO: permissions
805
        let conn = self.conn_pool.get().await?;
2✔
806

807
        let stmt = conn
2✔
808
            .prepare(
2✔
809
                "SELECT
2✔
810
                    definition, NULL AS pro_definition
2✔
811
                FROM
2✔
812
                    layer_providers
2✔
813
                WHERE
2✔
814
                    id = $1
2✔
815
                UNION ALL
2✔
816
                SELECT
2✔
817
                    NULL AS definition, definition AS pro_definition
2✔
818
                FROM
2✔
819
                    pro_layer_providers
2✔
820
                WHERE
2✔
821
                    id = $1",
2✔
822
            )
2✔
823
            .await?;
34✔
824

825
        let row = conn.query_one(&stmt, &[&id]).await?;
2✔
826

827
        if let Some(definition) = row.get::<_, Option<TypedDataProviderDefinition>>(0) {
2✔
828
            return Box::new(definition).initialize().await;
1✔
829
        }
1✔
830

1✔
831
        let pro_definition: TypedProDataProviderDefinition = row.get(1);
1✔
832
        Box::new(pro_definition).initialize().await
1✔
833
    }
4✔
834
}
835

836
#[async_trait]
837
pub trait ProLayerProviderDb: Send + Sync + 'static {
838
    async fn add_pro_layer_provider(
839
        &self,
840
        provider: TypedProDataProviderDefinition,
841
    ) -> Result<DataProviderId>;
842
}
843

844
#[async_trait]
845
impl<Tls> ProLayerProviderDb for ProPostgresDb<Tls>
846
where
847
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static,
848
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
849
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
850
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
851
{
852
    async fn add_pro_layer_provider(
1✔
853
        &self,
1✔
854
        provider: TypedProDataProviderDefinition,
1✔
855
    ) -> Result<DataProviderId> {
1✔
856
        ensure!(self.session.is_admin(), error::PermissionDenied);
1✔
857

858
        let conn = self.conn_pool.get().await?;
1✔
859

860
        let stmt = conn
1✔
861
            .prepare(
1✔
862
                "
1✔
863
              INSERT INTO pro_layer_providers (
1✔
864
                  id, 
1✔
865
                  type_name, 
1✔
866
                  name,
1✔
867
                  definition
1✔
868
              )
1✔
869
              VALUES ($1, $2, $3, $4)",
1✔
870
            )
1✔
871
            .await?;
×
872

873
        let id = provider.id();
1✔
874
        conn.execute(
1✔
875
            &stmt,
1✔
876
            &[&id, &provider.type_name(), &provider.name(), &provider],
1✔
877
        )
1✔
878
        .await?;
1✔
879
        Ok(id)
1✔
880
    }
2✔
881
}
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