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

geo-engine / geoengine / 5760657609

04 Aug 2023 08:43AM UTC coverage: 89.424% (+0.002%) from 89.422%
5760657609

push

github

web-flow
Merge pull request #847 from geo-engine/pg-layer-metadata-mapping

layer metadata as sql type

38 of 38 new or added lines in 4 files covered. (100.0%)

103799 of 116075 relevant lines covered (89.42%)

62375.87 hits per line

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

76.86
/services/src/pro/layers/postgres_layer_db.rs
1
use crate::api::model::datatypes::{DataProviderId, LayerId};
2
use crate::api::model::HashMapTextTextDbType;
3

4
use crate::error;
5
use crate::layers::layer::Property;
6
use crate::pro::contexts::ProPostgresDb;
7
use crate::pro::permissions::{Permission, PermissionDb, RoleId};
8

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

32
use snafu::{ensure, ResultExt};
33
use std::str::FromStr;
34
use uuid::Uuid;
35

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

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

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

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

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

85
#[async_trait]
86
impl<Tls> LayerDb for ProPostgresDb<Tls>
87
where
88
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static,
89
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
90
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
91
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
92
{
93
    async fn add_layer(&self, layer: AddLayer, collection: &LayerCollectionId) -> Result<LayerId> {
6✔
94
        ensure!(
6✔
95
            self.has_permission(collection.clone(), Permission::Owner)
6✔
96
                .await?,
20✔
97
            error::PermissionDenied
×
98
        );
99

100
        let collection_id =
6✔
101
            Uuid::from_str(&collection.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
6✔
102
                found: collection.0.clone(),
×
103
            })?;
6✔
104

105
        let mut conn = self.conn_pool.get().await?;
6✔
106

107
        let layer = layer;
6✔
108

6✔
109
        let layer_id = Uuid::new_v4();
6✔
110

111
        let trans = conn.build_transaction().start().await?;
6✔
112

113
        let stmt = trans
6✔
114
            .prepare(
6✔
115
                "
6✔
116
            INSERT INTO layers (id, name, description, workflow, symbology, properties, metadata)
6✔
117
            VALUES ($1, $2, $3, $4, $5, $6, $7);",
6✔
118
            )
6✔
119
            .await?;
118✔
120

121
        trans
122
            .execute(
123
                &stmt,
6✔
124
                &[
6✔
125
                    &layer_id,
6✔
126
                    &layer.name,
6✔
127
                    &layer.description,
6✔
128
                    &serde_json::to_value(&layer.workflow).context(crate::error::SerdeJson)?,
6✔
129
                    &layer.symbology,
6✔
130
                    &layer.properties,
6✔
131
                    &HashMapTextTextDbType::from(&layer.metadata),
6✔
132
                ],
133
            )
134
            .await?;
6✔
135

136
        let stmt = trans
6✔
137
            .prepare(
6✔
138
                "
6✔
139
        INSERT INTO collection_layers (collection, layer)
6✔
140
        VALUES ($1, $2) ON CONFLICT DO NOTHING;",
6✔
141
            )
6✔
142
            .await?;
6✔
143

144
        trans.execute(&stmt, &[&collection_id, &layer_id]).await?;
7✔
145

146
        // TODO: `ON CONFLICT DO NOTHING` means, we do not get an error if the permission already exists.
147
        //       Do we want that, or should we report an error and let the caller decide whether to ignore it?
148
        //       We should decide that and adjust all places where `ON CONFILCT DO NOTHING` is used.
149
        let stmt = trans
6✔
150
            .prepare(
6✔
151
                "
6✔
152
        INSERT INTO permissions (role_id, permission, layer_id)
6✔
153
        VALUES ($1, $2, $3) ON CONFLICT DO NOTHING;",
6✔
154
            )
6✔
155
            .await?;
6✔
156

157
        trans
6✔
158
            .execute(
6✔
159
                &stmt,
6✔
160
                &[
6✔
161
                    &RoleId::from(self.session.user.id),
6✔
162
                    &Permission::Owner,
6✔
163
                    &layer_id,
6✔
164
                ],
6✔
165
            )
6✔
166
            .await?;
6✔
167

168
        trans.commit().await?;
6✔
169

170
        Ok(LayerId(layer_id.to_string()))
6✔
171
    }
12✔
172

173
    async fn add_layer_with_id(
×
174
        &self,
×
175
        id: &LayerId,
×
176
        layer: AddLayer,
×
177
        collection: &LayerCollectionId,
×
178
    ) -> Result<()> {
×
179
        ensure!(
×
180
            self.has_permission(collection.clone(), Permission::Owner)
×
181
                .await?,
×
182
            error::PermissionDenied
×
183
        );
184

185
        let layer_id =
×
186
            Uuid::from_str(&id.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
×
187
                found: collection.0.clone(),
×
188
            })?;
×
189

190
        let collection_id =
×
191
            Uuid::from_str(&collection.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
×
192
                found: collection.0.clone(),
×
193
            })?;
×
194

195
        let mut conn = self.conn_pool.get().await?;
×
196

197
        let layer = layer;
×
198

199
        let trans = conn.build_transaction().start().await?;
×
200

201
        let stmt = trans
×
202
            .prepare(
×
203
                "
×
204
            INSERT INTO layers (id, name, description, workflow, symbology, properties, metadata)
×
205
            VALUES ($1, $2, $3, $4, $5, $6, $7);",
×
206
            )
×
207
            .await?;
×
208

209
        trans
210
            .execute(
211
                &stmt,
×
212
                &[
×
213
                    &layer_id,
×
214
                    &layer.name,
×
215
                    &layer.description,
×
216
                    &serde_json::to_value(&layer.workflow).context(crate::error::SerdeJson)?,
×
217
                    &layer.symbology,
×
218
                    &layer.properties,
×
219
                    &HashMapTextTextDbType::from(&layer.metadata),
×
220
                ],
221
            )
222
            .await?;
×
223

224
        let stmt = trans
×
225
            .prepare(
×
226
                "
×
227
            INSERT INTO collection_layers (collection, layer)
×
228
            VALUES ($1, $2) ON CONFLICT DO NOTHING;",
×
229
            )
×
230
            .await?;
×
231

232
        trans.execute(&stmt, &[&collection_id, &layer_id]).await?;
×
233

234
        let stmt = trans
×
235
            .prepare(
×
236
                "
×
237
            INSERT INTO permissions (role_id, permission, layer_id)
×
238
            VALUES ($1, $2, $3) ON CONFLICT DO NOTHING;",
×
239
            )
×
240
            .await?;
×
241

242
        trans
×
243
            .execute(
×
244
                &stmt,
×
245
                &[
×
246
                    &RoleId::from(self.session.user.id),
×
247
                    &Permission::Owner,
×
248
                    &layer_id,
×
249
                ],
×
250
            )
×
251
            .await?;
×
252

253
        trans.commit().await?;
×
254

255
        Ok(())
×
256
    }
×
257

258
    async fn add_layer_to_collection(
2✔
259
        &self,
2✔
260
        layer: &LayerId,
2✔
261
        collection: &LayerCollectionId,
2✔
262
    ) -> Result<()> {
2✔
263
        ensure!(
2✔
264
            self.has_permission(collection.clone(), Permission::Owner)
2✔
265
                .await?,
6✔
266
            error::PermissionDenied
×
267
        );
268

269
        let layer_id =
1✔
270
            Uuid::from_str(&layer.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
2✔
271
                found: layer.0.clone(),
1✔
272
            })?;
2✔
273

274
        let collection_id =
1✔
275
            Uuid::from_str(&collection.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
1✔
276
                found: collection.0.clone(),
×
277
            })?;
1✔
278

279
        let conn = self.conn_pool.get().await?;
1✔
280

281
        let stmt = conn
1✔
282
            .prepare(
1✔
283
                "
1✔
284
            INSERT INTO collection_layers (collection, layer)
1✔
285
            VALUES ($1, $2) ON CONFLICT DO NOTHING;",
1✔
286
            )
1✔
287
            .await?;
1✔
288

289
        conn.execute(&stmt, &[&collection_id, &layer_id]).await?;
1✔
290

291
        Ok(())
1✔
292
    }
4✔
293

294
    async fn add_layer_collection(
12✔
295
        &self,
12✔
296
        collection: AddLayerCollection,
12✔
297
        parent: &LayerCollectionId,
12✔
298
    ) -> Result<LayerCollectionId> {
12✔
299
        ensure!(
12✔
300
            self.has_permission(parent.clone(), Permission::Owner)
12✔
301
                .await?,
53✔
302
            error::PermissionDenied
2✔
303
        );
304

305
        let parent =
10✔
306
            Uuid::from_str(&parent.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
10✔
307
                found: parent.0.clone(),
×
308
            })?;
10✔
309

310
        let mut conn = self.conn_pool.get().await?;
10✔
311

312
        let collection = collection;
10✔
313

10✔
314
        let collection_id = Uuid::new_v4();
10✔
315

316
        let trans = conn.build_transaction().start().await?;
10✔
317

318
        let stmt = trans
10✔
319
            .prepare(
10✔
320
                "
10✔
321
            INSERT INTO layer_collections (id, name, description, properties)
10✔
322
            VALUES ($1, $2, $3, $4);",
10✔
323
            )
10✔
324
            .await?;
24✔
325

326
        trans
10✔
327
            .execute(
10✔
328
                &stmt,
10✔
329
                &[
10✔
330
                    &collection_id,
10✔
331
                    &collection.name,
10✔
332
                    &collection.description,
10✔
333
                    &collection.properties,
10✔
334
                ],
10✔
335
            )
10✔
336
            .await?;
8✔
337

338
        let stmt = trans
10✔
339
            .prepare(
10✔
340
                "
10✔
341
            INSERT INTO collection_children (parent, child)
10✔
342
            VALUES ($1, $2) ON CONFLICT DO NOTHING;",
10✔
343
            )
10✔
344
            .await?;
8✔
345

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

348
        let stmt = trans
10✔
349
            .prepare(
10✔
350
                "
10✔
351
            INSERT INTO permissions (role_id, permission, layer_collection_id)
10✔
352
            VALUES ($1, $2, $3) ON CONFLICT DO NOTHING;",
10✔
353
            )
10✔
354
            .await?;
8✔
355

356
        trans
10✔
357
            .execute(
10✔
358
                &stmt,
10✔
359
                &[
10✔
360
                    &RoleId::from(self.session.user.id),
10✔
361
                    &Permission::Owner,
10✔
362
                    &collection_id,
10✔
363
                ],
10✔
364
            )
10✔
365
            .await?;
8✔
366

367
        trans.commit().await?;
10✔
368

369
        Ok(LayerCollectionId(collection_id.to_string()))
10✔
370
    }
24✔
371

372
    async fn add_layer_collection_with_id(
×
373
        &self,
×
374
        id: &LayerCollectionId,
×
375
        collection: AddLayerCollection,
×
376
        parent: &LayerCollectionId,
×
377
    ) -> Result<()> {
×
378
        ensure!(
×
379
            self.has_permission(parent.clone(), Permission::Owner)
×
380
                .await?,
×
381
            error::PermissionDenied
×
382
        );
383

384
        let collection_id =
×
385
            Uuid::from_str(&id.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
×
386
                found: id.0.clone(),
×
387
            })?;
×
388

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

394
        let mut conn = self.conn_pool.get().await?;
×
395

396
        let trans = conn.build_transaction().start().await?;
×
397

398
        let stmt = trans
×
399
            .prepare(
×
400
                "
×
401
            INSERT INTO layer_collections (id, name, description, properties)
×
402
            VALUES ($1, $2, $3, $4);",
×
403
            )
×
404
            .await?;
×
405

406
        trans
×
407
            .execute(
×
408
                &stmt,
×
409
                &[
×
410
                    &collection_id,
×
411
                    &collection.name,
×
412
                    &collection.description,
×
413
                    &collection.properties,
×
414
                ],
×
415
            )
×
416
            .await?;
×
417

418
        let stmt = trans
×
419
            .prepare(
×
420
                "
×
421
            INSERT INTO collection_children (parent, child)
×
422
            VALUES ($1, $2) ON CONFLICT DO NOTHING;",
×
423
            )
×
424
            .await?;
×
425

426
        trans.execute(&stmt, &[&parent, &collection_id]).await?;
×
427

428
        let stmt = trans
×
429
            .prepare(
×
430
                "
×
431
            INSERT INTO permissions (role_id, permission, layer_collection_id)
×
432
            VALUES ($1, $2, $3) ON CONFLICT DO NOTHING;",
×
433
            )
×
434
            .await?;
×
435

436
        trans
×
437
            .execute(
×
438
                &stmt,
×
439
                &[
×
440
                    &RoleId::from(self.session.user.id),
×
441
                    &Permission::Owner,
×
442
                    &collection_id,
×
443
                ],
×
444
            )
×
445
            .await?;
×
446

447
        trans.commit().await?;
×
448

449
        Ok(())
×
450
    }
×
451

452
    async fn add_collection_to_parent(
1✔
453
        &self,
1✔
454
        collection: &LayerCollectionId,
1✔
455
        parent: &LayerCollectionId,
1✔
456
    ) -> Result<()> {
1✔
457
        ensure!(
1✔
458
            self.has_permission(collection.clone(), Permission::Owner)
1✔
459
                .await?,
3✔
460
            error::PermissionDenied
×
461
        );
462

463
        let collection =
1✔
464
            Uuid::from_str(&collection.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
1✔
465
                found: collection.0.clone(),
×
466
            })?;
1✔
467

468
        let parent =
1✔
469
            Uuid::from_str(&parent.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
1✔
470
                found: parent.0.clone(),
×
471
            })?;
1✔
472

473
        let conn = self.conn_pool.get().await?;
1✔
474

475
        let stmt = conn
1✔
476
            .prepare(
1✔
477
                "
1✔
478
            INSERT INTO collection_children (parent, child)
1✔
479
            VALUES ($1, $2) ON CONFLICT DO NOTHING;",
1✔
480
            )
1✔
481
            .await?;
1✔
482

483
        conn.execute(&stmt, &[&parent, &collection]).await?;
1✔
484

485
        Ok(())
1✔
486
    }
2✔
487

488
    async fn remove_layer_collection(&self, collection: &LayerCollectionId) -> Result<()> {
3✔
489
        ensure!(
3✔
490
            self.has_permission(collection.clone(), Permission::Owner)
3✔
491
                .await?,
9✔
492
            error::PermissionDenied
×
493
        );
494

495
        let collection =
3✔
496
            Uuid::from_str(&collection.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
3✔
497
                found: collection.0.clone(),
×
498
            })?;
3✔
499

500
        if collection == INTERNAL_LAYER_DB_ROOT_COLLECTION_ID {
3✔
501
            return Err(LayerDbError::CannotRemoveRootCollection.into());
1✔
502
        }
2✔
503

504
        let mut conn = self.conn_pool.get().await?;
2✔
505
        let transaction = conn.transaction().await?;
2✔
506

507
        // delete the collection!
508
        // on delete cascade removes all entries from `collection_children` and `collection_layers`
509

510
        let remove_layer_collection_stmt = transaction
2✔
511
            .prepare(
2✔
512
                "DELETE FROM layer_collections
2✔
513
                 WHERE id = $1;",
2✔
514
            )
2✔
515
            .await?;
2✔
516
        transaction
2✔
517
            .execute(&remove_layer_collection_stmt, &[&collection])
2✔
518
            .await?;
2✔
519

520
        _remove_collections_without_parent_collection(&transaction).await?;
4✔
521

522
        _remove_layers_without_parent_collection(&transaction).await?;
4✔
523

524
        transaction.commit().await.map_err(Into::into)
2✔
525
    }
6✔
526

527
    async fn remove_layer_from_collection(
2✔
528
        &self,
2✔
529
        layer: &LayerId,
2✔
530
        collection: &LayerCollectionId,
2✔
531
    ) -> Result<()> {
2✔
532
        ensure!(
2✔
533
            self.has_permission(layer.clone(), Permission::Owner)
2✔
534
                .await?,
6✔
535
            error::PermissionDenied
×
536
        );
537

538
        let collection_uuid =
2✔
539
            Uuid::from_str(&collection.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
2✔
540
                found: collection.0.clone(),
×
541
            })?;
2✔
542

543
        let layer_uuid =
2✔
544
            Uuid::from_str(&layer.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
2✔
545
                found: layer.0.clone(),
×
546
            })?;
2✔
547

548
        let mut conn = self.conn_pool.get().await?;
2✔
549
        let transaction = conn.transaction().await?;
2✔
550

551
        let remove_layer_collection_stmt = transaction
2✔
552
            .prepare(
2✔
553
                "DELETE FROM collection_layers
2✔
554
                 WHERE collection = $1
2✔
555
                 AND layer = $2;",
2✔
556
            )
2✔
557
            .await?;
2✔
558
        let num_results = transaction
2✔
559
            .execute(
2✔
560
                &remove_layer_collection_stmt,
2✔
561
                &[&collection_uuid, &layer_uuid],
2✔
562
            )
2✔
563
            .await?;
2✔
564

565
        if num_results == 0 {
2✔
566
            return Err(LayerDbError::NoLayerForGivenIdInCollection {
×
567
                layer: layer.clone(),
×
568
                collection: collection.clone(),
×
569
            }
×
570
            .into());
×
571
        }
2✔
572

2✔
573
        _remove_layers_without_parent_collection(&transaction).await?;
4✔
574

575
        transaction.commit().await.map_err(Into::into)
2✔
576
    }
4✔
577

578
    async fn remove_layer_collection_from_parent(
1✔
579
        &self,
1✔
580
        collection: &LayerCollectionId,
1✔
581
        parent: &LayerCollectionId,
1✔
582
    ) -> Result<()> {
1✔
583
        ensure!(
1✔
584
            self.has_permission(collection.clone(), Permission::Owner)
1✔
585
                .await?,
2✔
586
            error::PermissionDenied
×
587
        );
588

589
        let collection_uuid =
1✔
590
            Uuid::from_str(&collection.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
1✔
591
                found: collection.0.clone(),
×
592
            })?;
1✔
593

594
        let parent_collection_uuid =
1✔
595
            Uuid::from_str(&parent.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
1✔
596
                found: parent.0.clone(),
×
597
            })?;
1✔
598

599
        let mut conn = self.conn_pool.get().await?;
1✔
600
        let transaction = conn.transaction().await?;
1✔
601

602
        let remove_layer_collection_stmt = transaction
1✔
603
            .prepare(
1✔
604
                "DELETE FROM collection_children
1✔
605
                 WHERE child = $1
1✔
606
                 AND parent = $2;",
1✔
607
            )
1✔
608
            .await?;
1✔
609
        let num_results = transaction
1✔
610
            .execute(
1✔
611
                &remove_layer_collection_stmt,
1✔
612
                &[&collection_uuid, &parent_collection_uuid],
1✔
613
            )
1✔
614
            .await?;
1✔
615

616
        if num_results == 0 {
1✔
617
            return Err(LayerDbError::NoCollectionForGivenIdInCollection {
×
618
                collection: collection.clone(),
×
619
                parent: parent.clone(),
×
620
            }
×
621
            .into());
×
622
        }
1✔
623

1✔
624
        _remove_collections_without_parent_collection(&transaction).await?;
4✔
625

626
        _remove_layers_without_parent_collection(&transaction).await?;
2✔
627

628
        transaction.commit().await.map_err(Into::into)
1✔
629
    }
2✔
630
}
631

632
#[async_trait]
633
impl<Tls> LayerCollectionProvider for ProPostgresDb<Tls>
634
where
635
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static,
636
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
637
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
638
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
639
{
640
    #[allow(clippy::too_many_lines)]
641
    async fn load_layer_collection(
15✔
642
        &self,
15✔
643
        collection_id: &LayerCollectionId,
15✔
644
        options: LayerCollectionListOptions,
15✔
645
    ) -> Result<LayerCollection> {
15✔
646
        ensure!(
15✔
647
            self.has_permission(collection_id.clone(), Permission::Read)
15✔
648
                .await?,
45✔
649
            error::PermissionDenied
3✔
650
        );
651
        let collection = Uuid::from_str(&collection_id.0).map_err(|_| {
12✔
652
            crate::error::Error::IdStringMustBeUuid {
×
653
                found: collection_id.0.clone(),
×
654
            }
×
655
        })?;
12✔
656

657
        let conn = self.conn_pool.get().await?;
12✔
658

659
        let stmt = conn
12✔
660
            .prepare(
12✔
661
                "
12✔
662
        SELECT DISTINCT name, description, properties
12✔
663
        FROM user_permitted_layer_collections p 
12✔
664
            JOIN layer_collections c ON (p.layer_collection_id = c.id) 
12✔
665
        WHERE p.user_id = $1 AND layer_collection_id = $2;",
12✔
666
            )
12✔
667
            .await?;
12✔
668

669
        let row = conn
12✔
670
            .query_one(&stmt, &[&self.session.user.id, &collection])
12✔
671
            .await?;
12✔
672

673
        let name: String = row.get(0);
12✔
674
        let description: String = row.get(1);
12✔
675
        let properties: Vec<Property> = row.get(2);
12✔
676

677
        let stmt = conn
12✔
678
            .prepare(
12✔
679
                "
12✔
680
        SELECT DISTINCT id, name, description, properties, is_layer
12✔
681
        FROM (
12✔
682
            SELECT 
12✔
683
                concat(id, '') AS id, 
12✔
684
                name, 
12✔
685
                description, 
12✔
686
                properties, 
12✔
687
                FALSE AS is_layer
12✔
688
            FROM user_permitted_layer_collections u 
12✔
689
                JOIN layer_collections lc ON (u.layer_collection_id = lc.id)
12✔
690
                JOIN collection_children cc ON (layer_collection_id = cc.child)
12✔
691
            WHERE u.user_id = $4 AND cc.parent = $1
12✔
692
        ) u UNION (
12✔
693
            SELECT 
12✔
694
                concat(id, '') AS id, 
12✔
695
                name, 
12✔
696
                description, 
12✔
697
                properties, 
12✔
698
                TRUE AS is_layer
12✔
699
            FROM user_permitted_layers ul
12✔
700
                JOIN layers uc ON (ul.layer_id = uc.id) 
12✔
701
                JOIN collection_layers cl ON (layer_id = cl.layer)
12✔
702
            WHERE ul.user_id = $4 AND cl.collection = $1
12✔
703
        )
12✔
704
        ORDER BY is_layer ASC, name ASC
12✔
705
        LIMIT $2 
12✔
706
        OFFSET $3;            
12✔
707
        ",
12✔
708
            )
12✔
709
            .await?;
12✔
710

711
        let rows = conn
12✔
712
            .query(
12✔
713
                &stmt,
12✔
714
                &[
12✔
715
                    &collection,
12✔
716
                    &i64::from(options.limit),
12✔
717
                    &i64::from(options.offset),
12✔
718
                    &self.session.user.id,
12✔
719
                ],
12✔
720
            )
12✔
721
            .await?;
12✔
722

723
        let items = rows
12✔
724
            .into_iter()
12✔
725
            .map(|row| {
15✔
726
                let is_layer: bool = row.get(4);
15✔
727

15✔
728
                if is_layer {
15✔
729
                    Ok(CollectionItem::Layer(LayerListing {
5✔
730
                        id: ProviderLayerId {
5✔
731
                            provider_id: INTERNAL_PROVIDER_ID,
5✔
732
                            layer_id: LayerId(row.get(0)),
5✔
733
                        },
5✔
734
                        name: row.get(1),
5✔
735
                        description: row.get(2),
5✔
736
                        properties: row.get(3),
5✔
737
                    }))
5✔
738
                } else {
739
                    Ok(CollectionItem::Collection(LayerCollectionListing {
10✔
740
                        id: ProviderLayerCollectionId {
10✔
741
                            provider_id: INTERNAL_PROVIDER_ID,
10✔
742
                            collection_id: LayerCollectionId(row.get(0)),
10✔
743
                        },
10✔
744
                        name: row.get(1),
10✔
745
                        description: row.get(2),
10✔
746
                        properties: row.get(3),
10✔
747
                    }))
10✔
748
                }
749
            })
15✔
750
            .collect::<Result<Vec<CollectionItem>>>()?;
12✔
751

752
        Ok(LayerCollection {
12✔
753
            id: ProviderLayerCollectionId {
12✔
754
                provider_id: INTERNAL_PROVIDER_ID,
12✔
755
                collection_id: collection_id.clone(),
12✔
756
            },
12✔
757
            name,
12✔
758
            description,
12✔
759
            items,
12✔
760
            entry_label: None,
12✔
761
            properties,
12✔
762
        })
12✔
763
    }
30✔
764

765
    async fn get_root_layer_collection_id(&self) -> Result<LayerCollectionId> {
6✔
766
        Ok(LayerCollectionId(
6✔
767
            INTERNAL_LAYER_DB_ROOT_COLLECTION_ID.to_string(),
6✔
768
        ))
6✔
769
    }
6✔
770

771
    async fn load_layer(&self, id: &LayerId) -> Result<Layer> {
5✔
772
        ensure!(
5✔
773
            self.has_permission(id.clone(), Permission::Read).await?,
15✔
774
            error::PermissionDenied
3✔
775
        );
776

777
        let layer_id =
2✔
778
            Uuid::from_str(&id.0).map_err(|_| crate::error::Error::IdStringMustBeUuid {
2✔
779
                found: id.0.clone(),
×
780
            })?;
2✔
781

782
        let conn = self.conn_pool.get().await?;
2✔
783

784
        let stmt = conn
2✔
785
            .prepare(
2✔
786
                "
2✔
787
            SELECT 
2✔
788
                name,
2✔
789
                description,
2✔
790
                workflow,
2✔
791
                symbology,
2✔
792
                properties,
2✔
793
                metadata
2✔
794
            FROM layers l
2✔
795
            WHERE l.id = $1;",
2✔
796
            )
2✔
797
            .await?;
2✔
798

799
        let row = conn
2✔
800
            .query_one(&stmt, &[&layer_id])
2✔
801
            .await
2✔
802
            .map_err(|_error| LayerDbError::NoLayerForGivenId { id: id.clone() })?;
2✔
803

804
        Ok(Layer {
805
            id: ProviderLayerId {
2✔
806
                provider_id: INTERNAL_PROVIDER_ID,
2✔
807
                layer_id: id.clone(),
2✔
808
            },
2✔
809
            name: row.get(0),
2✔
810
            description: row.get(1),
2✔
811
            workflow: serde_json::from_value(row.get(2)).context(crate::error::SerdeJson)?,
2✔
812
            symbology: row.get(3),
2✔
813
            properties: row.get(4),
2✔
814
            metadata: row.get::<_, HashMapTextTextDbType>(5).into(),
2✔
815
        })
816
    }
10✔
817
}
818

819
#[async_trait]
820
impl<Tls> LayerProviderDb for ProPostgresDb<Tls>
821
where
822
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static,
823
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
824
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
825
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
826
{
827
    async fn add_layer_provider(
1✔
828
        &self,
1✔
829
        provider: Box<dyn DataProviderDefinition>,
1✔
830
    ) -> Result<DataProviderId> {
1✔
831
        ensure!(self.session.is_admin(), error::PermissionDenied);
1✔
832

833
        let conn = self.conn_pool.get().await?;
1✔
834

835
        let stmt = conn
1✔
836
            .prepare(
1✔
837
                "
1✔
838
              INSERT INTO layer_providers (
1✔
839
                  id, 
1✔
840
                  type_name, 
1✔
841
                  name,
1✔
842
                  definition
1✔
843
              )
1✔
844
              VALUES ($1, $2, $3, $4)",
1✔
845
            )
1✔
846
            .await?;
1✔
847

848
        let id = provider.id();
1✔
849
        conn.execute(
1✔
850
            &stmt,
1✔
851
            &[
1✔
852
                &id,
1✔
853
                &provider.type_name(),
1✔
854
                &provider.name(),
1✔
855
                &serde_json::to_value(provider)?,
1✔
856
            ],
857
        )
858
        .await?;
1✔
859
        Ok(id)
1✔
860
    }
2✔
861

862
    async fn list_layer_providers(
1✔
863
        &self,
1✔
864
        options: LayerProviderListingOptions,
1✔
865
    ) -> Result<Vec<LayerProviderListing>> {
1✔
866
        // TODO: permission
867
        let conn = self.conn_pool.get().await?;
1✔
868

869
        let stmt = conn
1✔
870
            .prepare(
1✔
871
                "
1✔
872
            SELECT 
1✔
873
                id, 
1✔
874
                name,
1✔
875
                type_name
1✔
876
            FROM 
1✔
877
                layer_providers
1✔
878
            ORDER BY name ASC
1✔
879
            LIMIT $1 
1✔
880
            OFFSET $2;",
1✔
881
            )
1✔
882
            .await?;
1✔
883

884
        let rows = conn
1✔
885
            .query(
1✔
886
                &stmt,
1✔
887
                &[&i64::from(options.limit), &i64::from(options.offset)],
1✔
888
            )
1✔
889
            .await?;
1✔
890

891
        Ok(rows
1✔
892
            .iter()
1✔
893
            .map(|row| LayerProviderListing {
1✔
894
                id: row.get(0),
1✔
895
                name: row.get(1),
1✔
896
                description: row.get(2),
1✔
897
            })
1✔
898
            .collect())
1✔
899
    }
2✔
900

901
    async fn load_layer_provider(&self, id: DataProviderId) -> Result<Box<dyn DataProvider>> {
1✔
902
        // TODO: permissions
903
        let conn = self.conn_pool.get().await?;
1✔
904

905
        let stmt = conn
1✔
906
            .prepare(
1✔
907
                "
1✔
908
               SELECT 
1✔
909
                   definition
1✔
910
               FROM 
1✔
911
                   layer_providers
1✔
912
               WHERE
1✔
913
                   id = $1",
1✔
914
            )
1✔
915
            .await?;
1✔
916

917
        let row = conn.query_one(&stmt, &[&id]).await?;
1✔
918

919
        let definition = serde_json::from_value::<Box<dyn DataProviderDefinition>>(row.get(0))?;
1✔
920

921
        definition.initialize().await
1✔
922
    }
2✔
923
}
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