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

geo-engine / geoengine / 12768016656

14 Jan 2025 12:26PM UTC coverage: 90.64% (+0.06%) from 90.576%
12768016656

push

github

web-flow
Merge pull request #1006 from geo-engine/migrate-pro-api

Migrate-pro-api

1106 of 1152 new or added lines in 24 files covered. (96.01%)

248 existing lines in 13 files now uncovered.

133501 of 147287 relevant lines covered (90.64%)

54652.65 hits per line

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

98.0
/services/src/contexts/postgres.rs
1
use super::migrations::{all_migrations, CurrentSchemaMigration, MigrationResult};
2
use super::{initialize_database, ExecutionContextImpl, Session, SimpleApplicationContext};
3
use crate::api::model::services::Volume;
4
use crate::contexts::{ApplicationContext, QueryContextImpl, SessionId, SimpleSession};
5
use crate::contexts::{GeoEngineDb, SessionContext};
6
use crate::datasets::upload::Volumes;
7
use crate::datasets::DatasetName;
8
use crate::error::{self, Error, Result};
9
use crate::projects::{ProjectId, STRectangle};
10
use crate::tasks::{SimpleTaskManager, SimpleTaskManagerBackend, SimpleTaskManagerContext};
11
use crate::util::config;
12
use crate::util::config::get_config_element;
13
use async_trait::async_trait;
14
use bb8_postgres::{
15
    bb8::Pool,
16
    bb8::PooledConnection,
17
    tokio_postgres::{error::SqlState, tls::MakeTlsConnect, tls::TlsConnect, Config, Socket},
18
    PostgresConnectionManager,
19
};
20
use geoengine_datatypes::raster::TilingSpecification;
21
use geoengine_operators::engine::ChunkByteSize;
22
use geoengine_operators::util::create_rayon_thread_pool;
23
use log::info;
24
use rayon::ThreadPool;
25
use snafu::ensure;
26
use std::sync::Arc;
27
use uuid::Uuid;
28

29
// TODO: distinguish user-facing errors from system-facing error messages
30

31
/// A context with references to Postgres backends of the database.
32
#[derive(Clone)]
33
pub struct PostgresContext<Tls>
34
where
35
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static + std::fmt::Debug,
36
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
37
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
38
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
39
{
40
    default_session_id: SessionId,
41
    thread_pool: Arc<ThreadPool>,
42
    exe_ctx_tiling_spec: TilingSpecification,
43
    query_ctx_chunk_size: ChunkByteSize,
44
    task_manager: Arc<SimpleTaskManagerBackend>,
45
    pool: Pool<PostgresConnectionManager<Tls>>,
46
    volumes: Volumes,
47
}
48

49
enum DatabaseStatus {
50
    Unitialized,
51
    InitializedClearDatabase,
52
    InitializedKeepDatabase,
53
}
54

55
impl<Tls> PostgresContext<Tls>
56
where
57
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static + std::fmt::Debug,
58
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
59
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
60
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
61
{
62
    pub async fn new_with_context_spec(
91✔
63
        config: Config,
91✔
64
        tls: Tls,
91✔
65
        exe_ctx_tiling_spec: TilingSpecification,
91✔
66
        query_ctx_chunk_size: ChunkByteSize,
91✔
67
        volumes: Volumes,
91✔
68
    ) -> Result<Self> {
91✔
69
        let pg_mgr = PostgresConnectionManager::new(config, tls);
91✔
70

71
        let pool = Pool::builder().build(pg_mgr).await?;
91✔
72
        let created_schema = Self::create_database(pool.get().await?).await?;
91✔
73

74
        let session = if created_schema {
91✔
75
            let session = SimpleSession::default();
91✔
76
            Self::create_default_session(pool.get().await?, session.id()).await?;
91✔
77
            session
91✔
78
        } else {
79
            Self::load_default_session(pool.get().await?).await?
×
80
        };
81

82
        Ok(PostgresContext {
91✔
83
            default_session_id: session.id(),
91✔
84
            task_manager: Default::default(),
91✔
85
            thread_pool: create_rayon_thread_pool(0),
91✔
86
            exe_ctx_tiling_spec,
91✔
87
            query_ctx_chunk_size,
91✔
88
            pool,
91✔
89
            volumes,
91✔
90
        })
91✔
91
    }
91✔
92

93
    async fn check_schema_status(
317✔
94
        conn: &PooledConnection<'_, PostgresConnectionManager<Tls>>,
317✔
95
    ) -> Result<DatabaseStatus> {
317✔
96
        let stmt = match conn
317✔
97
            .prepare("SELECT clear_database_on_start from geoengine;")
317✔
98
            .await
317✔
99
        {
100
            Ok(stmt) => stmt,
×
101
            Err(e) => {
317✔
102
                if let Some(code) = e.code() {
317✔
103
                    if *code == SqlState::UNDEFINED_TABLE {
317✔
104
                        info!("Initializing schema.");
317✔
105
                        return Ok(DatabaseStatus::Unitialized);
317✔
106
                    }
×
107
                }
×
108
                return Err(error::Error::TokioPostgres { source: e });
×
109
            }
110
        };
111

112
        let row = conn.query_one(&stmt, &[]).await?;
×
113

114
        if row.get(0) {
×
115
            Ok(DatabaseStatus::InitializedClearDatabase)
×
116
        } else {
117
            Ok(DatabaseStatus::InitializedKeepDatabase)
×
118
        }
119
    }
317✔
120

121
    /// Clears the database if the Settings demand and the database properties allows it.
122
    pub(crate) async fn maybe_clear_database(
317✔
123
        conn: &PooledConnection<'_, PostgresConnectionManager<Tls>>,
317✔
124
    ) -> Result<()> {
317✔
125
        let postgres_config = get_config_element::<crate::util::config::Postgres>()?;
317✔
126
        let database_status = Self::check_schema_status(conn).await?;
317✔
127
        let schema_name = postgres_config.schema;
317✔
128

129
        match database_status {
×
130
            DatabaseStatus::InitializedClearDatabase
×
131
                if postgres_config.clear_database_on_start && schema_name != "pg_temp" =>
×
132
            {
×
133
                info!("Clearing schema {}.", schema_name);
×
134
                conn.batch_execute(&format!("DROP SCHEMA {schema_name} CASCADE;"))
×
135
                    .await?;
×
136
            }
137
            DatabaseStatus::InitializedKeepDatabase if postgres_config.clear_database_on_start => {
×
138
                return Err(Error::ClearDatabaseOnStartupNotAllowed)
×
139
            }
140
            DatabaseStatus::InitializedClearDatabase
141
            | DatabaseStatus::InitializedKeepDatabase
142
            | DatabaseStatus::Unitialized => (),
317✔
143
        };
144

145
        Ok(())
317✔
146
    }
317✔
147

148
    /// Creates the database schema. Returns true if the schema was created, false if it already existed.
149
    pub(crate) async fn create_database(
91✔
150
        mut conn: PooledConnection<'_, PostgresConnectionManager<Tls>>,
91✔
151
    ) -> Result<bool> {
91✔
152
        Self::maybe_clear_database(&conn).await?;
91✔
153

154
        let migration = initialize_database(
91✔
155
            &mut conn,
91✔
156
            Box::new(CurrentSchemaMigration),
91✔
157
            &all_migrations(),
91✔
158
        )
91✔
159
        .await?;
91✔
160

161
        Ok(migration == MigrationResult::CreatedDatabase)
91✔
162
    }
91✔
163

164
    async fn create_default_session(
91✔
165
        conn: PooledConnection<'_, PostgresConnectionManager<Tls>>,
91✔
166
        session_id: SessionId,
91✔
167
    ) -> Result<()> {
91✔
168
        let stmt = conn
91✔
169
            .prepare("INSERT INTO sessions (id, project_id, view) VALUES ($1, NULL ,NULL);")
91✔
170
            .await?;
91✔
171

172
        conn.execute(&stmt, &[&session_id]).await?;
91✔
173

174
        Ok(())
91✔
175
    }
91✔
176
    async fn load_default_session(
16✔
177
        conn: PooledConnection<'_, PostgresConnectionManager<Tls>>,
16✔
178
    ) -> Result<SimpleSession> {
16✔
179
        let stmt = conn
16✔
180
            .prepare("SELECT id, project_id, view FROM sessions LIMIT 1;")
16✔
181
            .await?;
16✔
182

183
        let row = conn.query_one(&stmt, &[]).await?;
16✔
184

185
        Ok(SimpleSession::new(row.get(0), row.get(1), row.get(2)))
16✔
186
    }
16✔
187
}
188

189
#[async_trait]
190
impl<Tls> SimpleApplicationContext for PostgresContext<Tls>
191
where
192
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static + std::fmt::Debug,
193
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
194
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
195
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
196
{
UNCOV
197
    async fn default_session_id(&self) -> SessionId {
×
UNCOV
198
        self.default_session_id
×
UNCOV
199
    }
×
200

201
    async fn default_session(&self) -> Result<SimpleSession> {
16✔
202
        Self::load_default_session(self.pool.get().await?).await
16✔
203
    }
32✔
204

UNCOV
205
    async fn update_default_session_project(&self, project: ProjectId) -> Result<()> {
×
UNCOV
206
        let conn = self.pool.get().await?;
×
207

UNCOV
208
        let stmt = conn
×
UNCOV
209
            .prepare("UPDATE sessions SET project_id = $1 WHERE id = $2;")
×
UNCOV
210
            .await?;
×
211

UNCOV
212
        conn.execute(&stmt, &[&project, &self.default_session_id])
×
UNCOV
213
            .await?;
×
214

UNCOV
215
        Ok(())
×
UNCOV
216
    }
×
217

UNCOV
218
    async fn update_default_session_view(&self, view: STRectangle) -> Result<()> {
×
UNCOV
219
        let conn = self.pool.get().await?;
×
220

UNCOV
221
        let stmt = conn
×
UNCOV
222
            .prepare("UPDATE sessions SET view = $1 WHERE id = $2;")
×
UNCOV
223
            .await?;
×
224

UNCOV
225
        conn.execute(&stmt, &[&view, &self.default_session_id])
×
UNCOV
226
            .await?;
×
227

UNCOV
228
        Ok(())
×
UNCOV
229
    }
×
230

231
    async fn default_session_context(&self) -> Result<Self::SessionContext> {
74✔
232
        Ok(self.session_context(self.session_by_id(self.default_session_id).await?))
74✔
233
    }
148✔
234
}
235

236
#[async_trait]
237
impl<Tls> ApplicationContext for PostgresContext<Tls>
238
where
239
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static + std::fmt::Debug,
240
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
241
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
242
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
243
{
244
    type SessionContext = PostgresSessionContext<Tls>;
245
    type Session = SimpleSession;
246

247
    fn session_context(&self, session: Self::Session) -> Self::SessionContext {
93✔
248
        PostgresSessionContext {
93✔
249
            session,
93✔
250
            context: self.clone(),
93✔
251
        }
93✔
252
    }
93✔
253

254
    async fn session_by_id(&self, session_id: SessionId) -> Result<Self::Session> {
74✔
255
        let mut conn = self.pool.get().await?;
74✔
256

257
        let tx = conn.build_transaction().start().await?;
74✔
258

259
        let stmt = tx
74✔
260
            .prepare(
74✔
261
                "
74✔
262
            SELECT           
74✔
263
                project_id,
74✔
264
                view
74✔
265
            FROM sessions
74✔
266
            WHERE id = $1;",
74✔
267
            )
74✔
268
            .await?;
74✔
269

270
        let row = tx
74✔
271
            .query_one(&stmt, &[&session_id])
74✔
272
            .await
74✔
273
            .map_err(|_error| error::Error::InvalidSession)?;
74✔
274

275
        Ok(SimpleSession::new(session_id, row.get(0), row.get(1)))
74✔
276
    }
148✔
277
}
278

279
#[derive(Clone)]
280
pub struct PostgresSessionContext<Tls>
281
where
282
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static + std::fmt::Debug,
283
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
284
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
285
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
286
{
287
    session: SimpleSession,
288
    context: PostgresContext<Tls>,
289
}
290

291
#[async_trait]
292
impl<Tls> SessionContext for PostgresSessionContext<Tls>
293
where
294
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static + std::fmt::Debug,
295
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
296
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
297
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
298
{
299
    type Session = SimpleSession;
300
    type GeoEngineDB = PostgresDb<Tls>;
301

302
    type TaskContext = SimpleTaskManagerContext;
303
    type TaskManager = SimpleTaskManager; // this does not persist across restarts
304
    type QueryContext = QueryContextImpl;
305
    type ExecutionContext = ExecutionContextImpl<Self::GeoEngineDB>;
306

307
    fn db(&self) -> Self::GeoEngineDB {
97✔
308
        PostgresDb::new(self.context.pool.clone())
97✔
309
    }
97✔
310

UNCOV
311
    fn tasks(&self) -> Self::TaskManager {
×
UNCOV
312
        SimpleTaskManager::new(self.context.task_manager.clone())
×
UNCOV
313
    }
×
314

315
    fn query_context(&self, _workflow: Uuid, _computation: Uuid) -> Result<Self::QueryContext> {
1✔
316
        Ok(QueryContextImpl::new(
1✔
317
            self.context.query_ctx_chunk_size,
1✔
318
            self.context.thread_pool.clone(),
1✔
319
        ))
1✔
320
    }
1✔
321

322
    fn execution_context(&self) -> Result<Self::ExecutionContext> {
2✔
323
        Ok(ExecutionContextImpl::<PostgresDb<Tls>>::new(
2✔
324
            self.db(),
2✔
325
            self.context.thread_pool.clone(),
2✔
326
            self.context.exe_ctx_tiling_spec,
2✔
327
        ))
2✔
328
    }
2✔
329

UNCOV
330
    fn volumes(&self) -> Result<Vec<Volume>> {
×
UNCOV
331
        Ok(self
×
UNCOV
332
            .context
×
UNCOV
333
            .volumes
×
UNCOV
334
            .volumes
×
UNCOV
335
            .iter()
×
UNCOV
336
            .map(|v| Volume {
×
UNCOV
337
                name: v.name.0.clone(),
×
UNCOV
338
                path: Some(v.path.to_string_lossy().to_string()),
×
UNCOV
339
            })
×
UNCOV
340
            .collect())
×
UNCOV
341
    }
×
342

UNCOV
343
    fn session(&self) -> &Self::Session {
×
UNCOV
344
        &self.session
×
UNCOV
345
    }
×
346
}
347

348
#[derive(Debug)]
349
pub struct PostgresDb<Tls>
350
where
351
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static + std::fmt::Debug,
352
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
353
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
354
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
355
{
356
    pub(crate) conn_pool: Pool<PostgresConnectionManager<Tls>>,
357
}
358

359
impl<Tls> PostgresDb<Tls>
360
where
361
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static + std::fmt::Debug,
362
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
363
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
364
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
365
{
366
    pub fn new(conn_pool: Pool<PostgresConnectionManager<Tls>>) -> Self {
98✔
367
        Self { conn_pool }
98✔
368
    }
98✔
369

370
    /// Check whether the namespace of the given dataset is allowed for insertion
371
    pub(crate) fn check_namespace(id: &DatasetName) -> Result<()> {
14✔
372
        // due to a lack of users, etc., we only allow one namespace for now
14✔
373
        if id.namespace.is_none() {
14✔
374
            Ok(())
14✔
375
        } else {
376
            Err(Error::InvalidDatasetIdNamespace)
×
377
        }
378
    }
14✔
379
}
380

381
impl<Tls> GeoEngineDb for PostgresDb<Tls>
382
where
383
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static + std::fmt::Debug,
384
    <Tls as MakeTlsConnect<Socket>>::Stream: Send + Sync,
385
    <Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
386
    <<Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
387
{
388
}
389

390
impl TryFrom<config::Postgres> for Config {
391
    type Error = Error;
392

393
    fn try_from(db_config: config::Postgres) -> Result<Self> {
26✔
394
        ensure!(
26✔
395
            db_config.schema != "public",
26✔
396
            crate::error::InvalidDatabaseSchema
×
397
        );
398

399
        let mut pg_config = Config::new();
26✔
400
        pg_config
26✔
401
            .user(&db_config.user)
26✔
402
            .password(&db_config.password)
26✔
403
            .host(&db_config.host)
26✔
404
            .dbname(&db_config.database)
26✔
405
            .port(db_config.port)
26✔
406
            // fix schema by providing `search_path` option
26✔
407
            .options(format!("-c search_path={}", db_config.schema));
26✔
408
        Ok(pg_config)
26✔
409
    }
26✔
410
}
411

412
#[cfg(test)]
413
mod tests {
414
    use super::*;
415
    use crate::datasets::external::aruna::ArunaDataProviderDefinition;
416
    use crate::datasets::external::gbif::{GbifDataProvider, GbifDataProviderDefinition};
417
    use crate::datasets::external::gfbio_abcd::GfbioAbcdDataProviderDefinition;
418
    use crate::datasets::external::gfbio_collections::GfbioCollectionsDataProviderDefinition;
419
    use crate::datasets::external::netcdfcf::{
420
        EbvPortalDataProviderDefinition, NetCdfCfDataProviderDefinition,
421
    };
422
    use crate::datasets::external::pangaea::PangaeaDataProviderDefinition;
423
    use crate::datasets::listing::{DatasetListOptions, DatasetListing, ProvenanceOutput};
424
    use crate::datasets::listing::{DatasetProvider, Provenance};
425
    use crate::datasets::storage::{DatasetStore, MetaDataDefinition};
426
    use crate::datasets::upload::{FileId, UploadId};
427
    use crate::datasets::upload::{FileUpload, Upload, UploadDb};
428
    use crate::datasets::{AddDataset, DatasetIdAndName};
429
    use crate::ge_context;
430
    use crate::layers::add_from_directory::UNSORTED_COLLECTION_ID;
431
    use crate::layers::external::TypedDataProviderDefinition;
432
    use crate::layers::layer::{
433
        AddLayer, AddLayerCollection, CollectionItem, LayerCollection, LayerCollectionListOptions,
434
        LayerCollectionListing, LayerListing, Property, ProviderLayerCollectionId, ProviderLayerId,
435
    };
436
    use crate::layers::listing::{
437
        LayerCollectionId, LayerCollectionProvider, SearchParameters, SearchType,
438
    };
439
    use crate::layers::storage::{
440
        LayerDb, LayerProviderDb, LayerProviderListing, LayerProviderListingOptions,
441
        INTERNAL_PROVIDER_ID,
442
    };
443
    use crate::pro::contexts::ProPostgresContext;
444
    use crate::pro::users::UserAuth;
445
    use crate::projects::{
446
        ColorParam, CreateProject, DerivedColor, DerivedNumber, LayerUpdate, LineSymbology,
447
        LoadVersion, NumberParam, OrderBy, Plot, PlotUpdate, PointSymbology, PolygonSymbology,
448
        ProjectDb, ProjectId, ProjectLayer, ProjectListOptions, ProjectListing, RasterSymbology,
449
        STRectangle, StrokeParam, Symbology, TextSymbology, UpdateProject,
450
    };
451
    use crate::util::encryption::U96;
452
    use crate::util::postgres::{assert_sql_type, DatabaseConnectionConfig};
453
    use crate::util::tests::register_ndvi_workflow_helper;
454
    use crate::workflows::registry::WorkflowRegistry;
455
    use crate::workflows::workflow::Workflow;
456
    use aes_gcm::aead::generic_array::arr;
457
    use bb8_postgres::tokio_postgres::NoTls;
458
    use futures::join;
459
    use geoengine_datatypes::collections::VectorDataType;
460
    use geoengine_datatypes::dataset::{DataProviderId, LayerId};
461
    use geoengine_datatypes::operations::image::{
462
        Breakpoint, Colorizer, RasterColorizer, RgbParams, RgbaColor,
463
    };
464
    use geoengine_datatypes::primitives::{
465
        BoundingBox2D, ClassificationMeasurement, ColumnSelection, ContinuousMeasurement,
466
        Coordinate2D, DateTimeParseFormat, FeatureDataType, MultiLineString, MultiPoint,
467
        MultiPolygon, NoGeometry, RasterQueryRectangle, SpatialPartition2D, SpatialResolution,
468
        TimeGranularity, TimeInstance, TimeInterval, TimeStep, TypedGeometry, VectorQueryRectangle,
469
    };
470
    use geoengine_datatypes::primitives::{CacheTtlSeconds, Measurement};
471
    use geoengine_datatypes::raster::{
472
        RasterDataType, RasterPropertiesEntryType, RasterPropertiesKey,
473
    };
474
    use geoengine_datatypes::spatial_reference::{SpatialReference, SpatialReferenceOption};
475
    use geoengine_datatypes::test_data;
476
    use geoengine_datatypes::util::test::TestDefault;
477
    use geoengine_datatypes::util::{NotNanF64, StringPair};
478
    use geoengine_operators::engine::{
479
        MetaData, MetaDataProvider, MultipleRasterOrSingleVectorSource, PlotOperator,
480
        PlotResultDescriptor, RasterBandDescriptor, RasterBandDescriptors, RasterResultDescriptor,
481
        StaticMetaData, TypedOperator, TypedResultDescriptor, VectorColumnInfo, VectorOperator,
482
        VectorResultDescriptor,
483
    };
484
    use geoengine_operators::mock::{
485
        MockDatasetDataSourceLoadingInfo, MockPointSource, MockPointSourceParams,
486
    };
487
    use geoengine_operators::plot::{Statistics, StatisticsParams};
488
    use geoengine_operators::source::{
489
        CsvHeader, FileNotFoundHandling, FormatSpecifics, GdalDatasetGeoTransform,
490
        GdalDatasetParameters, GdalLoadingInfo, GdalLoadingInfoTemporalSlice, GdalMetaDataList,
491
        GdalMetaDataRegular, GdalMetaDataStatic, GdalMetadataMapping, GdalMetadataNetCdfCf,
492
        GdalRetryOptions, GdalSourceTimePlaceholder, OgrSourceColumnSpec, OgrSourceDataset,
493
        OgrSourceDatasetTimeType, OgrSourceDurationSpec, OgrSourceErrorSpec, OgrSourceTimeFormat,
494
        TimeReference, UnixTimeStampType,
495
    };
496
    use geoengine_operators::util::input::MultiRasterOrVectorOperator::Raster;
497
    use ordered_float::NotNan;
498
    use serde_json::json;
499
    use std::marker::PhantomData;
500
    use std::path::PathBuf;
501
    use std::str::FromStr;
502
    use tokio_postgres::config::Host;
503

504
    #[ge_context::test]
2✔
505
    async fn test(app_ctx: PostgresContext<NoTls>) {
1✔
506
        let session = app_ctx.default_session().await.unwrap();
1✔
507

1✔
508
        create_projects(&app_ctx, &session).await;
1✔
509

510
        let projects = list_projects(&app_ctx, &session).await;
1✔
511

512
        let project_id = projects[0].id;
1✔
513

1✔
514
        update_projects(&app_ctx, &session, project_id).await;
1✔
515

516
        delete_project(&app_ctx, &session, project_id).await;
1✔
517
    }
1✔
518

519
    async fn delete_project(
1✔
520
        app_ctx: &PostgresContext<NoTls>,
1✔
521
        session: &SimpleSession,
1✔
522
        project_id: ProjectId,
1✔
523
    ) {
1✔
524
        let db = app_ctx.session_context(session.clone()).db();
1✔
525

1✔
526
        db.delete_project(project_id).await.unwrap();
1✔
527

1✔
528
        assert!(db.load_project(project_id).await.is_err());
1✔
529
    }
1✔
530

531
    #[allow(clippy::too_many_lines)]
532
    async fn update_projects(
1✔
533
        app_ctx: &PostgresContext<NoTls>,
1✔
534
        session: &SimpleSession,
1✔
535
        project_id: ProjectId,
1✔
536
    ) {
1✔
537
        let db = app_ctx.session_context(session.clone()).db();
1✔
538

539
        let project = db
1✔
540
            .load_project_version(project_id, LoadVersion::Latest)
1✔
541
            .await
1✔
542
            .unwrap();
1✔
543

544
        let layer_workflow_id = db
1✔
545
            .register_workflow(Workflow {
1✔
546
                operator: TypedOperator::Vector(
1✔
547
                    MockPointSource {
1✔
548
                        params: MockPointSourceParams {
1✔
549
                            points: vec![Coordinate2D::new(1., 2.); 3],
1✔
550
                        },
1✔
551
                    }
1✔
552
                    .boxed(),
1✔
553
                ),
1✔
554
            })
1✔
555
            .await
1✔
556
            .unwrap();
1✔
557

1✔
558
        assert!(db.load_workflow(&layer_workflow_id).await.is_ok());
1✔
559

560
        let plot_workflow_id = db
1✔
561
            .register_workflow(Workflow {
1✔
562
                operator: Statistics {
1✔
563
                    params: StatisticsParams {
1✔
564
                        column_names: vec![],
1✔
565
                        percentiles: vec![],
1✔
566
                    },
1✔
567
                    sources: MultipleRasterOrSingleVectorSource {
1✔
568
                        source: Raster(vec![]),
1✔
569
                    },
1✔
570
                }
1✔
571
                .boxed()
1✔
572
                .into(),
1✔
573
            })
1✔
574
            .await
1✔
575
            .unwrap();
1✔
576

1✔
577
        assert!(db.load_workflow(&plot_workflow_id).await.is_ok());
1✔
578

579
        // add a plot
580
        let update = UpdateProject {
1✔
581
            id: project.id,
1✔
582
            name: Some("Test9 Updated".into()),
1✔
583
            description: None,
1✔
584
            layers: Some(vec![LayerUpdate::UpdateOrInsert(ProjectLayer {
1✔
585
                workflow: layer_workflow_id,
1✔
586
                name: "TestLayer".into(),
1✔
587
                symbology: PointSymbology::default().into(),
1✔
588
                visibility: Default::default(),
1✔
589
            })]),
1✔
590
            plots: Some(vec![PlotUpdate::UpdateOrInsert(Plot {
1✔
591
                workflow: plot_workflow_id,
1✔
592
                name: "Test Plot".into(),
1✔
593
            })]),
1✔
594
            bounds: None,
1✔
595
            time_step: None,
1✔
596
        };
1✔
597
        db.update_project(update).await.unwrap();
1✔
598

599
        let versions = db.list_project_versions(project_id).await.unwrap();
1✔
600
        assert_eq!(versions.len(), 2);
1✔
601

602
        // add second plot
603
        let update = UpdateProject {
1✔
604
            id: project.id,
1✔
605
            name: Some("Test9 Updated".into()),
1✔
606
            description: None,
1✔
607
            layers: Some(vec![LayerUpdate::UpdateOrInsert(ProjectLayer {
1✔
608
                workflow: layer_workflow_id,
1✔
609
                name: "TestLayer".into(),
1✔
610
                symbology: PointSymbology::default().into(),
1✔
611
                visibility: Default::default(),
1✔
612
            })]),
1✔
613
            plots: Some(vec![
1✔
614
                PlotUpdate::UpdateOrInsert(Plot {
1✔
615
                    workflow: plot_workflow_id,
1✔
616
                    name: "Test Plot".into(),
1✔
617
                }),
1✔
618
                PlotUpdate::UpdateOrInsert(Plot {
1✔
619
                    workflow: plot_workflow_id,
1✔
620
                    name: "Test Plot".into(),
1✔
621
                }),
1✔
622
            ]),
1✔
623
            bounds: None,
1✔
624
            time_step: None,
1✔
625
        };
1✔
626
        db.update_project(update).await.unwrap();
1✔
627

628
        let versions = db.list_project_versions(project_id).await.unwrap();
1✔
629
        assert_eq!(versions.len(), 3);
1✔
630

631
        // delete plots
632
        let update = UpdateProject {
1✔
633
            id: project.id,
1✔
634
            name: None,
1✔
635
            description: None,
1✔
636
            layers: None,
1✔
637
            plots: Some(vec![]),
1✔
638
            bounds: None,
1✔
639
            time_step: None,
1✔
640
        };
1✔
641
        db.update_project(update).await.unwrap();
1✔
642

643
        let versions = db.list_project_versions(project_id).await.unwrap();
1✔
644
        assert_eq!(versions.len(), 4);
1✔
645
    }
1✔
646

647
    async fn list_projects(
1✔
648
        app_ctx: &PostgresContext<NoTls>,
1✔
649
        session: &SimpleSession,
1✔
650
    ) -> Vec<ProjectListing> {
1✔
651
        let options = ProjectListOptions {
1✔
652
            order: OrderBy::NameDesc,
1✔
653
            offset: 0,
1✔
654
            limit: 2,
1✔
655
        };
1✔
656

1✔
657
        let db = app_ctx.session_context(session.clone()).db();
1✔
658

659
        let projects = db.list_projects(options).await.unwrap();
1✔
660

1✔
661
        assert_eq!(projects.len(), 2);
1✔
662
        assert_eq!(projects[0].name, "Test9");
1✔
663
        assert_eq!(projects[1].name, "Test8");
1✔
664
        projects
1✔
665
    }
1✔
666

667
    async fn create_projects(app_ctx: &PostgresContext<NoTls>, session: &SimpleSession) {
1✔
668
        let db = app_ctx.session_context(session.clone()).db();
1✔
669

670
        for i in 0..10 {
11✔
671
            let create = CreateProject {
10✔
672
                name: format!("Test{i}"),
10✔
673
                description: format!("Test{}", 10 - i),
10✔
674
                bounds: STRectangle::new(
10✔
675
                    SpatialReferenceOption::Unreferenced,
10✔
676
                    0.,
10✔
677
                    0.,
10✔
678
                    1.,
10✔
679
                    1.,
10✔
680
                    0,
10✔
681
                    1,
10✔
682
                )
10✔
683
                .unwrap(),
10✔
684
                time_step: None,
10✔
685
            };
10✔
686
            db.create_project(create).await.unwrap();
10✔
687
        }
688
    }
1✔
689

690
    #[ge_context::test]
2✔
691
    async fn it_persists_workflows(app_ctx: PostgresContext<NoTls>) {
1✔
692
        let workflow = Workflow {
1✔
693
            operator: TypedOperator::Vector(
1✔
694
                MockPointSource {
1✔
695
                    params: MockPointSourceParams {
1✔
696
                        points: vec![Coordinate2D::new(1., 2.); 3],
1✔
697
                    },
1✔
698
                }
1✔
699
                .boxed(),
1✔
700
            ),
1✔
701
        };
1✔
702

703
        let session = app_ctx.default_session().await.unwrap();
1✔
704
        let ctx = app_ctx.session_context(session);
1✔
705

1✔
706
        let db = ctx.db();
1✔
707
        let id = db.register_workflow(workflow).await.unwrap();
1✔
708

1✔
709
        drop(ctx);
1✔
710

711
        let workflow = db.load_workflow(&id).await.unwrap();
1✔
712

1✔
713
        let json = serde_json::to_string(&workflow).unwrap();
1✔
714
        assert_eq!(
1✔
715
            json,
1✔
716
            r#"{"type":"Vector","operator":{"type":"MockPointSource","params":{"points":[{"x":1.0,"y":2.0},{"x":1.0,"y":2.0},{"x":1.0,"y":2.0}]}}}"#
1✔
717
        );
1✔
718
    }
1✔
719

720
    #[allow(clippy::too_many_lines)]
721
    #[ge_context::test]
2✔
722
    async fn it_persists_datasets(app_ctx: PostgresContext<NoTls>) {
1✔
723
        let loading_info = OgrSourceDataset {
1✔
724
            file_name: PathBuf::from("test.csv"),
1✔
725
            layer_name: "test.csv".to_owned(),
1✔
726
            data_type: Some(VectorDataType::MultiPoint),
1✔
727
            time: OgrSourceDatasetTimeType::Start {
1✔
728
                start_field: "start".to_owned(),
1✔
729
                start_format: OgrSourceTimeFormat::Auto,
1✔
730
                duration: OgrSourceDurationSpec::Zero,
1✔
731
            },
1✔
732
            default_geometry: None,
1✔
733
            columns: Some(OgrSourceColumnSpec {
1✔
734
                format_specifics: Some(FormatSpecifics::Csv {
1✔
735
                    header: CsvHeader::Auto,
1✔
736
                }),
1✔
737
                x: "x".to_owned(),
1✔
738
                y: None,
1✔
739
                int: vec![],
1✔
740
                float: vec![],
1✔
741
                text: vec![],
1✔
742
                bool: vec![],
1✔
743
                datetime: vec![],
1✔
744
                rename: None,
1✔
745
            }),
1✔
746
            force_ogr_time_filter: false,
1✔
747
            force_ogr_spatial_filter: false,
1✔
748
            on_error: OgrSourceErrorSpec::Ignore,
1✔
749
            sql_query: None,
1✔
750
            attribute_query: None,
1✔
751
            cache_ttl: CacheTtlSeconds::default(),
1✔
752
        };
1✔
753

1✔
754
        let meta_data = MetaDataDefinition::OgrMetaData(StaticMetaData::<
1✔
755
            OgrSourceDataset,
1✔
756
            VectorResultDescriptor,
1✔
757
            VectorQueryRectangle,
1✔
758
        > {
1✔
759
            loading_info: loading_info.clone(),
1✔
760
            result_descriptor: VectorResultDescriptor {
1✔
761
                data_type: VectorDataType::MultiPoint,
1✔
762
                spatial_reference: SpatialReference::epsg_4326().into(),
1✔
763
                columns: [(
1✔
764
                    "foo".to_owned(),
1✔
765
                    VectorColumnInfo {
1✔
766
                        data_type: FeatureDataType::Float,
1✔
767
                        measurement: Measurement::Unitless,
1✔
768
                    },
1✔
769
                )]
1✔
770
                .into_iter()
1✔
771
                .collect(),
1✔
772
                time: None,
1✔
773
                bbox: None,
1✔
774
            },
1✔
775
            phantom: Default::default(),
1✔
776
        });
1✔
777

778
        let session = app_ctx.default_session().await.unwrap();
1✔
779

1✔
780
        let dataset_name = DatasetName::new(None, "my_dataset");
1✔
781

1✔
782
        let db = app_ctx.session_context(session.clone()).db();
1✔
783
        let DatasetIdAndName {
784
            id: dataset_id,
1✔
785
            name: dataset_name,
1✔
786
        } = db
1✔
787
            .add_dataset(
1✔
788
                AddDataset {
1✔
789
                    name: Some(dataset_name.clone()),
1✔
790
                    display_name: "Ogr Test".to_owned(),
1✔
791
                    description: "desc".to_owned(),
1✔
792
                    source_operator: "OgrSource".to_owned(),
1✔
793
                    symbology: None,
1✔
794
                    provenance: Some(vec![Provenance {
1✔
795
                        citation: "citation".to_owned(),
1✔
796
                        license: "license".to_owned(),
1✔
797
                        uri: "uri".to_owned(),
1✔
798
                    }]),
1✔
799
                    tags: Some(vec!["upload".to_owned(), "test".to_owned()]),
1✔
800
                },
1✔
801
                meta_data,
1✔
802
            )
1✔
803
            .await
1✔
804
            .unwrap();
1✔
805

806
        let datasets = db
1✔
807
            .list_datasets(DatasetListOptions {
1✔
808
                filter: None,
1✔
809
                order: crate::datasets::listing::OrderBy::NameAsc,
1✔
810
                offset: 0,
1✔
811
                limit: 10,
1✔
812
                tags: None,
1✔
813
            })
1✔
814
            .await
1✔
815
            .unwrap();
1✔
816

1✔
817
        assert_eq!(datasets.len(), 1);
1✔
818

819
        assert_eq!(
1✔
820
            datasets[0],
1✔
821
            DatasetListing {
1✔
822
                id: dataset_id,
1✔
823
                name: dataset_name,
1✔
824
                display_name: "Ogr Test".to_owned(),
1✔
825
                description: "desc".to_owned(),
1✔
826
                source_operator: "OgrSource".to_owned(),
1✔
827
                symbology: None,
1✔
828
                tags: vec!["upload".to_owned(), "test".to_owned()],
1✔
829
                result_descriptor: TypedResultDescriptor::Vector(VectorResultDescriptor {
1✔
830
                    data_type: VectorDataType::MultiPoint,
1✔
831
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
832
                    columns: [(
1✔
833
                        "foo".to_owned(),
1✔
834
                        VectorColumnInfo {
1✔
835
                            data_type: FeatureDataType::Float,
1✔
836
                            measurement: Measurement::Unitless
1✔
837
                        }
1✔
838
                    )]
1✔
839
                    .into_iter()
1✔
840
                    .collect(),
1✔
841
                    time: None,
1✔
842
                    bbox: None,
1✔
843
                })
1✔
844
            },
1✔
845
        );
1✔
846

847
        let provenance = db.load_provenance(&dataset_id).await.unwrap();
1✔
848

1✔
849
        assert_eq!(
1✔
850
            provenance,
1✔
851
            ProvenanceOutput {
1✔
852
                data: dataset_id.into(),
1✔
853
                provenance: Some(vec![Provenance {
1✔
854
                    citation: "citation".to_owned(),
1✔
855
                    license: "license".to_owned(),
1✔
856
                    uri: "uri".to_owned(),
1✔
857
                }])
1✔
858
            }
1✔
859
        );
1✔
860

861
        let meta_data: Box<dyn MetaData<OgrSourceDataset, _, _>> =
1✔
862
            db.meta_data(&dataset_id.into()).await.unwrap();
1✔
863

864
        assert_eq!(
1✔
865
            meta_data
1✔
866
                .loading_info(VectorQueryRectangle {
1✔
867
                    spatial_bounds: BoundingBox2D::new_unchecked(
1✔
868
                        (-180., -90.).into(),
1✔
869
                        (180., 90.).into()
1✔
870
                    ),
1✔
871
                    time_interval: TimeInterval::default(),
1✔
872
                    spatial_resolution: SpatialResolution::zero_point_one(),
1✔
873
                    attributes: ColumnSelection::all()
1✔
874
                })
1✔
875
                .await
1✔
876
                .unwrap(),
1✔
877
            loading_info
878
        );
879
    }
1✔
880

881
    #[ge_context::test]
2✔
882
    async fn it_persists_uploads(app_ctx: PostgresContext<NoTls>) {
1✔
883
        let id = UploadId::from_str("2de18cd8-4a38-4111-a445-e3734bc18a80").unwrap();
1✔
884
        let input = Upload {
1✔
885
            id,
1✔
886
            files: vec![FileUpload {
1✔
887
                id: FileId::from_str("e80afab0-831d-4d40-95d6-1e4dfd277e72").unwrap(),
1✔
888
                name: "test.csv".to_owned(),
1✔
889
                byte_size: 1337,
1✔
890
            }],
1✔
891
        };
1✔
892

893
        let session = app_ctx.default_session().await.unwrap();
1✔
894

1✔
895
        let db = app_ctx.session_context(session.clone()).db();
1✔
896

1✔
897
        db.create_upload(input.clone()).await.unwrap();
1✔
898

899
        let upload = db.load_upload(id).await.unwrap();
1✔
900

1✔
901
        assert_eq!(upload, input);
1✔
902
    }
1✔
903

904
    #[allow(clippy::too_many_lines)]
905
    #[ge_context::test]
2✔
906
    async fn it_persists_layer_providers(app_ctx: PostgresContext<NoTls>) {
1✔
907
        let db = app_ctx.default_session_context().await.unwrap().db();
1✔
908

1✔
909
        let provider = NetCdfCfDataProviderDefinition {
1✔
910
            name: "netcdfcf".to_string(),
1✔
911
            description: "NetCdfCfProviderDefinition".to_string(),
1✔
912
            priority: Some(21),
1✔
913
            data: test_data!("netcdf4d/").into(),
1✔
914
            overviews: test_data!("netcdf4d/overviews/").into(),
1✔
915
            cache_ttl: CacheTtlSeconds::new(0),
1✔
916
        };
1✔
917

918
        let provider_id = db.add_layer_provider(provider.into()).await.unwrap();
1✔
919

920
        let providers = db
1✔
921
            .list_layer_providers(LayerProviderListingOptions {
1✔
922
                offset: 0,
1✔
923
                limit: 10,
1✔
924
            })
1✔
925
            .await
1✔
926
            .unwrap();
1✔
927

1✔
928
        assert_eq!(providers.len(), 1);
1✔
929

930
        assert_eq!(
1✔
931
            providers[0],
1✔
932
            LayerProviderListing {
1✔
933
                id: provider_id,
1✔
934
                name: "netcdfcf".to_owned(),
1✔
935
                priority: 21,
1✔
936
            }
1✔
937
        );
1✔
938

939
        let provider = db.load_layer_provider(provider_id).await.unwrap();
1✔
940

941
        let datasets = provider
1✔
942
            .load_layer_collection(
1✔
943
                &provider.get_root_layer_collection_id().await.unwrap(),
1✔
944
                LayerCollectionListOptions {
1✔
945
                    offset: 0,
1✔
946
                    limit: 10,
1✔
947
                },
1✔
948
            )
1✔
949
            .await
1✔
950
            .unwrap();
1✔
951

1✔
952
        assert_eq!(datasets.items.len(), 5, "{:?}", datasets.items);
1✔
953
    }
1✔
954

955
    #[allow(clippy::too_many_lines)]
956
    #[ge_context::test]
2✔
957
    async fn it_loads_all_meta_data_types(app_ctx: PostgresContext<NoTls>) {
1✔
958
        let session = app_ctx.default_session().await.unwrap();
1✔
959

1✔
960
        let db = app_ctx.session_context(session.clone()).db();
1✔
961

1✔
962
        let vector_descriptor = VectorResultDescriptor {
1✔
963
            data_type: VectorDataType::Data,
1✔
964
            spatial_reference: SpatialReferenceOption::Unreferenced,
1✔
965
            columns: Default::default(),
1✔
966
            time: None,
1✔
967
            bbox: None,
1✔
968
        };
1✔
969

1✔
970
        let raster_descriptor = RasterResultDescriptor {
1✔
971
            data_type: RasterDataType::U8,
1✔
972
            spatial_reference: SpatialReferenceOption::Unreferenced,
1✔
973
            time: None,
1✔
974
            bbox: None,
1✔
975
            resolution: None,
1✔
976
            bands: RasterBandDescriptors::new_single_band(),
1✔
977
        };
1✔
978

1✔
979
        let vector_ds = AddDataset {
1✔
980
            name: None,
1✔
981
            display_name: "OgrDataset".to_string(),
1✔
982
            description: "My Ogr dataset".to_string(),
1✔
983
            source_operator: "OgrSource".to_string(),
1✔
984
            symbology: None,
1✔
985
            provenance: None,
1✔
986
            tags: Some(vec!["upload".to_owned(), "test".to_owned()]),
1✔
987
        };
1✔
988

1✔
989
        let raster_ds = AddDataset {
1✔
990
            name: None,
1✔
991
            display_name: "GdalDataset".to_string(),
1✔
992
            description: "My Gdal dataset".to_string(),
1✔
993
            source_operator: "GdalSource".to_string(),
1✔
994
            symbology: None,
1✔
995
            provenance: None,
1✔
996
            tags: Some(vec!["upload".to_owned(), "test".to_owned()]),
1✔
997
        };
1✔
998

1✔
999
        let gdal_params = GdalDatasetParameters {
1✔
1000
            file_path: Default::default(),
1✔
1001
            rasterband_channel: 0,
1✔
1002
            geo_transform: GdalDatasetGeoTransform {
1✔
1003
                origin_coordinate: Default::default(),
1✔
1004
                x_pixel_size: 0.0,
1✔
1005
                y_pixel_size: 0.0,
1✔
1006
            },
1✔
1007
            width: 0,
1✔
1008
            height: 0,
1✔
1009
            file_not_found_handling: FileNotFoundHandling::NoData,
1✔
1010
            no_data_value: None,
1✔
1011
            properties_mapping: None,
1✔
1012
            gdal_open_options: None,
1✔
1013
            gdal_config_options: None,
1✔
1014
            allow_alphaband_as_mask: false,
1✔
1015
            retry: None,
1✔
1016
        };
1✔
1017

1✔
1018
        let meta = StaticMetaData {
1✔
1019
            loading_info: OgrSourceDataset {
1✔
1020
                file_name: Default::default(),
1✔
1021
                layer_name: String::new(),
1✔
1022
                data_type: None,
1✔
1023
                time: Default::default(),
1✔
1024
                default_geometry: None,
1✔
1025
                columns: None,
1✔
1026
                force_ogr_time_filter: false,
1✔
1027
                force_ogr_spatial_filter: false,
1✔
1028
                on_error: OgrSourceErrorSpec::Ignore,
1✔
1029
                sql_query: None,
1✔
1030
                attribute_query: None,
1✔
1031
                cache_ttl: CacheTtlSeconds::default(),
1✔
1032
            },
1✔
1033
            result_descriptor: vector_descriptor.clone(),
1✔
1034
            phantom: Default::default(),
1✔
1035
        };
1✔
1036

1037
        let id = db.add_dataset(vector_ds, meta.into()).await.unwrap().id;
1✔
1038

1039
        let meta: geoengine_operators::util::Result<
1✔
1040
            Box<dyn MetaData<OgrSourceDataset, VectorResultDescriptor, VectorQueryRectangle>>,
1✔
1041
        > = db.meta_data(&id.into()).await;
1✔
1042

1043
        assert!(meta.is_ok());
1✔
1044

1045
        let meta = GdalMetaDataRegular {
1✔
1046
            result_descriptor: raster_descriptor.clone(),
1✔
1047
            params: gdal_params.clone(),
1✔
1048
            time_placeholders: Default::default(),
1✔
1049
            data_time: Default::default(),
1✔
1050
            step: TimeStep {
1✔
1051
                granularity: TimeGranularity::Millis,
1✔
1052
                step: 0,
1✔
1053
            },
1✔
1054
            cache_ttl: CacheTtlSeconds::default(),
1✔
1055
        };
1✔
1056

1057
        let id = db
1✔
1058
            .add_dataset(raster_ds.clone(), meta.into())
1✔
1059
            .await
1✔
1060
            .unwrap()
1✔
1061
            .id;
1062

1063
        let meta: geoengine_operators::util::Result<
1✔
1064
            Box<dyn MetaData<GdalLoadingInfo, RasterResultDescriptor, RasterQueryRectangle>>,
1✔
1065
        > = db.meta_data(&id.into()).await;
1✔
1066

1067
        assert!(meta.is_ok());
1✔
1068

1069
        let meta = GdalMetaDataStatic {
1✔
1070
            time: None,
1✔
1071
            params: gdal_params.clone(),
1✔
1072
            result_descriptor: raster_descriptor.clone(),
1✔
1073
            cache_ttl: CacheTtlSeconds::default(),
1✔
1074
        };
1✔
1075

1076
        let id = db
1✔
1077
            .add_dataset(raster_ds.clone(), meta.into())
1✔
1078
            .await
1✔
1079
            .unwrap()
1✔
1080
            .id;
1081

1082
        let meta: geoengine_operators::util::Result<
1✔
1083
            Box<dyn MetaData<GdalLoadingInfo, RasterResultDescriptor, RasterQueryRectangle>>,
1✔
1084
        > = db.meta_data(&id.into()).await;
1✔
1085

1086
        assert!(meta.is_ok());
1✔
1087

1088
        let meta = GdalMetaDataList {
1✔
1089
            result_descriptor: raster_descriptor.clone(),
1✔
1090
            params: vec![],
1✔
1091
        };
1✔
1092

1093
        let id = db
1✔
1094
            .add_dataset(raster_ds.clone(), meta.into())
1✔
1095
            .await
1✔
1096
            .unwrap()
1✔
1097
            .id;
1098

1099
        let meta: geoengine_operators::util::Result<
1✔
1100
            Box<dyn MetaData<GdalLoadingInfo, RasterResultDescriptor, RasterQueryRectangle>>,
1✔
1101
        > = db.meta_data(&id.into()).await;
1✔
1102

1103
        assert!(meta.is_ok());
1✔
1104

1105
        let meta = GdalMetadataNetCdfCf {
1✔
1106
            result_descriptor: raster_descriptor.clone(),
1✔
1107
            params: gdal_params.clone(),
1✔
1108
            start: TimeInstance::MIN,
1✔
1109
            end: TimeInstance::MAX,
1✔
1110
            step: TimeStep {
1✔
1111
                granularity: TimeGranularity::Millis,
1✔
1112
                step: 0,
1✔
1113
            },
1✔
1114
            band_offset: 0,
1✔
1115
            cache_ttl: CacheTtlSeconds::default(),
1✔
1116
        };
1✔
1117

1118
        let id = db
1✔
1119
            .add_dataset(raster_ds.clone(), meta.into())
1✔
1120
            .await
1✔
1121
            .unwrap()
1✔
1122
            .id;
1123

1124
        let meta: geoengine_operators::util::Result<
1✔
1125
            Box<dyn MetaData<GdalLoadingInfo, RasterResultDescriptor, RasterQueryRectangle>>,
1✔
1126
        > = db.meta_data(&id.into()).await;
1✔
1127

1128
        assert!(meta.is_ok());
1✔
1129
    }
1✔
1130

1131
    #[allow(clippy::too_many_lines)]
1132
    #[ge_context::test]
2✔
1133
    async fn it_collects_layers(app_ctx: PostgresContext<NoTls>) {
1✔
1134
        let session = app_ctx.default_session().await.unwrap();
1✔
1135

1✔
1136
        let layer_db = app_ctx.session_context(session).db();
1✔
1137

1✔
1138
        let workflow = Workflow {
1✔
1139
            operator: TypedOperator::Vector(
1✔
1140
                MockPointSource {
1✔
1141
                    params: MockPointSourceParams {
1✔
1142
                        points: vec![Coordinate2D::new(1., 2.); 3],
1✔
1143
                    },
1✔
1144
                }
1✔
1145
                .boxed(),
1✔
1146
            ),
1✔
1147
        };
1✔
1148

1149
        let root_collection_id = layer_db.get_root_layer_collection_id().await.unwrap();
1✔
1150

1151
        let layer1 = layer_db
1✔
1152
            .add_layer(
1✔
1153
                AddLayer {
1✔
1154
                    name: "Layer1".to_string(),
1✔
1155
                    description: "Layer 1".to_string(),
1✔
1156
                    symbology: None,
1✔
1157
                    workflow: workflow.clone(),
1✔
1158
                    metadata: [("meta".to_string(), "datum".to_string())].into(),
1✔
1159
                    properties: vec![("proper".to_string(), "tee".to_string()).into()],
1✔
1160
                },
1✔
1161
                &root_collection_id,
1✔
1162
            )
1✔
1163
            .await
1✔
1164
            .unwrap();
1✔
1165

1166
        assert_eq!(
1✔
1167
            layer_db.load_layer(&layer1).await.unwrap(),
1✔
1168
            crate::layers::layer::Layer {
1✔
1169
                id: ProviderLayerId {
1✔
1170
                    provider_id: INTERNAL_PROVIDER_ID,
1✔
1171
                    layer_id: layer1.clone(),
1✔
1172
                },
1✔
1173
                name: "Layer1".to_string(),
1✔
1174
                description: "Layer 1".to_string(),
1✔
1175
                symbology: None,
1✔
1176
                workflow: workflow.clone(),
1✔
1177
                metadata: [("meta".to_string(), "datum".to_string())].into(),
1✔
1178
                properties: vec![("proper".to_string(), "tee".to_string()).into()],
1✔
1179
            }
1✔
1180
        );
1181

1182
        let collection1_id = layer_db
1✔
1183
            .add_layer_collection(
1✔
1184
                AddLayerCollection {
1✔
1185
                    name: "Collection1".to_string(),
1✔
1186
                    description: "Collection 1".to_string(),
1✔
1187
                    properties: Default::default(),
1✔
1188
                },
1✔
1189
                &root_collection_id,
1✔
1190
            )
1✔
1191
            .await
1✔
1192
            .unwrap();
1✔
1193

1194
        let layer2 = layer_db
1✔
1195
            .add_layer(
1✔
1196
                AddLayer {
1✔
1197
                    name: "Layer2".to_string(),
1✔
1198
                    description: "Layer 2".to_string(),
1✔
1199
                    symbology: None,
1✔
1200
                    workflow: workflow.clone(),
1✔
1201
                    metadata: Default::default(),
1✔
1202
                    properties: Default::default(),
1✔
1203
                },
1✔
1204
                &collection1_id,
1✔
1205
            )
1✔
1206
            .await
1✔
1207
            .unwrap();
1✔
1208

1209
        let collection2_id = layer_db
1✔
1210
            .add_layer_collection(
1✔
1211
                AddLayerCollection {
1✔
1212
                    name: "Collection2".to_string(),
1✔
1213
                    description: "Collection 2".to_string(),
1✔
1214
                    properties: Default::default(),
1✔
1215
                },
1✔
1216
                &collection1_id,
1✔
1217
            )
1✔
1218
            .await
1✔
1219
            .unwrap();
1✔
1220

1✔
1221
        layer_db
1✔
1222
            .add_collection_to_parent(&collection2_id, &collection1_id)
1✔
1223
            .await
1✔
1224
            .unwrap();
1✔
1225

1226
        let root_collection = layer_db
1✔
1227
            .load_layer_collection(
1✔
1228
                &root_collection_id,
1✔
1229
                LayerCollectionListOptions {
1✔
1230
                    offset: 0,
1✔
1231
                    limit: 20,
1✔
1232
                },
1✔
1233
            )
1✔
1234
            .await
1✔
1235
            .unwrap();
1✔
1236

1✔
1237
        assert_eq!(
1✔
1238
            root_collection,
1✔
1239
            LayerCollection {
1✔
1240
                id: ProviderLayerCollectionId {
1✔
1241
                    provider_id: INTERNAL_PROVIDER_ID,
1✔
1242
                    collection_id: root_collection_id,
1✔
1243
                },
1✔
1244
                name: "Layers".to_string(),
1✔
1245
                description: "All available Geo Engine layers".to_string(),
1✔
1246
                items: vec![
1✔
1247
                    CollectionItem::Collection(LayerCollectionListing {
1✔
1248
                        id: ProviderLayerCollectionId {
1✔
1249
                            provider_id: INTERNAL_PROVIDER_ID,
1✔
1250
                            collection_id: collection1_id.clone(),
1✔
1251
                        },
1✔
1252
                        name: "Collection1".to_string(),
1✔
1253
                        description: "Collection 1".to_string(),
1✔
1254
                        properties: Default::default(),
1✔
1255
                    }),
1✔
1256
                    CollectionItem::Collection(LayerCollectionListing {
1✔
1257
                        id: ProviderLayerCollectionId {
1✔
1258
                            provider_id: INTERNAL_PROVIDER_ID,
1✔
1259
                            collection_id: LayerCollectionId(UNSORTED_COLLECTION_ID.to_string()),
1✔
1260
                        },
1✔
1261
                        name: "Unsorted".to_string(),
1✔
1262
                        description: "Unsorted Layers".to_string(),
1✔
1263
                        properties: Default::default(),
1✔
1264
                    }),
1✔
1265
                    CollectionItem::Layer(LayerListing {
1✔
1266
                        id: ProviderLayerId {
1✔
1267
                            provider_id: INTERNAL_PROVIDER_ID,
1✔
1268
                            layer_id: layer1,
1✔
1269
                        },
1✔
1270
                        name: "Layer1".to_string(),
1✔
1271
                        description: "Layer 1".to_string(),
1✔
1272
                        properties: vec![("proper".to_string(), "tee".to_string()).into()],
1✔
1273
                    })
1✔
1274
                ],
1✔
1275
                entry_label: None,
1✔
1276
                properties: vec![],
1✔
1277
            }
1✔
1278
        );
1✔
1279

1280
        let collection1 = layer_db
1✔
1281
            .load_layer_collection(
1✔
1282
                &collection1_id,
1✔
1283
                LayerCollectionListOptions {
1✔
1284
                    offset: 0,
1✔
1285
                    limit: 20,
1✔
1286
                },
1✔
1287
            )
1✔
1288
            .await
1✔
1289
            .unwrap();
1✔
1290

1✔
1291
        assert_eq!(
1✔
1292
            collection1,
1✔
1293
            LayerCollection {
1✔
1294
                id: ProviderLayerCollectionId {
1✔
1295
                    provider_id: INTERNAL_PROVIDER_ID,
1✔
1296
                    collection_id: collection1_id,
1✔
1297
                },
1✔
1298
                name: "Collection1".to_string(),
1✔
1299
                description: "Collection 1".to_string(),
1✔
1300
                items: vec![
1✔
1301
                    CollectionItem::Collection(LayerCollectionListing {
1✔
1302
                        id: ProviderLayerCollectionId {
1✔
1303
                            provider_id: INTERNAL_PROVIDER_ID,
1✔
1304
                            collection_id: collection2_id,
1✔
1305
                        },
1✔
1306
                        name: "Collection2".to_string(),
1✔
1307
                        description: "Collection 2".to_string(),
1✔
1308
                        properties: Default::default(),
1✔
1309
                    }),
1✔
1310
                    CollectionItem::Layer(LayerListing {
1✔
1311
                        id: ProviderLayerId {
1✔
1312
                            provider_id: INTERNAL_PROVIDER_ID,
1✔
1313
                            layer_id: layer2,
1✔
1314
                        },
1✔
1315
                        name: "Layer2".to_string(),
1✔
1316
                        description: "Layer 2".to_string(),
1✔
1317
                        properties: vec![],
1✔
1318
                    })
1✔
1319
                ],
1✔
1320
                entry_label: None,
1✔
1321
                properties: vec![],
1✔
1322
            }
1✔
1323
        );
1✔
1324
    }
1✔
1325

1326
    #[allow(clippy::too_many_lines)]
1327
    #[ge_context::test]
2✔
1328
    async fn it_searches_layers(app_ctx: PostgresContext<NoTls>) {
1✔
1329
        let session = app_ctx.default_session().await.unwrap();
1✔
1330

1✔
1331
        let layer_db = app_ctx.session_context(session).db();
1✔
1332

1✔
1333
        let workflow = Workflow {
1✔
1334
            operator: TypedOperator::Vector(
1✔
1335
                MockPointSource {
1✔
1336
                    params: MockPointSourceParams {
1✔
1337
                        points: vec![Coordinate2D::new(1., 2.); 3],
1✔
1338
                    },
1✔
1339
                }
1✔
1340
                .boxed(),
1✔
1341
            ),
1✔
1342
        };
1✔
1343

1344
        let root_collection_id = layer_db.get_root_layer_collection_id().await.unwrap();
1✔
1345

1346
        let layer1 = layer_db
1✔
1347
            .add_layer(
1✔
1348
                AddLayer {
1✔
1349
                    name: "Layer1".to_string(),
1✔
1350
                    description: "Layer 1".to_string(),
1✔
1351
                    symbology: None,
1✔
1352
                    workflow: workflow.clone(),
1✔
1353
                    metadata: [("meta".to_string(), "datum".to_string())].into(),
1✔
1354
                    properties: vec![("proper".to_string(), "tee".to_string()).into()],
1✔
1355
                },
1✔
1356
                &root_collection_id,
1✔
1357
            )
1✔
1358
            .await
1✔
1359
            .unwrap();
1✔
1360

1361
        let collection1_id = layer_db
1✔
1362
            .add_layer_collection(
1✔
1363
                AddLayerCollection {
1✔
1364
                    name: "Collection1".to_string(),
1✔
1365
                    description: "Collection 1".to_string(),
1✔
1366
                    properties: Default::default(),
1✔
1367
                },
1✔
1368
                &root_collection_id,
1✔
1369
            )
1✔
1370
            .await
1✔
1371
            .unwrap();
1✔
1372

1373
        let layer2 = layer_db
1✔
1374
            .add_layer(
1✔
1375
                AddLayer {
1✔
1376
                    name: "Layer2".to_string(),
1✔
1377
                    description: "Layer 2".to_string(),
1✔
1378
                    symbology: None,
1✔
1379
                    workflow: workflow.clone(),
1✔
1380
                    metadata: Default::default(),
1✔
1381
                    properties: Default::default(),
1✔
1382
                },
1✔
1383
                &collection1_id,
1✔
1384
            )
1✔
1385
            .await
1✔
1386
            .unwrap();
1✔
1387

1388
        let collection2_id = layer_db
1✔
1389
            .add_layer_collection(
1✔
1390
                AddLayerCollection {
1✔
1391
                    name: "Collection2".to_string(),
1✔
1392
                    description: "Collection 2".to_string(),
1✔
1393
                    properties: Default::default(),
1✔
1394
                },
1✔
1395
                &collection1_id,
1✔
1396
            )
1✔
1397
            .await
1✔
1398
            .unwrap();
1✔
1399

1400
        let root_collection_all = layer_db
1✔
1401
            .search(
1✔
1402
                &root_collection_id,
1✔
1403
                SearchParameters {
1✔
1404
                    search_type: SearchType::Fulltext,
1✔
1405
                    search_string: String::new(),
1✔
1406
                    limit: 10,
1✔
1407
                    offset: 0,
1✔
1408
                },
1✔
1409
            )
1✔
1410
            .await
1✔
1411
            .unwrap();
1✔
1412

1✔
1413
        assert_eq!(
1✔
1414
            root_collection_all,
1✔
1415
            LayerCollection {
1✔
1416
                id: ProviderLayerCollectionId {
1✔
1417
                    provider_id: INTERNAL_PROVIDER_ID,
1✔
1418
                    collection_id: root_collection_id.clone(),
1✔
1419
                },
1✔
1420
                name: "Layers".to_string(),
1✔
1421
                description: "All available Geo Engine layers".to_string(),
1✔
1422
                items: vec![
1✔
1423
                    CollectionItem::Collection(LayerCollectionListing {
1✔
1424
                        id: ProviderLayerCollectionId {
1✔
1425
                            provider_id: INTERNAL_PROVIDER_ID,
1✔
1426
                            collection_id: collection1_id.clone(),
1✔
1427
                        },
1✔
1428
                        name: "Collection1".to_string(),
1✔
1429
                        description: "Collection 1".to_string(),
1✔
1430
                        properties: Default::default(),
1✔
1431
                    }),
1✔
1432
                    CollectionItem::Collection(LayerCollectionListing {
1✔
1433
                        id: ProviderLayerCollectionId {
1✔
1434
                            provider_id: INTERNAL_PROVIDER_ID,
1✔
1435
                            collection_id: collection2_id.clone(),
1✔
1436
                        },
1✔
1437
                        name: "Collection2".to_string(),
1✔
1438
                        description: "Collection 2".to_string(),
1✔
1439
                        properties: Default::default(),
1✔
1440
                    }),
1✔
1441
                    CollectionItem::Collection(LayerCollectionListing {
1✔
1442
                        id: ProviderLayerCollectionId {
1✔
1443
                            provider_id: INTERNAL_PROVIDER_ID,
1✔
1444
                            collection_id: LayerCollectionId(
1✔
1445
                                "ffb2dd9e-f5ad-427c-b7f1-c9a0c7a0ae3f".to_string()
1✔
1446
                            ),
1✔
1447
                        },
1✔
1448
                        name: "Unsorted".to_string(),
1✔
1449
                        description: "Unsorted Layers".to_string(),
1✔
1450
                        properties: Default::default(),
1✔
1451
                    }),
1✔
1452
                    CollectionItem::Layer(LayerListing {
1✔
1453
                        id: ProviderLayerId {
1✔
1454
                            provider_id: INTERNAL_PROVIDER_ID,
1✔
1455
                            layer_id: layer1.clone(),
1✔
1456
                        },
1✔
1457
                        name: "Layer1".to_string(),
1✔
1458
                        description: "Layer 1".to_string(),
1✔
1459
                        properties: vec![("proper".to_string(), "tee".to_string()).into()],
1✔
1460
                    }),
1✔
1461
                    CollectionItem::Layer(LayerListing {
1✔
1462
                        id: ProviderLayerId {
1✔
1463
                            provider_id: INTERNAL_PROVIDER_ID,
1✔
1464
                            layer_id: layer2.clone(),
1✔
1465
                        },
1✔
1466
                        name: "Layer2".to_string(),
1✔
1467
                        description: "Layer 2".to_string(),
1✔
1468
                        properties: vec![],
1✔
1469
                    }),
1✔
1470
                ],
1✔
1471
                entry_label: None,
1✔
1472
                properties: vec![],
1✔
1473
            }
1✔
1474
        );
1✔
1475

1476
        let root_collection_filtered = layer_db
1✔
1477
            .search(
1✔
1478
                &root_collection_id,
1✔
1479
                SearchParameters {
1✔
1480
                    search_type: SearchType::Fulltext,
1✔
1481
                    search_string: "lection".to_string(),
1✔
1482
                    limit: 10,
1✔
1483
                    offset: 0,
1✔
1484
                },
1✔
1485
            )
1✔
1486
            .await
1✔
1487
            .unwrap();
1✔
1488

1✔
1489
        assert_eq!(
1✔
1490
            root_collection_filtered,
1✔
1491
            LayerCollection {
1✔
1492
                id: ProviderLayerCollectionId {
1✔
1493
                    provider_id: INTERNAL_PROVIDER_ID,
1✔
1494
                    collection_id: root_collection_id.clone(),
1✔
1495
                },
1✔
1496
                name: "Layers".to_string(),
1✔
1497
                description: "All available Geo Engine layers".to_string(),
1✔
1498
                items: vec![
1✔
1499
                    CollectionItem::Collection(LayerCollectionListing {
1✔
1500
                        id: ProviderLayerCollectionId {
1✔
1501
                            provider_id: INTERNAL_PROVIDER_ID,
1✔
1502
                            collection_id: collection1_id.clone(),
1✔
1503
                        },
1✔
1504
                        name: "Collection1".to_string(),
1✔
1505
                        description: "Collection 1".to_string(),
1✔
1506
                        properties: Default::default(),
1✔
1507
                    }),
1✔
1508
                    CollectionItem::Collection(LayerCollectionListing {
1✔
1509
                        id: ProviderLayerCollectionId {
1✔
1510
                            provider_id: INTERNAL_PROVIDER_ID,
1✔
1511
                            collection_id: collection2_id.clone(),
1✔
1512
                        },
1✔
1513
                        name: "Collection2".to_string(),
1✔
1514
                        description: "Collection 2".to_string(),
1✔
1515
                        properties: Default::default(),
1✔
1516
                    }),
1✔
1517
                ],
1✔
1518
                entry_label: None,
1✔
1519
                properties: vec![],
1✔
1520
            }
1✔
1521
        );
1✔
1522

1523
        let collection1_all = layer_db
1✔
1524
            .search(
1✔
1525
                &collection1_id,
1✔
1526
                SearchParameters {
1✔
1527
                    search_type: SearchType::Fulltext,
1✔
1528
                    search_string: String::new(),
1✔
1529
                    limit: 10,
1✔
1530
                    offset: 0,
1✔
1531
                },
1✔
1532
            )
1✔
1533
            .await
1✔
1534
            .unwrap();
1✔
1535

1✔
1536
        assert_eq!(
1✔
1537
            collection1_all,
1✔
1538
            LayerCollection {
1✔
1539
                id: ProviderLayerCollectionId {
1✔
1540
                    provider_id: INTERNAL_PROVIDER_ID,
1✔
1541
                    collection_id: collection1_id.clone(),
1✔
1542
                },
1✔
1543
                name: "Collection1".to_string(),
1✔
1544
                description: "Collection 1".to_string(),
1✔
1545
                items: vec![
1✔
1546
                    CollectionItem::Collection(LayerCollectionListing {
1✔
1547
                        id: ProviderLayerCollectionId {
1✔
1548
                            provider_id: INTERNAL_PROVIDER_ID,
1✔
1549
                            collection_id: collection2_id.clone(),
1✔
1550
                        },
1✔
1551
                        name: "Collection2".to_string(),
1✔
1552
                        description: "Collection 2".to_string(),
1✔
1553
                        properties: Default::default(),
1✔
1554
                    }),
1✔
1555
                    CollectionItem::Layer(LayerListing {
1✔
1556
                        id: ProviderLayerId {
1✔
1557
                            provider_id: INTERNAL_PROVIDER_ID,
1✔
1558
                            layer_id: layer2.clone(),
1✔
1559
                        },
1✔
1560
                        name: "Layer2".to_string(),
1✔
1561
                        description: "Layer 2".to_string(),
1✔
1562
                        properties: vec![],
1✔
1563
                    }),
1✔
1564
                ],
1✔
1565
                entry_label: None,
1✔
1566
                properties: vec![],
1✔
1567
            }
1✔
1568
        );
1✔
1569

1570
        let collection1_filtered_fulltext = layer_db
1✔
1571
            .search(
1✔
1572
                &collection1_id,
1✔
1573
                SearchParameters {
1✔
1574
                    search_type: SearchType::Fulltext,
1✔
1575
                    search_string: "ay".to_string(),
1✔
1576
                    limit: 10,
1✔
1577
                    offset: 0,
1✔
1578
                },
1✔
1579
            )
1✔
1580
            .await
1✔
1581
            .unwrap();
1✔
1582

1✔
1583
        assert_eq!(
1✔
1584
            collection1_filtered_fulltext,
1✔
1585
            LayerCollection {
1✔
1586
                id: ProviderLayerCollectionId {
1✔
1587
                    provider_id: INTERNAL_PROVIDER_ID,
1✔
1588
                    collection_id: collection1_id.clone(),
1✔
1589
                },
1✔
1590
                name: "Collection1".to_string(),
1✔
1591
                description: "Collection 1".to_string(),
1✔
1592
                items: vec![CollectionItem::Layer(LayerListing {
1✔
1593
                    id: ProviderLayerId {
1✔
1594
                        provider_id: INTERNAL_PROVIDER_ID,
1✔
1595
                        layer_id: layer2.clone(),
1✔
1596
                    },
1✔
1597
                    name: "Layer2".to_string(),
1✔
1598
                    description: "Layer 2".to_string(),
1✔
1599
                    properties: vec![],
1✔
1600
                }),],
1✔
1601
                entry_label: None,
1✔
1602
                properties: vec![],
1✔
1603
            }
1✔
1604
        );
1✔
1605

1606
        let collection1_filtered_prefix = layer_db
1✔
1607
            .search(
1✔
1608
                &collection1_id,
1✔
1609
                SearchParameters {
1✔
1610
                    search_type: SearchType::Prefix,
1✔
1611
                    search_string: "ay".to_string(),
1✔
1612
                    limit: 10,
1✔
1613
                    offset: 0,
1✔
1614
                },
1✔
1615
            )
1✔
1616
            .await
1✔
1617
            .unwrap();
1✔
1618

1✔
1619
        assert_eq!(
1✔
1620
            collection1_filtered_prefix,
1✔
1621
            LayerCollection {
1✔
1622
                id: ProviderLayerCollectionId {
1✔
1623
                    provider_id: INTERNAL_PROVIDER_ID,
1✔
1624
                    collection_id: collection1_id.clone(),
1✔
1625
                },
1✔
1626
                name: "Collection1".to_string(),
1✔
1627
                description: "Collection 1".to_string(),
1✔
1628
                items: vec![],
1✔
1629
                entry_label: None,
1✔
1630
                properties: vec![],
1✔
1631
            }
1✔
1632
        );
1✔
1633

1634
        let collection1_filtered_prefix2 = layer_db
1✔
1635
            .search(
1✔
1636
                &collection1_id,
1✔
1637
                SearchParameters {
1✔
1638
                    search_type: SearchType::Prefix,
1✔
1639
                    search_string: "Lay".to_string(),
1✔
1640
                    limit: 10,
1✔
1641
                    offset: 0,
1✔
1642
                },
1✔
1643
            )
1✔
1644
            .await
1✔
1645
            .unwrap();
1✔
1646

1✔
1647
        assert_eq!(
1✔
1648
            collection1_filtered_prefix2,
1✔
1649
            LayerCollection {
1✔
1650
                id: ProviderLayerCollectionId {
1✔
1651
                    provider_id: INTERNAL_PROVIDER_ID,
1✔
1652
                    collection_id: collection1_id.clone(),
1✔
1653
                },
1✔
1654
                name: "Collection1".to_string(),
1✔
1655
                description: "Collection 1".to_string(),
1✔
1656
                items: vec![CollectionItem::Layer(LayerListing {
1✔
1657
                    id: ProviderLayerId {
1✔
1658
                        provider_id: INTERNAL_PROVIDER_ID,
1✔
1659
                        layer_id: layer2.clone(),
1✔
1660
                    },
1✔
1661
                    name: "Layer2".to_string(),
1✔
1662
                    description: "Layer 2".to_string(),
1✔
1663
                    properties: vec![],
1✔
1664
                }),],
1✔
1665
                entry_label: None,
1✔
1666
                properties: vec![],
1✔
1667
            }
1✔
1668
        );
1✔
1669
    }
1✔
1670

1671
    #[allow(clippy::too_many_lines)]
1672
    #[ge_context::test]
2✔
1673
    async fn it_autocompletes_layers(app_ctx: PostgresContext<NoTls>) {
1✔
1674
        let session = app_ctx.default_session().await.unwrap();
1✔
1675

1✔
1676
        let layer_db = app_ctx.session_context(session).db();
1✔
1677

1✔
1678
        let workflow = Workflow {
1✔
1679
            operator: TypedOperator::Vector(
1✔
1680
                MockPointSource {
1✔
1681
                    params: MockPointSourceParams {
1✔
1682
                        points: vec![Coordinate2D::new(1., 2.); 3],
1✔
1683
                    },
1✔
1684
                }
1✔
1685
                .boxed(),
1✔
1686
            ),
1✔
1687
        };
1✔
1688

1689
        let root_collection_id = layer_db.get_root_layer_collection_id().await.unwrap();
1✔
1690

1691
        let _layer1 = layer_db
1✔
1692
            .add_layer(
1✔
1693
                AddLayer {
1✔
1694
                    name: "Layer1".to_string(),
1✔
1695
                    description: "Layer 1".to_string(),
1✔
1696
                    symbology: None,
1✔
1697
                    workflow: workflow.clone(),
1✔
1698
                    metadata: [("meta".to_string(), "datum".to_string())].into(),
1✔
1699
                    properties: vec![("proper".to_string(), "tee".to_string()).into()],
1✔
1700
                },
1✔
1701
                &root_collection_id,
1✔
1702
            )
1✔
1703
            .await
1✔
1704
            .unwrap();
1✔
1705

1706
        let collection1_id = layer_db
1✔
1707
            .add_layer_collection(
1✔
1708
                AddLayerCollection {
1✔
1709
                    name: "Collection1".to_string(),
1✔
1710
                    description: "Collection 1".to_string(),
1✔
1711
                    properties: Default::default(),
1✔
1712
                },
1✔
1713
                &root_collection_id,
1✔
1714
            )
1✔
1715
            .await
1✔
1716
            .unwrap();
1✔
1717

1718
        let _layer2 = layer_db
1✔
1719
            .add_layer(
1✔
1720
                AddLayer {
1✔
1721
                    name: "Layer2".to_string(),
1✔
1722
                    description: "Layer 2".to_string(),
1✔
1723
                    symbology: None,
1✔
1724
                    workflow: workflow.clone(),
1✔
1725
                    metadata: Default::default(),
1✔
1726
                    properties: Default::default(),
1✔
1727
                },
1✔
1728
                &collection1_id,
1✔
1729
            )
1✔
1730
            .await
1✔
1731
            .unwrap();
1✔
1732

1733
        let _collection2_id = layer_db
1✔
1734
            .add_layer_collection(
1✔
1735
                AddLayerCollection {
1✔
1736
                    name: "Collection2".to_string(),
1✔
1737
                    description: "Collection 2".to_string(),
1✔
1738
                    properties: Default::default(),
1✔
1739
                },
1✔
1740
                &collection1_id,
1✔
1741
            )
1✔
1742
            .await
1✔
1743
            .unwrap();
1✔
1744

1745
        let root_collection_all = layer_db
1✔
1746
            .autocomplete_search(
1✔
1747
                &root_collection_id,
1✔
1748
                SearchParameters {
1✔
1749
                    search_type: SearchType::Fulltext,
1✔
1750
                    search_string: String::new(),
1✔
1751
                    limit: 10,
1✔
1752
                    offset: 0,
1✔
1753
                },
1✔
1754
            )
1✔
1755
            .await
1✔
1756
            .unwrap();
1✔
1757

1✔
1758
        assert_eq!(
1✔
1759
            root_collection_all,
1✔
1760
            vec![
1✔
1761
                "Collection1".to_string(),
1✔
1762
                "Collection2".to_string(),
1✔
1763
                "Layer1".to_string(),
1✔
1764
                "Layer2".to_string(),
1✔
1765
                "Unsorted".to_string(),
1✔
1766
            ]
1✔
1767
        );
1✔
1768

1769
        let root_collection_filtered = layer_db
1✔
1770
            .autocomplete_search(
1✔
1771
                &root_collection_id,
1✔
1772
                SearchParameters {
1✔
1773
                    search_type: SearchType::Fulltext,
1✔
1774
                    search_string: "lection".to_string(),
1✔
1775
                    limit: 10,
1✔
1776
                    offset: 0,
1✔
1777
                },
1✔
1778
            )
1✔
1779
            .await
1✔
1780
            .unwrap();
1✔
1781

1✔
1782
        assert_eq!(
1✔
1783
            root_collection_filtered,
1✔
1784
            vec!["Collection1".to_string(), "Collection2".to_string(),]
1✔
1785
        );
1✔
1786

1787
        let collection1_all = layer_db
1✔
1788
            .autocomplete_search(
1✔
1789
                &collection1_id,
1✔
1790
                SearchParameters {
1✔
1791
                    search_type: SearchType::Fulltext,
1✔
1792
                    search_string: String::new(),
1✔
1793
                    limit: 10,
1✔
1794
                    offset: 0,
1✔
1795
                },
1✔
1796
            )
1✔
1797
            .await
1✔
1798
            .unwrap();
1✔
1799

1✔
1800
        assert_eq!(
1✔
1801
            collection1_all,
1✔
1802
            vec!["Collection2".to_string(), "Layer2".to_string(),]
1✔
1803
        );
1✔
1804

1805
        let collection1_filtered_fulltext = layer_db
1✔
1806
            .autocomplete_search(
1✔
1807
                &collection1_id,
1✔
1808
                SearchParameters {
1✔
1809
                    search_type: SearchType::Fulltext,
1✔
1810
                    search_string: "ay".to_string(),
1✔
1811
                    limit: 10,
1✔
1812
                    offset: 0,
1✔
1813
                },
1✔
1814
            )
1✔
1815
            .await
1✔
1816
            .unwrap();
1✔
1817

1✔
1818
        assert_eq!(collection1_filtered_fulltext, vec!["Layer2".to_string(),]);
1✔
1819

1820
        let collection1_filtered_prefix = layer_db
1✔
1821
            .autocomplete_search(
1✔
1822
                &collection1_id,
1✔
1823
                SearchParameters {
1✔
1824
                    search_type: SearchType::Prefix,
1✔
1825
                    search_string: "ay".to_string(),
1✔
1826
                    limit: 10,
1✔
1827
                    offset: 0,
1✔
1828
                },
1✔
1829
            )
1✔
1830
            .await
1✔
1831
            .unwrap();
1✔
1832

1✔
1833
        assert_eq!(collection1_filtered_prefix, Vec::<String>::new());
1✔
1834

1835
        let collection1_filtered_prefix2 = layer_db
1✔
1836
            .autocomplete_search(
1✔
1837
                &collection1_id,
1✔
1838
                SearchParameters {
1✔
1839
                    search_type: SearchType::Prefix,
1✔
1840
                    search_string: "Lay".to_string(),
1✔
1841
                    limit: 10,
1✔
1842
                    offset: 0,
1✔
1843
                },
1✔
1844
            )
1✔
1845
            .await
1✔
1846
            .unwrap();
1✔
1847

1✔
1848
        assert_eq!(collection1_filtered_prefix2, vec!["Layer2".to_string(),]);
1✔
1849
    }
1✔
1850

1851
    #[allow(clippy::too_many_lines)]
1852
    #[ge_context::test]
2✔
1853
    async fn it_reports_search_capabilities(app_ctx: PostgresContext<NoTls>) {
1✔
1854
        let session = app_ctx.default_session().await.unwrap();
1✔
1855

1✔
1856
        let layer_db = app_ctx.session_context(session).db();
1✔
1857

1✔
1858
        let capabilities = layer_db.capabilities().search;
1✔
1859

1860
        let root_collection_id = layer_db.get_root_layer_collection_id().await.unwrap();
1✔
1861

1✔
1862
        if capabilities.search_types.fulltext {
1✔
1863
            assert!(layer_db
1✔
1864
                .search(
1✔
1865
                    &root_collection_id,
1✔
1866
                    SearchParameters {
1✔
1867
                        search_type: SearchType::Fulltext,
1✔
1868
                        search_string: String::new(),
1✔
1869
                        limit: 10,
1✔
1870
                        offset: 0,
1✔
1871
                    },
1✔
1872
                )
1✔
1873
                .await
1✔
1874
                .is_ok());
1✔
1875

1876
            if capabilities.autocomplete {
1✔
1877
                assert!(layer_db
1✔
1878
                    .autocomplete_search(
1✔
1879
                        &root_collection_id,
1✔
1880
                        SearchParameters {
1✔
1881
                            search_type: SearchType::Fulltext,
1✔
1882
                            search_string: String::new(),
1✔
1883
                            limit: 10,
1✔
1884
                            offset: 0,
1✔
1885
                        },
1✔
1886
                    )
1✔
1887
                    .await
1✔
1888
                    .is_ok());
1✔
1889
            } else {
1890
                assert!(layer_db
×
1891
                    .autocomplete_search(
×
1892
                        &root_collection_id,
×
1893
                        SearchParameters {
×
1894
                            search_type: SearchType::Fulltext,
×
1895
                            search_string: String::new(),
×
1896
                            limit: 10,
×
1897
                            offset: 0,
×
1898
                        },
×
1899
                    )
×
1900
                    .await
×
1901
                    .is_err());
×
1902
            }
1903
        }
×
1904
        if capabilities.search_types.prefix {
1✔
1905
            assert!(layer_db
1✔
1906
                .search(
1✔
1907
                    &root_collection_id,
1✔
1908
                    SearchParameters {
1✔
1909
                        search_type: SearchType::Prefix,
1✔
1910
                        search_string: String::new(),
1✔
1911
                        limit: 10,
1✔
1912
                        offset: 0,
1✔
1913
                    },
1✔
1914
                )
1✔
1915
                .await
1✔
1916
                .is_ok());
1✔
1917

1918
            if capabilities.autocomplete {
1✔
1919
                assert!(layer_db
1✔
1920
                    .autocomplete_search(
1✔
1921
                        &root_collection_id,
1✔
1922
                        SearchParameters {
1✔
1923
                            search_type: SearchType::Prefix,
1✔
1924
                            search_string: String::new(),
1✔
1925
                            limit: 10,
1✔
1926
                            offset: 0,
1✔
1927
                        },
1✔
1928
                    )
1✔
1929
                    .await
1✔
1930
                    .is_ok());
1✔
1931
            } else {
1932
                assert!(layer_db
×
1933
                    .autocomplete_search(
×
1934
                        &root_collection_id,
×
1935
                        SearchParameters {
×
1936
                            search_type: SearchType::Prefix,
×
1937
                            search_string: String::new(),
×
1938
                            limit: 10,
×
1939
                            offset: 0,
×
1940
                        },
×
1941
                    )
×
1942
                    .await
×
1943
                    .is_err());
×
1944
            }
1945
        }
×
1946
    }
1✔
1947

1948
    #[allow(clippy::too_many_lines)]
1949
    #[ge_context::test]
2✔
1950
    async fn it_removes_layer_collections(app_ctx: PostgresContext<NoTls>) {
1✔
1951
        let session = app_ctx.default_session().await.unwrap();
1✔
1952

1✔
1953
        let layer_db = app_ctx.session_context(session).db();
1✔
1954

1✔
1955
        let layer = AddLayer {
1✔
1956
            name: "layer".to_string(),
1✔
1957
            description: "description".to_string(),
1✔
1958
            workflow: Workflow {
1✔
1959
                operator: TypedOperator::Vector(
1✔
1960
                    MockPointSource {
1✔
1961
                        params: MockPointSourceParams {
1✔
1962
                            points: vec![Coordinate2D::new(1., 2.); 3],
1✔
1963
                        },
1✔
1964
                    }
1✔
1965
                    .boxed(),
1✔
1966
                ),
1✔
1967
            },
1✔
1968
            symbology: None,
1✔
1969
            metadata: Default::default(),
1✔
1970
            properties: Default::default(),
1✔
1971
        };
1✔
1972

1973
        let root_collection = &layer_db.get_root_layer_collection_id().await.unwrap();
1✔
1974

1✔
1975
        let collection = AddLayerCollection {
1✔
1976
            name: "top collection".to_string(),
1✔
1977
            description: "description".to_string(),
1✔
1978
            properties: Default::default(),
1✔
1979
        };
1✔
1980

1981
        let top_c_id = layer_db
1✔
1982
            .add_layer_collection(collection, root_collection)
1✔
1983
            .await
1✔
1984
            .unwrap();
1✔
1985

1986
        let l_id = layer_db.add_layer(layer, &top_c_id).await.unwrap();
1✔
1987

1✔
1988
        let collection = AddLayerCollection {
1✔
1989
            name: "empty collection".to_string(),
1✔
1990
            description: "description".to_string(),
1✔
1991
            properties: Default::default(),
1✔
1992
        };
1✔
1993

1994
        let empty_c_id = layer_db
1✔
1995
            .add_layer_collection(collection, &top_c_id)
1✔
1996
            .await
1✔
1997
            .unwrap();
1✔
1998

1999
        let items = layer_db
1✔
2000
            .load_layer_collection(
1✔
2001
                &top_c_id,
1✔
2002
                LayerCollectionListOptions {
1✔
2003
                    offset: 0,
1✔
2004
                    limit: 20,
1✔
2005
                },
1✔
2006
            )
1✔
2007
            .await
1✔
2008
            .unwrap();
1✔
2009

1✔
2010
        assert_eq!(
1✔
2011
            items,
1✔
2012
            LayerCollection {
1✔
2013
                id: ProviderLayerCollectionId {
1✔
2014
                    provider_id: INTERNAL_PROVIDER_ID,
1✔
2015
                    collection_id: top_c_id.clone(),
1✔
2016
                },
1✔
2017
                name: "top collection".to_string(),
1✔
2018
                description: "description".to_string(),
1✔
2019
                items: vec![
1✔
2020
                    CollectionItem::Collection(LayerCollectionListing {
1✔
2021
                        id: ProviderLayerCollectionId {
1✔
2022
                            provider_id: INTERNAL_PROVIDER_ID,
1✔
2023
                            collection_id: empty_c_id.clone(),
1✔
2024
                        },
1✔
2025
                        name: "empty collection".to_string(),
1✔
2026
                        description: "description".to_string(),
1✔
2027
                        properties: Default::default(),
1✔
2028
                    }),
1✔
2029
                    CollectionItem::Layer(LayerListing {
1✔
2030
                        id: ProviderLayerId {
1✔
2031
                            provider_id: INTERNAL_PROVIDER_ID,
1✔
2032
                            layer_id: l_id.clone(),
1✔
2033
                        },
1✔
2034
                        name: "layer".to_string(),
1✔
2035
                        description: "description".to_string(),
1✔
2036
                        properties: vec![],
1✔
2037
                    })
1✔
2038
                ],
1✔
2039
                entry_label: None,
1✔
2040
                properties: vec![],
1✔
2041
            }
1✔
2042
        );
1✔
2043

2044
        // remove empty collection
2045
        layer_db.remove_layer_collection(&empty_c_id).await.unwrap();
1✔
2046

2047
        let items = layer_db
1✔
2048
            .load_layer_collection(
1✔
2049
                &top_c_id,
1✔
2050
                LayerCollectionListOptions {
1✔
2051
                    offset: 0,
1✔
2052
                    limit: 20,
1✔
2053
                },
1✔
2054
            )
1✔
2055
            .await
1✔
2056
            .unwrap();
1✔
2057

1✔
2058
        assert_eq!(
1✔
2059
            items,
1✔
2060
            LayerCollection {
1✔
2061
                id: ProviderLayerCollectionId {
1✔
2062
                    provider_id: INTERNAL_PROVIDER_ID,
1✔
2063
                    collection_id: top_c_id.clone(),
1✔
2064
                },
1✔
2065
                name: "top collection".to_string(),
1✔
2066
                description: "description".to_string(),
1✔
2067
                items: vec![CollectionItem::Layer(LayerListing {
1✔
2068
                    id: ProviderLayerId {
1✔
2069
                        provider_id: INTERNAL_PROVIDER_ID,
1✔
2070
                        layer_id: l_id.clone(),
1✔
2071
                    },
1✔
2072
                    name: "layer".to_string(),
1✔
2073
                    description: "description".to_string(),
1✔
2074
                    properties: vec![],
1✔
2075
                })],
1✔
2076
                entry_label: None,
1✔
2077
                properties: vec![],
1✔
2078
            }
1✔
2079
        );
1✔
2080

2081
        // remove top (not root) collection
2082
        layer_db.remove_layer_collection(&top_c_id).await.unwrap();
1✔
2083

1✔
2084
        layer_db
1✔
2085
            .load_layer_collection(
1✔
2086
                &top_c_id,
1✔
2087
                LayerCollectionListOptions {
1✔
2088
                    offset: 0,
1✔
2089
                    limit: 20,
1✔
2090
                },
1✔
2091
            )
1✔
2092
            .await
1✔
2093
            .unwrap_err();
1✔
2094

1✔
2095
        // should be deleted automatically
1✔
2096
        layer_db.load_layer(&l_id).await.unwrap_err();
1✔
2097

1✔
2098
        // it is not allowed to remove the root collection
1✔
2099
        layer_db
1✔
2100
            .remove_layer_collection(root_collection)
1✔
2101
            .await
1✔
2102
            .unwrap_err();
1✔
2103
        layer_db
1✔
2104
            .load_layer_collection(
1✔
2105
                root_collection,
1✔
2106
                LayerCollectionListOptions {
1✔
2107
                    offset: 0,
1✔
2108
                    limit: 20,
1✔
2109
                },
1✔
2110
            )
1✔
2111
            .await
1✔
2112
            .unwrap();
1✔
2113
    }
1✔
2114

2115
    #[ge_context::test]
2✔
2116
    #[allow(clippy::too_many_lines)]
2117
    async fn it_removes_collections_from_collections(app_ctx: PostgresContext<NoTls>) {
1✔
2118
        let session = app_ctx.default_session().await.unwrap();
1✔
2119

1✔
2120
        let db = app_ctx.session_context(session).db();
1✔
2121

2122
        let root_collection_id = &db.get_root_layer_collection_id().await.unwrap();
1✔
2123

2124
        let mid_collection_id = db
1✔
2125
            .add_layer_collection(
1✔
2126
                AddLayerCollection {
1✔
2127
                    name: "mid collection".to_string(),
1✔
2128
                    description: "description".to_string(),
1✔
2129
                    properties: Default::default(),
1✔
2130
                },
1✔
2131
                root_collection_id,
1✔
2132
            )
1✔
2133
            .await
1✔
2134
            .unwrap();
1✔
2135

2136
        let bottom_collection_id = db
1✔
2137
            .add_layer_collection(
1✔
2138
                AddLayerCollection {
1✔
2139
                    name: "bottom collection".to_string(),
1✔
2140
                    description: "description".to_string(),
1✔
2141
                    properties: Default::default(),
1✔
2142
                },
1✔
2143
                &mid_collection_id,
1✔
2144
            )
1✔
2145
            .await
1✔
2146
            .unwrap();
1✔
2147

2148
        let layer_id = db
1✔
2149
            .add_layer(
1✔
2150
                AddLayer {
1✔
2151
                    name: "layer".to_string(),
1✔
2152
                    description: "description".to_string(),
1✔
2153
                    workflow: Workflow {
1✔
2154
                        operator: TypedOperator::Vector(
1✔
2155
                            MockPointSource {
1✔
2156
                                params: MockPointSourceParams {
1✔
2157
                                    points: vec![Coordinate2D::new(1., 2.); 3],
1✔
2158
                                },
1✔
2159
                            }
1✔
2160
                            .boxed(),
1✔
2161
                        ),
1✔
2162
                    },
1✔
2163
                    symbology: None,
1✔
2164
                    metadata: Default::default(),
1✔
2165
                    properties: Default::default(),
1✔
2166
                },
1✔
2167
                &mid_collection_id,
1✔
2168
            )
1✔
2169
            .await
1✔
2170
            .unwrap();
1✔
2171

1✔
2172
        // removing the mid collection…
1✔
2173
        db.remove_layer_collection_from_parent(&mid_collection_id, root_collection_id)
1✔
2174
            .await
1✔
2175
            .unwrap();
1✔
2176

1✔
2177
        // …should remove itself
1✔
2178
        db.load_layer_collection(&mid_collection_id, LayerCollectionListOptions::default())
1✔
2179
            .await
1✔
2180
            .unwrap_err();
1✔
2181

1✔
2182
        // …should remove the bottom collection
1✔
2183
        db.load_layer_collection(&bottom_collection_id, LayerCollectionListOptions::default())
1✔
2184
            .await
1✔
2185
            .unwrap_err();
1✔
2186

1✔
2187
        // … and should remove the layer of the bottom collection
1✔
2188
        db.load_layer(&layer_id).await.unwrap_err();
1✔
2189

1✔
2190
        // the root collection is still there
1✔
2191
        db.load_layer_collection(root_collection_id, LayerCollectionListOptions::default())
1✔
2192
            .await
1✔
2193
            .unwrap();
1✔
2194
    }
1✔
2195

2196
    #[ge_context::test]
2✔
2197
    #[allow(clippy::too_many_lines)]
2198
    async fn it_removes_layers_from_collections(app_ctx: PostgresContext<NoTls>) {
1✔
2199
        let session = app_ctx.default_session().await.unwrap();
1✔
2200

1✔
2201
        let db = app_ctx.session_context(session).db();
1✔
2202

2203
        let root_collection = &db.get_root_layer_collection_id().await.unwrap();
1✔
2204

2205
        let another_collection = db
1✔
2206
            .add_layer_collection(
1✔
2207
                AddLayerCollection {
1✔
2208
                    name: "top collection".to_string(),
1✔
2209
                    description: "description".to_string(),
1✔
2210
                    properties: Default::default(),
1✔
2211
                },
1✔
2212
                root_collection,
1✔
2213
            )
1✔
2214
            .await
1✔
2215
            .unwrap();
1✔
2216

2217
        let layer_in_one_collection = db
1✔
2218
            .add_layer(
1✔
2219
                AddLayer {
1✔
2220
                    name: "layer 1".to_string(),
1✔
2221
                    description: "description".to_string(),
1✔
2222
                    workflow: Workflow {
1✔
2223
                        operator: TypedOperator::Vector(
1✔
2224
                            MockPointSource {
1✔
2225
                                params: MockPointSourceParams {
1✔
2226
                                    points: vec![Coordinate2D::new(1., 2.); 3],
1✔
2227
                                },
1✔
2228
                            }
1✔
2229
                            .boxed(),
1✔
2230
                        ),
1✔
2231
                    },
1✔
2232
                    symbology: None,
1✔
2233
                    metadata: Default::default(),
1✔
2234
                    properties: Default::default(),
1✔
2235
                },
1✔
2236
                &another_collection,
1✔
2237
            )
1✔
2238
            .await
1✔
2239
            .unwrap();
1✔
2240

2241
        let layer_in_two_collections = db
1✔
2242
            .add_layer(
1✔
2243
                AddLayer {
1✔
2244
                    name: "layer 2".to_string(),
1✔
2245
                    description: "description".to_string(),
1✔
2246
                    workflow: Workflow {
1✔
2247
                        operator: TypedOperator::Vector(
1✔
2248
                            MockPointSource {
1✔
2249
                                params: MockPointSourceParams {
1✔
2250
                                    points: vec![Coordinate2D::new(1., 2.); 3],
1✔
2251
                                },
1✔
2252
                            }
1✔
2253
                            .boxed(),
1✔
2254
                        ),
1✔
2255
                    },
1✔
2256
                    symbology: None,
1✔
2257
                    metadata: Default::default(),
1✔
2258
                    properties: Default::default(),
1✔
2259
                },
1✔
2260
                &another_collection,
1✔
2261
            )
1✔
2262
            .await
1✔
2263
            .unwrap();
1✔
2264

1✔
2265
        db.add_layer_to_collection(&layer_in_two_collections, root_collection)
1✔
2266
            .await
1✔
2267
            .unwrap();
1✔
2268

1✔
2269
        // remove first layer --> should be deleted entirely
1✔
2270

1✔
2271
        db.remove_layer_from_collection(&layer_in_one_collection, &another_collection)
1✔
2272
            .await
1✔
2273
            .unwrap();
1✔
2274

2275
        let number_of_layer_in_collection = db
1✔
2276
            .load_layer_collection(
1✔
2277
                &another_collection,
1✔
2278
                LayerCollectionListOptions {
1✔
2279
                    offset: 0,
1✔
2280
                    limit: 20,
1✔
2281
                },
1✔
2282
            )
1✔
2283
            .await
1✔
2284
            .unwrap()
1✔
2285
            .items
1✔
2286
            .len();
1✔
2287
        assert_eq!(
1✔
2288
            number_of_layer_in_collection,
1✔
2289
            1 /* only the other collection should be here */
1✔
2290
        );
1✔
2291

2292
        db.load_layer(&layer_in_one_collection).await.unwrap_err();
1✔
2293

1✔
2294
        // remove second layer --> should only be gone in collection
1✔
2295

1✔
2296
        db.remove_layer_from_collection(&layer_in_two_collections, &another_collection)
1✔
2297
            .await
1✔
2298
            .unwrap();
1✔
2299

2300
        let number_of_layer_in_collection = db
1✔
2301
            .load_layer_collection(
1✔
2302
                &another_collection,
1✔
2303
                LayerCollectionListOptions {
1✔
2304
                    offset: 0,
1✔
2305
                    limit: 20,
1✔
2306
                },
1✔
2307
            )
1✔
2308
            .await
1✔
2309
            .unwrap()
1✔
2310
            .items
1✔
2311
            .len();
1✔
2312
        assert_eq!(
1✔
2313
            number_of_layer_in_collection,
1✔
2314
            0 /* both layers were deleted */
1✔
2315
        );
1✔
2316

2317
        db.load_layer(&layer_in_two_collections).await.unwrap();
1✔
2318
    }
1✔
2319

2320
    #[ge_context::test]
2✔
2321
    #[allow(clippy::too_many_lines)]
2322
    async fn it_deletes_dataset(app_ctx: PostgresContext<NoTls>) {
1✔
2323
        let loading_info = OgrSourceDataset {
1✔
2324
            file_name: PathBuf::from("test.csv"),
1✔
2325
            layer_name: "test.csv".to_owned(),
1✔
2326
            data_type: Some(VectorDataType::MultiPoint),
1✔
2327
            time: OgrSourceDatasetTimeType::Start {
1✔
2328
                start_field: "start".to_owned(),
1✔
2329
                start_format: OgrSourceTimeFormat::Auto,
1✔
2330
                duration: OgrSourceDurationSpec::Zero,
1✔
2331
            },
1✔
2332
            default_geometry: None,
1✔
2333
            columns: Some(OgrSourceColumnSpec {
1✔
2334
                format_specifics: Some(FormatSpecifics::Csv {
1✔
2335
                    header: CsvHeader::Auto,
1✔
2336
                }),
1✔
2337
                x: "x".to_owned(),
1✔
2338
                y: None,
1✔
2339
                int: vec![],
1✔
2340
                float: vec![],
1✔
2341
                text: vec![],
1✔
2342
                bool: vec![],
1✔
2343
                datetime: vec![],
1✔
2344
                rename: None,
1✔
2345
            }),
1✔
2346
            force_ogr_time_filter: false,
1✔
2347
            force_ogr_spatial_filter: false,
1✔
2348
            on_error: OgrSourceErrorSpec::Ignore,
1✔
2349
            sql_query: None,
1✔
2350
            attribute_query: None,
1✔
2351
            cache_ttl: CacheTtlSeconds::default(),
1✔
2352
        };
1✔
2353

1✔
2354
        let meta_data = MetaDataDefinition::OgrMetaData(StaticMetaData::<
1✔
2355
            OgrSourceDataset,
1✔
2356
            VectorResultDescriptor,
1✔
2357
            VectorQueryRectangle,
1✔
2358
        > {
1✔
2359
            loading_info: loading_info.clone(),
1✔
2360
            result_descriptor: VectorResultDescriptor {
1✔
2361
                data_type: VectorDataType::MultiPoint,
1✔
2362
                spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2363
                columns: [(
1✔
2364
                    "foo".to_owned(),
1✔
2365
                    VectorColumnInfo {
1✔
2366
                        data_type: FeatureDataType::Float,
1✔
2367
                        measurement: Measurement::Unitless,
1✔
2368
                    },
1✔
2369
                )]
1✔
2370
                .into_iter()
1✔
2371
                .collect(),
1✔
2372
                time: None,
1✔
2373
                bbox: None,
1✔
2374
            },
1✔
2375
            phantom: Default::default(),
1✔
2376
        });
1✔
2377

2378
        let session = app_ctx.default_session().await.unwrap();
1✔
2379

1✔
2380
        let dataset_name = DatasetName::new(None, "my_dataset");
1✔
2381

1✔
2382
        let db = app_ctx.session_context(session.clone()).db();
1✔
2383
        let dataset_id = db
1✔
2384
            .add_dataset(
1✔
2385
                AddDataset {
1✔
2386
                    name: Some(dataset_name),
1✔
2387
                    display_name: "Ogr Test".to_owned(),
1✔
2388
                    description: "desc".to_owned(),
1✔
2389
                    source_operator: "OgrSource".to_owned(),
1✔
2390
                    symbology: None,
1✔
2391
                    provenance: Some(vec![Provenance {
1✔
2392
                        citation: "citation".to_owned(),
1✔
2393
                        license: "license".to_owned(),
1✔
2394
                        uri: "uri".to_owned(),
1✔
2395
                    }]),
1✔
2396
                    tags: Some(vec!["upload".to_owned(), "test".to_owned()]),
1✔
2397
                },
1✔
2398
                meta_data,
1✔
2399
            )
1✔
2400
            .await
1✔
2401
            .unwrap()
1✔
2402
            .id;
1✔
2403

1✔
2404
        assert!(db.load_dataset(&dataset_id).await.is_ok());
1✔
2405

2406
        db.delete_dataset(dataset_id).await.unwrap();
1✔
2407

1✔
2408
        assert!(db.load_dataset(&dataset_id).await.is_err());
1✔
2409
    }
1✔
2410

2411
    #[ge_context::test]
2✔
2412
    #[allow(clippy::too_many_lines)]
2413
    async fn it_deletes_admin_dataset(app_ctx: PostgresContext<NoTls>) {
1✔
2414
        let dataset_name = DatasetName::new(None, "my_dataset");
1✔
2415

1✔
2416
        let loading_info = OgrSourceDataset {
1✔
2417
            file_name: PathBuf::from("test.csv"),
1✔
2418
            layer_name: "test.csv".to_owned(),
1✔
2419
            data_type: Some(VectorDataType::MultiPoint),
1✔
2420
            time: OgrSourceDatasetTimeType::Start {
1✔
2421
                start_field: "start".to_owned(),
1✔
2422
                start_format: OgrSourceTimeFormat::Auto,
1✔
2423
                duration: OgrSourceDurationSpec::Zero,
1✔
2424
            },
1✔
2425
            default_geometry: None,
1✔
2426
            columns: Some(OgrSourceColumnSpec {
1✔
2427
                format_specifics: Some(FormatSpecifics::Csv {
1✔
2428
                    header: CsvHeader::Auto,
1✔
2429
                }),
1✔
2430
                x: "x".to_owned(),
1✔
2431
                y: None,
1✔
2432
                int: vec![],
1✔
2433
                float: vec![],
1✔
2434
                text: vec![],
1✔
2435
                bool: vec![],
1✔
2436
                datetime: vec![],
1✔
2437
                rename: None,
1✔
2438
            }),
1✔
2439
            force_ogr_time_filter: false,
1✔
2440
            force_ogr_spatial_filter: false,
1✔
2441
            on_error: OgrSourceErrorSpec::Ignore,
1✔
2442
            sql_query: None,
1✔
2443
            attribute_query: None,
1✔
2444
            cache_ttl: CacheTtlSeconds::default(),
1✔
2445
        };
1✔
2446

1✔
2447
        let meta_data = MetaDataDefinition::OgrMetaData(StaticMetaData::<
1✔
2448
            OgrSourceDataset,
1✔
2449
            VectorResultDescriptor,
1✔
2450
            VectorQueryRectangle,
1✔
2451
        > {
1✔
2452
            loading_info: loading_info.clone(),
1✔
2453
            result_descriptor: VectorResultDescriptor {
1✔
2454
                data_type: VectorDataType::MultiPoint,
1✔
2455
                spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2456
                columns: [(
1✔
2457
                    "foo".to_owned(),
1✔
2458
                    VectorColumnInfo {
1✔
2459
                        data_type: FeatureDataType::Float,
1✔
2460
                        measurement: Measurement::Unitless,
1✔
2461
                    },
1✔
2462
                )]
1✔
2463
                .into_iter()
1✔
2464
                .collect(),
1✔
2465
                time: None,
1✔
2466
                bbox: None,
1✔
2467
            },
1✔
2468
            phantom: Default::default(),
1✔
2469
        });
1✔
2470

2471
        let session = app_ctx.default_session().await.unwrap();
1✔
2472

1✔
2473
        let db = app_ctx.session_context(session).db();
1✔
2474
        let dataset_id = db
1✔
2475
            .add_dataset(
1✔
2476
                AddDataset {
1✔
2477
                    name: Some(dataset_name),
1✔
2478
                    display_name: "Ogr Test".to_owned(),
1✔
2479
                    description: "desc".to_owned(),
1✔
2480
                    source_operator: "OgrSource".to_owned(),
1✔
2481
                    symbology: None,
1✔
2482
                    provenance: Some(vec![Provenance {
1✔
2483
                        citation: "citation".to_owned(),
1✔
2484
                        license: "license".to_owned(),
1✔
2485
                        uri: "uri".to_owned(),
1✔
2486
                    }]),
1✔
2487
                    tags: Some(vec!["upload".to_owned(), "test".to_owned()]),
1✔
2488
                },
1✔
2489
                meta_data,
1✔
2490
            )
1✔
2491
            .await
1✔
2492
            .unwrap()
1✔
2493
            .id;
1✔
2494

1✔
2495
        assert!(db.load_dataset(&dataset_id).await.is_ok());
1✔
2496

2497
        db.delete_dataset(dataset_id).await.unwrap();
1✔
2498

1✔
2499
        assert!(db.load_dataset(&dataset_id).await.is_err());
1✔
2500
    }
1✔
2501

2502
    #[ge_context::test]
2✔
2503
    async fn test_missing_layer_dataset_in_collection_listing(app_ctx: PostgresContext<NoTls>) {
1✔
2504
        let session = app_ctx.default_session().await.unwrap();
1✔
2505
        let db = app_ctx.session_context(session).db();
1✔
2506

2507
        let root_collection_id = &db.get_root_layer_collection_id().await.unwrap();
1✔
2508

2509
        let top_collection_id = db
1✔
2510
            .add_layer_collection(
1✔
2511
                AddLayerCollection {
1✔
2512
                    name: "top collection".to_string(),
1✔
2513
                    description: "description".to_string(),
1✔
2514
                    properties: Default::default(),
1✔
2515
                },
1✔
2516
                root_collection_id,
1✔
2517
            )
1✔
2518
            .await
1✔
2519
            .unwrap();
1✔
2520

1✔
2521
        let faux_layer = LayerId("faux".to_string());
1✔
2522

1✔
2523
        // this should fail
1✔
2524
        db.add_layer_to_collection(&faux_layer, &top_collection_id)
1✔
2525
            .await
1✔
2526
            .unwrap_err();
1✔
2527

2528
        let root_collection_layers = db
1✔
2529
            .load_layer_collection(
1✔
2530
                &top_collection_id,
1✔
2531
                LayerCollectionListOptions {
1✔
2532
                    offset: 0,
1✔
2533
                    limit: 20,
1✔
2534
                },
1✔
2535
            )
1✔
2536
            .await
1✔
2537
            .unwrap();
1✔
2538

1✔
2539
        assert_eq!(
1✔
2540
            root_collection_layers,
1✔
2541
            LayerCollection {
1✔
2542
                id: ProviderLayerCollectionId {
1✔
2543
                    provider_id: DataProviderId(
1✔
2544
                        "ce5e84db-cbf9-48a2-9a32-d4b7cc56ea74".try_into().unwrap()
1✔
2545
                    ),
1✔
2546
                    collection_id: top_collection_id.clone(),
1✔
2547
                },
1✔
2548
                name: "top collection".to_string(),
1✔
2549
                description: "description".to_string(),
1✔
2550
                items: vec![],
1✔
2551
                entry_label: None,
1✔
2552
                properties: vec![],
1✔
2553
            }
1✔
2554
        );
1✔
2555
    }
1✔
2556

2557
    #[allow(clippy::too_many_lines)]
2558
    #[crate::pro::ge_context::test]
2✔
2559
    async fn it_updates_project_layer_symbology(app_ctx: ProPostgresContext<NoTls>) {
1✔
2560
        let session = app_ctx.create_anonymous_session().await.unwrap();
1✔
2561

2562
        let (_, workflow_id) = register_ndvi_workflow_helper(&app_ctx).await;
1✔
2563

2564
        let db = app_ctx.session_context(session.clone()).db();
1✔
2565

1✔
2566
        let create_project: CreateProject = serde_json::from_value(json!({
1✔
2567
            "name": "Default",
1✔
2568
            "description": "Default project",
1✔
2569
            "bounds": {
1✔
2570
                "boundingBox": {
1✔
2571
                    "lowerLeftCoordinate": {
1✔
2572
                        "x": -180,
1✔
2573
                        "y": -90
1✔
2574
                    },
1✔
2575
                    "upperRightCoordinate": {
1✔
2576
                        "x": 180,
1✔
2577
                        "y": 90
1✔
2578
                    }
1✔
2579
                },
1✔
2580
                "spatialReference": "EPSG:4326",
1✔
2581
                "timeInterval": {
1✔
2582
                    "start": 1_396_353_600_000i64,
1✔
2583
                    "end": 1_396_353_600_000i64
1✔
2584
                }
1✔
2585
            },
1✔
2586
            "timeStep": {
1✔
2587
                "step": 1,
1✔
2588
                "granularity": "months"
1✔
2589
            }
1✔
2590
        }))
1✔
2591
        .unwrap();
1✔
2592

2593
        let project_id = db.create_project(create_project).await.unwrap();
1✔
2594

1✔
2595
        let update: UpdateProject = serde_json::from_value(json!({
1✔
2596
            "id": project_id.to_string(),
1✔
2597
            "layers": [{
1✔
2598
                "name": "NDVI",
1✔
2599
                "workflow": workflow_id.to_string(),
1✔
2600
                "visibility": {
1✔
2601
                    "data": true,
1✔
2602
                    "legend": false
1✔
2603
                },
1✔
2604
                "symbology": {
1✔
2605
                    "type": "raster",
1✔
2606
                    "opacity": 1,
1✔
2607
                    "rasterColorizer": {
1✔
2608
                        "type": "singleBand",
1✔
2609
                        "band": 0,
1✔
2610
                        "bandColorizer": {
1✔
2611
                            "type": "linearGradient",
1✔
2612
                            "breakpoints": [{
1✔
2613
                                "value": 1,
1✔
2614
                                "color": [0, 0, 0, 255]
1✔
2615
                            }, {
1✔
2616
                                "value": 255,
1✔
2617
                                "color": [255, 255, 255, 255]
1✔
2618
                            }],
1✔
2619
                            "noDataColor": [0, 0, 0, 0],
1✔
2620
                            "overColor": [255, 255, 255, 127],
1✔
2621
                            "underColor": [255, 255, 255, 127]
1✔
2622
                        }
1✔
2623
                    }
1✔
2624
                }
1✔
2625
            }]
1✔
2626
        }))
1✔
2627
        .unwrap();
1✔
2628

1✔
2629
        db.update_project(update).await.unwrap();
1✔
2630

1✔
2631
        let update: UpdateProject = serde_json::from_value(json!({
1✔
2632
            "id": project_id.to_string(),
1✔
2633
            "layers": [{
1✔
2634
                "name": "NDVI",
1✔
2635
                "workflow": workflow_id.to_string(),
1✔
2636
                "visibility": {
1✔
2637
                    "data": true,
1✔
2638
                    "legend": false
1✔
2639
                },
1✔
2640
                "symbology": {
1✔
2641
                    "type": "raster",
1✔
2642
                    "opacity": 1,
1✔
2643
                    "rasterColorizer": {
1✔
2644
                        "type": "singleBand",
1✔
2645
                        "band": 0,
1✔
2646
                        "bandColorizer": {
1✔
2647
                            "type": "linearGradient",
1✔
2648
                            "breakpoints": [{
1✔
2649
                                "value": 1,
1✔
2650
                                "color": [0, 0, 4, 255]
1✔
2651
                            }, {
1✔
2652
                                "value": 17.866_666_666_666_667,
1✔
2653
                                "color": [11, 9, 36, 255]
1✔
2654
                            }, {
1✔
2655
                                "value": 34.733_333_333_333_334,
1✔
2656
                                "color": [32, 17, 75, 255]
1✔
2657
                            }, {
1✔
2658
                                "value": 51.6,
1✔
2659
                                "color": [59, 15, 112, 255]
1✔
2660
                            }, {
1✔
2661
                                "value": 68.466_666_666_666_67,
1✔
2662
                                "color": [87, 21, 126, 255]
1✔
2663
                            }, {
1✔
2664
                                "value": 85.333_333_333_333_33,
1✔
2665
                                "color": [114, 31, 129, 255]
1✔
2666
                            }, {
1✔
2667
                                "value": 102.199_999_999_999_99,
1✔
2668
                                "color": [140, 41, 129, 255]
1✔
2669
                            }, {
1✔
2670
                                "value": 119.066_666_666_666_65,
1✔
2671
                                "color": [168, 50, 125, 255]
1✔
2672
                            }, {
1✔
2673
                                "value": 135.933_333_333_333_34,
1✔
2674
                                "color": [196, 60, 117, 255]
1✔
2675
                            }, {
1✔
2676
                                "value": 152.799_999_999_999_98,
1✔
2677
                                "color": [222, 73, 104, 255]
1✔
2678
                            }, {
1✔
2679
                                "value": 169.666_666_666_666_66,
1✔
2680
                                "color": [241, 96, 93, 255]
1✔
2681
                            }, {
1✔
2682
                                "value": 186.533_333_333_333_33,
1✔
2683
                                "color": [250, 127, 94, 255]
1✔
2684
                            }, {
1✔
2685
                                "value": 203.399_999_999_999_98,
1✔
2686
                                "color": [254, 159, 109, 255]
1✔
2687
                            }, {
1✔
2688
                                "value": 220.266_666_666_666_65,
1✔
2689
                                "color": [254, 191, 132, 255]
1✔
2690
                            }, {
1✔
2691
                                "value": 237.133_333_333_333_3,
1✔
2692
                                "color": [253, 222, 160, 255]
1✔
2693
                            }, {
1✔
2694
                                "value": 254,
1✔
2695
                                "color": [252, 253, 191, 255]
1✔
2696
                            }],
1✔
2697
                            "noDataColor": [0, 0, 0, 0],
1✔
2698
                            "overColor": [255, 255, 255, 127],
1✔
2699
                            "underColor": [255, 255, 255, 127]
1✔
2700
                        }
1✔
2701
                    }
1✔
2702
                }
1✔
2703
            }]
1✔
2704
        }))
1✔
2705
        .unwrap();
1✔
2706

1✔
2707
        db.update_project(update).await.unwrap();
1✔
2708

1✔
2709
        let update: UpdateProject = serde_json::from_value(json!({
1✔
2710
            "id": project_id.to_string(),
1✔
2711
            "layers": [{
1✔
2712
                "name": "NDVI",
1✔
2713
                "workflow": workflow_id.to_string(),
1✔
2714
                "visibility": {
1✔
2715
                    "data": true,
1✔
2716
                    "legend": false
1✔
2717
                },
1✔
2718
                "symbology": {
1✔
2719
                    "type": "raster",
1✔
2720
                    "opacity": 1,
1✔
2721
                    "rasterColorizer": {
1✔
2722
                        "type": "singleBand",
1✔
2723
                        "band": 0,
1✔
2724
                        "bandColorizer": {
1✔
2725
                            "type": "linearGradient",
1✔
2726
                            "breakpoints": [{
1✔
2727
                                "value": 1,
1✔
2728
                                "color": [0, 0, 4, 255]
1✔
2729
                            }, {
1✔
2730
                                "value": 17.866_666_666_666_667,
1✔
2731
                                "color": [11, 9, 36, 255]
1✔
2732
                            }, {
1✔
2733
                                "value": 34.733_333_333_333_334,
1✔
2734
                                "color": [32, 17, 75, 255]
1✔
2735
                            }, {
1✔
2736
                                "value": 51.6,
1✔
2737
                                "color": [59, 15, 112, 255]
1✔
2738
                            }, {
1✔
2739
                                "value": 68.466_666_666_666_67,
1✔
2740
                                "color": [87, 21, 126, 255]
1✔
2741
                            }, {
1✔
2742
                                "value": 85.333_333_333_333_33,
1✔
2743
                                "color": [114, 31, 129, 255]
1✔
2744
                            }, {
1✔
2745
                                "value": 102.199_999_999_999_99,
1✔
2746
                                "color": [140, 41, 129, 255]
1✔
2747
                            }, {
1✔
2748
                                "value": 119.066_666_666_666_65,
1✔
2749
                                "color": [168, 50, 125, 255]
1✔
2750
                            }, {
1✔
2751
                                "value": 135.933_333_333_333_34,
1✔
2752
                                "color": [196, 60, 117, 255]
1✔
2753
                            }, {
1✔
2754
                                "value": 152.799_999_999_999_98,
1✔
2755
                                "color": [222, 73, 104, 255]
1✔
2756
                            }, {
1✔
2757
                                "value": 169.666_666_666_666_66,
1✔
2758
                                "color": [241, 96, 93, 255]
1✔
2759
                            }, {
1✔
2760
                                "value": 186.533_333_333_333_33,
1✔
2761
                                "color": [250, 127, 94, 255]
1✔
2762
                            }, {
1✔
2763
                                "value": 203.399_999_999_999_98,
1✔
2764
                                "color": [254, 159, 109, 255]
1✔
2765
                            }, {
1✔
2766
                                "value": 220.266_666_666_666_65,
1✔
2767
                                "color": [254, 191, 132, 255]
1✔
2768
                            }, {
1✔
2769
                                "value": 237.133_333_333_333_3,
1✔
2770
                                "color": [253, 222, 160, 255]
1✔
2771
                            }, {
1✔
2772
                                "value": 254,
1✔
2773
                                "color": [252, 253, 191, 255]
1✔
2774
                            }],
1✔
2775
                            "noDataColor": [0, 0, 0, 0],
1✔
2776
                            "overColor": [255, 255, 255, 127],
1✔
2777
                            "underColor": [255, 255, 255, 127]
1✔
2778
                        }
1✔
2779
                    }
1✔
2780
                }
1✔
2781
            }]
1✔
2782
        }))
1✔
2783
        .unwrap();
1✔
2784

1✔
2785
        db.update_project(update).await.unwrap();
1✔
2786

1✔
2787
        let update: UpdateProject = serde_json::from_value(json!({
1✔
2788
            "id": project_id.to_string(),
1✔
2789
            "layers": [{
1✔
2790
                "name": "NDVI",
1✔
2791
                "workflow": workflow_id.to_string(),
1✔
2792
                "visibility": {
1✔
2793
                    "data": true,
1✔
2794
                    "legend": false
1✔
2795
                },
1✔
2796
                "symbology": {
1✔
2797
                    "type": "raster",
1✔
2798
                    "opacity": 1,
1✔
2799
                    "rasterColorizer": {
1✔
2800
                        "type": "singleBand",
1✔
2801
                        "band": 0,
1✔
2802
                        "bandColorizer": {
1✔
2803
                            "type": "linearGradient",
1✔
2804
                            "breakpoints": [{
1✔
2805
                                "value": 1,
1✔
2806
                                "color": [0, 0, 4, 255]
1✔
2807
                            }, {
1✔
2808
                                "value": 17.933_333_333_333_334,
1✔
2809
                                "color": [11, 9, 36, 255]
1✔
2810
                            }, {
1✔
2811
                                "value": 34.866_666_666_666_67,
1✔
2812
                                "color": [32, 17, 75, 255]
1✔
2813
                            }, {
1✔
2814
                                "value": 51.800_000_000_000_004,
1✔
2815
                                "color": [59, 15, 112, 255]
1✔
2816
                            }, {
1✔
2817
                                "value": 68.733_333_333_333_33,
1✔
2818
                                "color": [87, 21, 126, 255]
1✔
2819
                            }, {
1✔
2820
                                "value": 85.666_666_666_666_66,
1✔
2821
                                "color": [114, 31, 129, 255]
1✔
2822
                            }, {
1✔
2823
                                "value": 102.6,
1✔
2824
                                "color": [140, 41, 129, 255]
1✔
2825
                            }, {
1✔
2826
                                "value": 119.533_333_333_333_32,
1✔
2827
                                "color": [168, 50, 125, 255]
1✔
2828
                            }, {
1✔
2829
                                "value": 136.466_666_666_666_67,
1✔
2830
                                "color": [196, 60, 117, 255]
1✔
2831
                            }, {
1✔
2832
                                "value": 153.4,
1✔
2833
                                "color": [222, 73, 104, 255]
1✔
2834
                            }, {
1✔
2835
                                "value": 170.333_333_333_333_31,
1✔
2836
                                "color": [241, 96, 93, 255]
1✔
2837
                            }, {
1✔
2838
                                "value": 187.266_666_666_666_65,
1✔
2839
                                "color": [250, 127, 94, 255]
1✔
2840
                            }, {
1✔
2841
                                "value": 204.2,
1✔
2842
                                "color": [254, 159, 109, 255]
1✔
2843
                            }, {
1✔
2844
                                "value": 221.133_333_333_333_33,
1✔
2845
                                "color": [254, 191, 132, 255]
1✔
2846
                            }, {
1✔
2847
                                "value": 238.066_666_666_666_63,
1✔
2848
                                "color": [253, 222, 160, 255]
1✔
2849
                            }, {
1✔
2850
                                "value": 255,
1✔
2851
                                "color": [252, 253, 191, 255]
1✔
2852
                            }],
1✔
2853
                            "noDataColor": [0, 0, 0, 0],
1✔
2854
                            "overColor": [255, 255, 255, 127],
1✔
2855
                            "underColor": [255, 255, 255, 127]
1✔
2856
                        }
1✔
2857
                    }
1✔
2858
                }
1✔
2859
            }]
1✔
2860
        }))
1✔
2861
        .unwrap();
1✔
2862

2863
        // run two updates concurrently
2864
        let (r0, r1) = join!(db.update_project(update.clone()), db.update_project(update));
1✔
2865

2866
        assert!(r0.is_ok());
1✔
2867
        assert!(r1.is_ok());
1✔
2868
    }
1✔
2869

2870
    #[ge_context::test]
2✔
2871
    #[allow(clippy::too_many_lines)]
2872
    async fn it_resolves_dataset_names_to_ids(app_ctx: PostgresContext<NoTls>) {
1✔
2873
        let session = app_ctx.default_session().await.unwrap();
1✔
2874
        let db = app_ctx.session_context(session.clone()).db();
1✔
2875

1✔
2876
        let loading_info = OgrSourceDataset {
1✔
2877
            file_name: PathBuf::from("test.csv"),
1✔
2878
            layer_name: "test.csv".to_owned(),
1✔
2879
            data_type: Some(VectorDataType::MultiPoint),
1✔
2880
            time: OgrSourceDatasetTimeType::Start {
1✔
2881
                start_field: "start".to_owned(),
1✔
2882
                start_format: OgrSourceTimeFormat::Auto,
1✔
2883
                duration: OgrSourceDurationSpec::Zero,
1✔
2884
            },
1✔
2885
            default_geometry: None,
1✔
2886
            columns: Some(OgrSourceColumnSpec {
1✔
2887
                format_specifics: Some(FormatSpecifics::Csv {
1✔
2888
                    header: CsvHeader::Auto,
1✔
2889
                }),
1✔
2890
                x: "x".to_owned(),
1✔
2891
                y: None,
1✔
2892
                int: vec![],
1✔
2893
                float: vec![],
1✔
2894
                text: vec![],
1✔
2895
                bool: vec![],
1✔
2896
                datetime: vec![],
1✔
2897
                rename: None,
1✔
2898
            }),
1✔
2899
            force_ogr_time_filter: false,
1✔
2900
            force_ogr_spatial_filter: false,
1✔
2901
            on_error: OgrSourceErrorSpec::Ignore,
1✔
2902
            sql_query: None,
1✔
2903
            attribute_query: None,
1✔
2904
            cache_ttl: CacheTtlSeconds::default(),
1✔
2905
        };
1✔
2906

1✔
2907
        let meta_data = MetaDataDefinition::OgrMetaData(StaticMetaData::<
1✔
2908
            OgrSourceDataset,
1✔
2909
            VectorResultDescriptor,
1✔
2910
            VectorQueryRectangle,
1✔
2911
        > {
1✔
2912
            loading_info: loading_info.clone(),
1✔
2913
            result_descriptor: VectorResultDescriptor {
1✔
2914
                data_type: VectorDataType::MultiPoint,
1✔
2915
                spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2916
                columns: [(
1✔
2917
                    "foo".to_owned(),
1✔
2918
                    VectorColumnInfo {
1✔
2919
                        data_type: FeatureDataType::Float,
1✔
2920
                        measurement: Measurement::Unitless,
1✔
2921
                    },
1✔
2922
                )]
1✔
2923
                .into_iter()
1✔
2924
                .collect(),
1✔
2925
                time: None,
1✔
2926
                bbox: None,
1✔
2927
            },
1✔
2928
            phantom: Default::default(),
1✔
2929
        });
1✔
2930

2931
        let DatasetIdAndName {
2932
            id: dataset_id1,
1✔
2933
            name: dataset_name1,
1✔
2934
        } = db
1✔
2935
            .add_dataset(
1✔
2936
                AddDataset {
1✔
2937
                    name: Some(DatasetName::new(None, "my_dataset".to_owned())),
1✔
2938
                    display_name: "Ogr Test".to_owned(),
1✔
2939
                    description: "desc".to_owned(),
1✔
2940
                    source_operator: "OgrSource".to_owned(),
1✔
2941
                    symbology: None,
1✔
2942
                    provenance: Some(vec![Provenance {
1✔
2943
                        citation: "citation".to_owned(),
1✔
2944
                        license: "license".to_owned(),
1✔
2945
                        uri: "uri".to_owned(),
1✔
2946
                    }]),
1✔
2947
                    tags: Some(vec!["upload".to_owned(), "test".to_owned()]),
1✔
2948
                },
1✔
2949
                meta_data.clone(),
1✔
2950
            )
1✔
2951
            .await
1✔
2952
            .unwrap();
1✔
2953

2954
        assert_eq!(
1✔
2955
            db.resolve_dataset_name_to_id(&dataset_name1)
1✔
2956
                .await
1✔
2957
                .unwrap()
1✔
2958
                .unwrap(),
1✔
2959
            dataset_id1
2960
        );
2961
    }
1✔
2962

2963
    #[ge_context::test]
2✔
2964
    #[allow(clippy::too_many_lines)]
2965
    async fn test_postgres_type_serialization(app_ctx: PostgresContext<NoTls>) {
1✔
2966
        let pool = app_ctx.pool.get().await.unwrap();
1✔
2967

1✔
2968
        assert_sql_type(&pool, "RgbaColor", [RgbaColor::new(0, 1, 2, 3)]).await;
1✔
2969

2970
        assert_sql_type(
1✔
2971
            &pool,
1✔
2972
            "double precision",
1✔
2973
            [NotNanF64::from(NotNan::<f64>::new(1.0).unwrap())],
1✔
2974
        )
1✔
2975
        .await;
1✔
2976

2977
        assert_sql_type(
1✔
2978
            &pool,
1✔
2979
            "Breakpoint",
1✔
2980
            [Breakpoint {
1✔
2981
                value: NotNan::<f64>::new(1.0).unwrap(),
1✔
2982
                color: RgbaColor::new(0, 0, 0, 0),
1✔
2983
            }],
1✔
2984
        )
1✔
2985
        .await;
1✔
2986

2987
        assert_sql_type(
1✔
2988
            &pool,
1✔
2989
            "Colorizer",
1✔
2990
            [
1✔
2991
                Colorizer::LinearGradient {
1✔
2992
                    breakpoints: vec![
1✔
2993
                        Breakpoint {
1✔
2994
                            value: NotNan::<f64>::new(-10.0).unwrap(),
1✔
2995
                            color: RgbaColor::new(0, 0, 0, 0),
1✔
2996
                        },
1✔
2997
                        Breakpoint {
1✔
2998
                            value: NotNan::<f64>::new(2.0).unwrap(),
1✔
2999
                            color: RgbaColor::new(255, 0, 0, 255),
1✔
3000
                        },
1✔
3001
                    ],
1✔
3002
                    no_data_color: RgbaColor::new(0, 10, 20, 30),
1✔
3003
                    over_color: RgbaColor::new(1, 2, 3, 4),
1✔
3004
                    under_color: RgbaColor::new(5, 6, 7, 8),
1✔
3005
                },
1✔
3006
                Colorizer::LogarithmicGradient {
1✔
3007
                    breakpoints: vec![
1✔
3008
                        Breakpoint {
1✔
3009
                            value: NotNan::<f64>::new(1.0).unwrap(),
1✔
3010
                            color: RgbaColor::new(0, 0, 0, 0),
1✔
3011
                        },
1✔
3012
                        Breakpoint {
1✔
3013
                            value: NotNan::<f64>::new(2.0).unwrap(),
1✔
3014
                            color: RgbaColor::new(255, 0, 0, 255),
1✔
3015
                        },
1✔
3016
                    ],
1✔
3017
                    no_data_color: RgbaColor::new(0, 10, 20, 30),
1✔
3018
                    over_color: RgbaColor::new(1, 2, 3, 4),
1✔
3019
                    under_color: RgbaColor::new(5, 6, 7, 8),
1✔
3020
                },
1✔
3021
                Colorizer::palette(
1✔
3022
                    [
1✔
3023
                        (NotNan::<f64>::new(1.0).unwrap(), RgbaColor::new(0, 0, 0, 0)),
1✔
3024
                        (
1✔
3025
                            NotNan::<f64>::new(2.0).unwrap(),
1✔
3026
                            RgbaColor::new(255, 0, 0, 255),
1✔
3027
                        ),
1✔
3028
                        (
1✔
3029
                            NotNan::<f64>::new(3.0).unwrap(),
1✔
3030
                            RgbaColor::new(0, 10, 20, 30),
1✔
3031
                        ),
1✔
3032
                    ]
1✔
3033
                    .into(),
1✔
3034
                    RgbaColor::new(1, 2, 3, 4),
1✔
3035
                    RgbaColor::new(5, 6, 7, 8),
1✔
3036
                )
1✔
3037
                .unwrap(),
1✔
3038
            ],
1✔
3039
        )
1✔
3040
        .await;
1✔
3041

3042
        assert_sql_type(
1✔
3043
            &pool,
1✔
3044
            "ColorParam",
1✔
3045
            [
1✔
3046
                ColorParam::Static {
1✔
3047
                    color: RgbaColor::new(0, 10, 20, 30),
1✔
3048
                },
1✔
3049
                ColorParam::Derived(DerivedColor {
1✔
3050
                    attribute: "foobar".to_string(),
1✔
3051
                    colorizer: Colorizer::test_default(),
1✔
3052
                }),
1✔
3053
            ],
1✔
3054
        )
1✔
3055
        .await;
1✔
3056

3057
        assert_sql_type(
1✔
3058
            &pool,
1✔
3059
            "NumberParam",
1✔
3060
            [
1✔
3061
                NumberParam::Static { value: 42 },
1✔
3062
                NumberParam::Derived(DerivedNumber {
1✔
3063
                    attribute: "foobar".to_string(),
1✔
3064
                    factor: 1.0,
1✔
3065
                    default_value: 42.,
1✔
3066
                }),
1✔
3067
            ],
1✔
3068
        )
1✔
3069
        .await;
1✔
3070

3071
        assert_sql_type(
1✔
3072
            &pool,
1✔
3073
            "StrokeParam",
1✔
3074
            [StrokeParam {
1✔
3075
                width: NumberParam::Static { value: 42 },
1✔
3076
                color: ColorParam::Static {
1✔
3077
                    color: RgbaColor::new(0, 10, 20, 30),
1✔
3078
                },
1✔
3079
            }],
1✔
3080
        )
1✔
3081
        .await;
1✔
3082

3083
        assert_sql_type(
1✔
3084
            &pool,
1✔
3085
            "TextSymbology",
1✔
3086
            [TextSymbology {
1✔
3087
                attribute: "attribute".to_string(),
1✔
3088
                fill_color: ColorParam::Static {
1✔
3089
                    color: RgbaColor::new(0, 10, 20, 30),
1✔
3090
                },
1✔
3091
                stroke: StrokeParam {
1✔
3092
                    width: NumberParam::Static { value: 42 },
1✔
3093
                    color: ColorParam::Static {
1✔
3094
                        color: RgbaColor::new(0, 10, 20, 30),
1✔
3095
                    },
1✔
3096
                },
1✔
3097
            }],
1✔
3098
        )
1✔
3099
        .await;
1✔
3100

3101
        assert_sql_type(
1✔
3102
            &pool,
1✔
3103
            "RasterColorizer",
1✔
3104
            [RasterColorizer::SingleBand {
1✔
3105
                band: 0,
1✔
3106
                band_colorizer: Colorizer::LinearGradient {
1✔
3107
                    breakpoints: vec![
1✔
3108
                        Breakpoint {
1✔
3109
                            value: NotNan::<f64>::new(-10.0).unwrap(),
1✔
3110
                            color: RgbaColor::new(0, 0, 0, 0),
1✔
3111
                        },
1✔
3112
                        Breakpoint {
1✔
3113
                            value: NotNan::<f64>::new(2.0).unwrap(),
1✔
3114
                            color: RgbaColor::new(255, 0, 0, 255),
1✔
3115
                        },
1✔
3116
                    ],
1✔
3117
                    no_data_color: RgbaColor::new(0, 10, 20, 30),
1✔
3118
                    over_color: RgbaColor::new(1, 2, 3, 4),
1✔
3119
                    under_color: RgbaColor::new(5, 6, 7, 8),
1✔
3120
                },
1✔
3121
            }],
1✔
3122
        )
1✔
3123
        .await;
1✔
3124

3125
        assert_sql_type(
1✔
3126
            &pool,
1✔
3127
            "RasterColorizer",
1✔
3128
            [RasterColorizer::MultiBand {
1✔
3129
                red_band: 0,
1✔
3130
                green_band: 1,
1✔
3131
                blue_band: 2,
1✔
3132
                rgb_params: RgbParams {
1✔
3133
                    red_min: 0.,
1✔
3134
                    red_max: 255.,
1✔
3135
                    red_scale: 1.,
1✔
3136
                    green_min: 0.,
1✔
3137
                    green_max: 255.,
1✔
3138
                    green_scale: 1.,
1✔
3139
                    blue_min: 0.,
1✔
3140
                    blue_max: 255.,
1✔
3141
                    blue_scale: 1.,
1✔
3142
                    no_data_color: RgbaColor::new(0, 10, 20, 30),
1✔
3143
                },
1✔
3144
            }],
1✔
3145
        )
1✔
3146
        .await;
1✔
3147

3148
        assert_sql_type(
1✔
3149
            &pool,
1✔
3150
            "Symbology",
1✔
3151
            [
1✔
3152
                Symbology::Point(PointSymbology {
1✔
3153
                    fill_color: ColorParam::Static {
1✔
3154
                        color: RgbaColor::new(0, 10, 20, 30),
1✔
3155
                    },
1✔
3156
                    stroke: StrokeParam {
1✔
3157
                        width: NumberParam::Static { value: 42 },
1✔
3158
                        color: ColorParam::Static {
1✔
3159
                            color: RgbaColor::new(0, 10, 20, 30),
1✔
3160
                        },
1✔
3161
                    },
1✔
3162
                    radius: NumberParam::Static { value: 42 },
1✔
3163
                    text: Some(TextSymbology {
1✔
3164
                        attribute: "attribute".to_string(),
1✔
3165
                        fill_color: ColorParam::Static {
1✔
3166
                            color: RgbaColor::new(0, 10, 20, 30),
1✔
3167
                        },
1✔
3168
                        stroke: StrokeParam {
1✔
3169
                            width: NumberParam::Static { value: 42 },
1✔
3170
                            color: ColorParam::Static {
1✔
3171
                                color: RgbaColor::new(0, 10, 20, 30),
1✔
3172
                            },
1✔
3173
                        },
1✔
3174
                    }),
1✔
3175
                }),
1✔
3176
                Symbology::Line(LineSymbology {
1✔
3177
                    stroke: StrokeParam {
1✔
3178
                        width: NumberParam::Static { value: 42 },
1✔
3179
                        color: ColorParam::Static {
1✔
3180
                            color: RgbaColor::new(0, 10, 20, 30),
1✔
3181
                        },
1✔
3182
                    },
1✔
3183
                    text: Some(TextSymbology {
1✔
3184
                        attribute: "attribute".to_string(),
1✔
3185
                        fill_color: ColorParam::Static {
1✔
3186
                            color: RgbaColor::new(0, 10, 20, 30),
1✔
3187
                        },
1✔
3188
                        stroke: StrokeParam {
1✔
3189
                            width: NumberParam::Static { value: 42 },
1✔
3190
                            color: ColorParam::Static {
1✔
3191
                                color: RgbaColor::new(0, 10, 20, 30),
1✔
3192
                            },
1✔
3193
                        },
1✔
3194
                    }),
1✔
3195
                    auto_simplified: true,
1✔
3196
                }),
1✔
3197
                Symbology::Polygon(PolygonSymbology {
1✔
3198
                    fill_color: ColorParam::Static {
1✔
3199
                        color: RgbaColor::new(0, 10, 20, 30),
1✔
3200
                    },
1✔
3201
                    stroke: StrokeParam {
1✔
3202
                        width: NumberParam::Static { value: 42 },
1✔
3203
                        color: ColorParam::Static {
1✔
3204
                            color: RgbaColor::new(0, 10, 20, 30),
1✔
3205
                        },
1✔
3206
                    },
1✔
3207
                    text: Some(TextSymbology {
1✔
3208
                        attribute: "attribute".to_string(),
1✔
3209
                        fill_color: ColorParam::Static {
1✔
3210
                            color: RgbaColor::new(0, 10, 20, 30),
1✔
3211
                        },
1✔
3212
                        stroke: StrokeParam {
1✔
3213
                            width: NumberParam::Static { value: 42 },
1✔
3214
                            color: ColorParam::Static {
1✔
3215
                                color: RgbaColor::new(0, 10, 20, 30),
1✔
3216
                            },
1✔
3217
                        },
1✔
3218
                    }),
1✔
3219
                    auto_simplified: true,
1✔
3220
                }),
1✔
3221
                Symbology::Raster(RasterSymbology {
1✔
3222
                    opacity: 1.0,
1✔
3223
                    raster_colorizer: RasterColorizer::SingleBand {
1✔
3224
                        band: 0,
1✔
3225
                        band_colorizer: Colorizer::LinearGradient {
1✔
3226
                            breakpoints: vec![
1✔
3227
                                Breakpoint {
1✔
3228
                                    value: NotNan::<f64>::new(-10.0).unwrap(),
1✔
3229
                                    color: RgbaColor::new(0, 0, 0, 0),
1✔
3230
                                },
1✔
3231
                                Breakpoint {
1✔
3232
                                    value: NotNan::<f64>::new(2.0).unwrap(),
1✔
3233
                                    color: RgbaColor::new(255, 0, 0, 255),
1✔
3234
                                },
1✔
3235
                            ],
1✔
3236
                            no_data_color: RgbaColor::new(0, 10, 20, 30),
1✔
3237
                            over_color: RgbaColor::new(1, 2, 3, 4),
1✔
3238
                            under_color: RgbaColor::new(5, 6, 7, 8),
1✔
3239
                        },
1✔
3240
                    },
1✔
3241
                }),
1✔
3242
            ],
1✔
3243
        )
1✔
3244
        .await;
1✔
3245

3246
        assert_sql_type(
1✔
3247
            &pool,
1✔
3248
            "RasterDataType",
1✔
3249
            [
1✔
3250
                RasterDataType::U8,
1✔
3251
                RasterDataType::U16,
1✔
3252
                RasterDataType::U32,
1✔
3253
                RasterDataType::U64,
1✔
3254
                RasterDataType::I8,
1✔
3255
                RasterDataType::I16,
1✔
3256
                RasterDataType::I32,
1✔
3257
                RasterDataType::I64,
1✔
3258
                RasterDataType::F32,
1✔
3259
                RasterDataType::F64,
1✔
3260
            ],
1✔
3261
        )
1✔
3262
        .await;
1✔
3263

3264
        assert_sql_type(
1✔
3265
            &pool,
1✔
3266
            "Measurement",
1✔
3267
            [
1✔
3268
                Measurement::Unitless,
1✔
3269
                Measurement::Continuous(ContinuousMeasurement {
1✔
3270
                    measurement: "Temperature".to_string(),
1✔
3271
                    unit: Some("°C".to_string()),
1✔
3272
                }),
1✔
3273
                Measurement::Classification(ClassificationMeasurement {
1✔
3274
                    measurement: "Color".to_string(),
1✔
3275
                    classes: [(1, "Grayscale".to_string()), (2, "Colorful".to_string())].into(),
1✔
3276
                }),
1✔
3277
            ],
1✔
3278
        )
1✔
3279
        .await;
1✔
3280

3281
        assert_sql_type(&pool, "Coordinate2D", [Coordinate2D::new(0.0f64, 1.)]).await;
1✔
3282

3283
        assert_sql_type(
1✔
3284
            &pool,
1✔
3285
            "SpatialPartition2D",
1✔
3286
            [
1✔
3287
                SpatialPartition2D::new(Coordinate2D::new(0.0f64, 1.), Coordinate2D::new(2., 0.5))
1✔
3288
                    .unwrap(),
1✔
3289
            ],
1✔
3290
        )
1✔
3291
        .await;
1✔
3292

3293
        assert_sql_type(
1✔
3294
            &pool,
1✔
3295
            "BoundingBox2D",
1✔
3296
            [
1✔
3297
                BoundingBox2D::new(Coordinate2D::new(0.0f64, 0.5), Coordinate2D::new(2., 1.0))
1✔
3298
                    .unwrap(),
1✔
3299
            ],
1✔
3300
        )
1✔
3301
        .await;
1✔
3302

3303
        assert_sql_type(
1✔
3304
            &pool,
1✔
3305
            "SpatialResolution",
1✔
3306
            [SpatialResolution { x: 1.2, y: 2.3 }],
1✔
3307
        )
1✔
3308
        .await;
1✔
3309

3310
        assert_sql_type(
1✔
3311
            &pool,
1✔
3312
            "VectorDataType",
1✔
3313
            [
1✔
3314
                VectorDataType::Data,
1✔
3315
                VectorDataType::MultiPoint,
1✔
3316
                VectorDataType::MultiLineString,
1✔
3317
                VectorDataType::MultiPolygon,
1✔
3318
            ],
1✔
3319
        )
1✔
3320
        .await;
1✔
3321

3322
        assert_sql_type(
1✔
3323
            &pool,
1✔
3324
            "FeatureDataType",
1✔
3325
            [
1✔
3326
                FeatureDataType::Category,
1✔
3327
                FeatureDataType::Int,
1✔
3328
                FeatureDataType::Float,
1✔
3329
                FeatureDataType::Text,
1✔
3330
                FeatureDataType::Bool,
1✔
3331
                FeatureDataType::DateTime,
1✔
3332
            ],
1✔
3333
        )
1✔
3334
        .await;
1✔
3335

3336
        assert_sql_type(&pool, "TimeInterval", [TimeInterval::default()]).await;
1✔
3337

3338
        assert_sql_type(
1✔
3339
            &pool,
1✔
3340
            "SpatialReference",
1✔
3341
            [
1✔
3342
                SpatialReferenceOption::Unreferenced,
1✔
3343
                SpatialReferenceOption::SpatialReference(SpatialReference::epsg_4326()),
1✔
3344
            ],
1✔
3345
        )
1✔
3346
        .await;
1✔
3347

3348
        assert_sql_type(
1✔
3349
            &pool,
1✔
3350
            "PlotResultDescriptor",
1✔
3351
            [PlotResultDescriptor {
1✔
3352
                spatial_reference: SpatialReferenceOption::Unreferenced,
1✔
3353
                time: None,
1✔
3354
                bbox: None,
1✔
3355
            }],
1✔
3356
        )
1✔
3357
        .await;
1✔
3358

3359
        assert_sql_type(
1✔
3360
            &pool,
1✔
3361
            "VectorResultDescriptor",
1✔
3362
            [VectorResultDescriptor {
1✔
3363
                data_type: VectorDataType::MultiPoint,
1✔
3364
                spatial_reference: SpatialReferenceOption::SpatialReference(
1✔
3365
                    SpatialReference::epsg_4326(),
1✔
3366
                ),
1✔
3367
                columns: [(
1✔
3368
                    "foo".to_string(),
1✔
3369
                    VectorColumnInfo {
1✔
3370
                        data_type: FeatureDataType::Int,
1✔
3371
                        measurement: Measurement::Unitless,
1✔
3372
                    },
1✔
3373
                )]
1✔
3374
                .into(),
1✔
3375
                time: Some(TimeInterval::default()),
1✔
3376
                bbox: Some(
1✔
3377
                    BoundingBox2D::new(Coordinate2D::new(0.0f64, 0.5), Coordinate2D::new(2., 1.0))
1✔
3378
                        .unwrap(),
1✔
3379
                ),
1✔
3380
            }],
1✔
3381
        )
1✔
3382
        .await;
1✔
3383

3384
        assert_sql_type(
1✔
3385
            &pool,
1✔
3386
            "RasterResultDescriptor",
1✔
3387
            [RasterResultDescriptor {
1✔
3388
                data_type: RasterDataType::U8,
1✔
3389
                spatial_reference: SpatialReferenceOption::SpatialReference(
1✔
3390
                    SpatialReference::epsg_4326(),
1✔
3391
                ),
1✔
3392
                time: Some(TimeInterval::default()),
1✔
3393
                bbox: Some(
1✔
3394
                    SpatialPartition2D::new(
1✔
3395
                        Coordinate2D::new(0.0f64, 1.),
1✔
3396
                        Coordinate2D::new(2., 0.5),
1✔
3397
                    )
1✔
3398
                    .unwrap(),
1✔
3399
                ),
1✔
3400
                resolution: Some(SpatialResolution { x: 1.2, y: 2.3 }),
1✔
3401
                bands: RasterBandDescriptors::new_single_band(),
1✔
3402
            }],
1✔
3403
        )
1✔
3404
        .await;
1✔
3405

3406
        assert_sql_type(
1✔
3407
            &pool,
1✔
3408
            "ResultDescriptor",
1✔
3409
            [
1✔
3410
                TypedResultDescriptor::Vector(VectorResultDescriptor {
1✔
3411
                    data_type: VectorDataType::MultiPoint,
1✔
3412
                    spatial_reference: SpatialReferenceOption::SpatialReference(
1✔
3413
                        SpatialReference::epsg_4326(),
1✔
3414
                    ),
1✔
3415
                    columns: [(
1✔
3416
                        "foo".to_string(),
1✔
3417
                        VectorColumnInfo {
1✔
3418
                            data_type: FeatureDataType::Int,
1✔
3419
                            measurement: Measurement::Unitless,
1✔
3420
                        },
1✔
3421
                    )]
1✔
3422
                    .into(),
1✔
3423
                    time: Some(TimeInterval::default()),
1✔
3424
                    bbox: Some(
1✔
3425
                        BoundingBox2D::new(
1✔
3426
                            Coordinate2D::new(0.0f64, 0.5),
1✔
3427
                            Coordinate2D::new(2., 1.0),
1✔
3428
                        )
1✔
3429
                        .unwrap(),
1✔
3430
                    ),
1✔
3431
                }),
1✔
3432
                TypedResultDescriptor::Raster(RasterResultDescriptor {
1✔
3433
                    data_type: RasterDataType::U8,
1✔
3434
                    spatial_reference: SpatialReferenceOption::SpatialReference(
1✔
3435
                        SpatialReference::epsg_4326(),
1✔
3436
                    ),
1✔
3437
                    time: Some(TimeInterval::default()),
1✔
3438
                    bbox: Some(
1✔
3439
                        SpatialPartition2D::new(
1✔
3440
                            Coordinate2D::new(0.0f64, 1.),
1✔
3441
                            Coordinate2D::new(2., 0.5),
1✔
3442
                        )
1✔
3443
                        .unwrap(),
1✔
3444
                    ),
1✔
3445
                    resolution: Some(SpatialResolution { x: 1.2, y: 2.3 }),
1✔
3446
                    bands: RasterBandDescriptors::new_single_band(),
1✔
3447
                }),
1✔
3448
                TypedResultDescriptor::Plot(PlotResultDescriptor {
1✔
3449
                    spatial_reference: SpatialReferenceOption::Unreferenced,
1✔
3450
                    time: None,
1✔
3451
                    bbox: None,
1✔
3452
                }),
1✔
3453
            ],
1✔
3454
        )
1✔
3455
        .await;
1✔
3456

3457
        assert_sql_type(
1✔
3458
            &pool,
1✔
3459
            "MockDatasetDataSourceLoadingInfo",
1✔
3460
            [MockDatasetDataSourceLoadingInfo {
1✔
3461
                points: vec![Coordinate2D::new(0.0f64, 0.5), Coordinate2D::new(2., 1.0)],
1✔
3462
            }],
1✔
3463
        )
1✔
3464
        .await;
1✔
3465

3466
        assert_sql_type(
1✔
3467
            &pool,
1✔
3468
            "OgrSourceTimeFormat",
1✔
3469
            [
1✔
3470
                OgrSourceTimeFormat::Auto,
1✔
3471
                OgrSourceTimeFormat::Custom {
1✔
3472
                    custom_format: geoengine_datatypes::primitives::DateTimeParseFormat::custom(
1✔
3473
                        "%Y-%m-%dT%H:%M:%S%.3fZ".to_string(),
1✔
3474
                    ),
1✔
3475
                },
1✔
3476
                OgrSourceTimeFormat::UnixTimeStamp {
1✔
3477
                    timestamp_type: UnixTimeStampType::EpochSeconds,
1✔
3478
                    fmt: geoengine_datatypes::primitives::DateTimeParseFormat::unix(),
1✔
3479
                },
1✔
3480
            ],
1✔
3481
        )
1✔
3482
        .await;
1✔
3483

3484
        assert_sql_type(
1✔
3485
            &pool,
1✔
3486
            "OgrSourceDurationSpec",
1✔
3487
            [
1✔
3488
                OgrSourceDurationSpec::Infinite,
1✔
3489
                OgrSourceDurationSpec::Zero,
1✔
3490
                OgrSourceDurationSpec::Value(TimeStep {
1✔
3491
                    granularity: TimeGranularity::Millis,
1✔
3492
                    step: 1000,
1✔
3493
                }),
1✔
3494
            ],
1✔
3495
        )
1✔
3496
        .await;
1✔
3497

3498
        assert_sql_type(
1✔
3499
            &pool,
1✔
3500
            "OgrSourceDatasetTimeType",
1✔
3501
            [
1✔
3502
                OgrSourceDatasetTimeType::None,
1✔
3503
                OgrSourceDatasetTimeType::Start {
1✔
3504
                    start_field: "start".to_string(),
1✔
3505
                    start_format: OgrSourceTimeFormat::Auto,
1✔
3506
                    duration: OgrSourceDurationSpec::Zero,
1✔
3507
                },
1✔
3508
                OgrSourceDatasetTimeType::StartEnd {
1✔
3509
                    start_field: "start".to_string(),
1✔
3510
                    start_format: OgrSourceTimeFormat::Auto,
1✔
3511
                    end_field: "end".to_string(),
1✔
3512
                    end_format: OgrSourceTimeFormat::Auto,
1✔
3513
                },
1✔
3514
                OgrSourceDatasetTimeType::StartDuration {
1✔
3515
                    start_field: "start".to_string(),
1✔
3516
                    start_format: OgrSourceTimeFormat::Auto,
1✔
3517
                    duration_field: "duration".to_string(),
1✔
3518
                },
1✔
3519
            ],
1✔
3520
        )
1✔
3521
        .await;
1✔
3522

3523
        assert_sql_type(
1✔
3524
            &pool,
1✔
3525
            "FormatSpecifics",
1✔
3526
            [FormatSpecifics::Csv {
1✔
3527
                header: CsvHeader::Yes,
1✔
3528
            }],
1✔
3529
        )
1✔
3530
        .await;
1✔
3531

3532
        assert_sql_type(
1✔
3533
            &pool,
1✔
3534
            "OgrSourceColumnSpec",
1✔
3535
            [OgrSourceColumnSpec {
1✔
3536
                format_specifics: Some(FormatSpecifics::Csv {
1✔
3537
                    header: CsvHeader::Auto,
1✔
3538
                }),
1✔
3539
                x: "x".to_string(),
1✔
3540
                y: Some("y".to_string()),
1✔
3541
                int: vec!["int".to_string()],
1✔
3542
                float: vec!["float".to_string()],
1✔
3543
                text: vec!["text".to_string()],
1✔
3544
                bool: vec!["bool".to_string()],
1✔
3545
                datetime: vec!["datetime".to_string()],
1✔
3546
                rename: Some(
1✔
3547
                    [
1✔
3548
                        ("xx".to_string(), "xx_renamed".to_string()),
1✔
3549
                        ("yx".to_string(), "yy_renamed".to_string()),
1✔
3550
                    ]
1✔
3551
                    .into(),
1✔
3552
                ),
1✔
3553
            }],
1✔
3554
        )
1✔
3555
        .await;
1✔
3556

3557
        assert_sql_type(
1✔
3558
            &pool,
1✔
3559
            "point[]",
1✔
3560
            [MultiPoint::new(vec![
1✔
3561
                Coordinate2D::new(0.0f64, 0.5),
1✔
3562
                Coordinate2D::new(2., 1.0),
1✔
3563
            ])
1✔
3564
            .unwrap()],
1✔
3565
        )
1✔
3566
        .await;
1✔
3567

3568
        assert_sql_type(
1✔
3569
            &pool,
1✔
3570
            "path[]",
1✔
3571
            [MultiLineString::new(vec![
1✔
3572
                vec![Coordinate2D::new(0.0f64, 0.5), Coordinate2D::new(2., 1.0)],
1✔
3573
                vec![Coordinate2D::new(0.0f64, 0.5), Coordinate2D::new(2., 1.0)],
1✔
3574
            ])
1✔
3575
            .unwrap()],
1✔
3576
        )
1✔
3577
        .await;
1✔
3578

3579
        assert_sql_type(
1✔
3580
            &pool,
1✔
3581
            "\"Polygon\"[]",
1✔
3582
            [MultiPolygon::new(vec![
1✔
3583
                vec![
1✔
3584
                    vec![
1✔
3585
                        Coordinate2D::new(0.0f64, 0.5),
1✔
3586
                        Coordinate2D::new(2., 1.0),
1✔
3587
                        Coordinate2D::new(2., 1.0),
1✔
3588
                        Coordinate2D::new(0.0f64, 0.5),
1✔
3589
                    ],
1✔
3590
                    vec![
1✔
3591
                        Coordinate2D::new(0.0f64, 0.5),
1✔
3592
                        Coordinate2D::new(2., 1.0),
1✔
3593
                        Coordinate2D::new(2., 1.0),
1✔
3594
                        Coordinate2D::new(0.0f64, 0.5),
1✔
3595
                    ],
1✔
3596
                ],
1✔
3597
                vec![
1✔
3598
                    vec![
1✔
3599
                        Coordinate2D::new(0.0f64, 0.5),
1✔
3600
                        Coordinate2D::new(2., 1.0),
1✔
3601
                        Coordinate2D::new(2., 1.0),
1✔
3602
                        Coordinate2D::new(0.0f64, 0.5),
1✔
3603
                    ],
1✔
3604
                    vec![
1✔
3605
                        Coordinate2D::new(0.0f64, 0.5),
1✔
3606
                        Coordinate2D::new(2., 1.0),
1✔
3607
                        Coordinate2D::new(2., 1.0),
1✔
3608
                        Coordinate2D::new(0.0f64, 0.5),
1✔
3609
                    ],
1✔
3610
                ],
1✔
3611
            ])
1✔
3612
            .unwrap()],
1✔
3613
        )
1✔
3614
        .await;
1✔
3615

3616
        assert_sql_type(
1✔
3617
            &pool,
1✔
3618
            "TypedGeometry",
1✔
3619
            [
1✔
3620
                TypedGeometry::Data(NoGeometry),
1✔
3621
                TypedGeometry::MultiPoint(
1✔
3622
                    MultiPoint::new(vec![
1✔
3623
                        Coordinate2D::new(0.0f64, 0.5),
1✔
3624
                        Coordinate2D::new(2., 1.0),
1✔
3625
                    ])
1✔
3626
                    .unwrap(),
1✔
3627
                ),
1✔
3628
                TypedGeometry::MultiLineString(
1✔
3629
                    MultiLineString::new(vec![
1✔
3630
                        vec![Coordinate2D::new(0.0f64, 0.5), Coordinate2D::new(2., 1.0)],
1✔
3631
                        vec![Coordinate2D::new(0.0f64, 0.5), Coordinate2D::new(2., 1.0)],
1✔
3632
                    ])
1✔
3633
                    .unwrap(),
1✔
3634
                ),
1✔
3635
                TypedGeometry::MultiPolygon(
1✔
3636
                    MultiPolygon::new(vec![
1✔
3637
                        vec![
1✔
3638
                            vec![
1✔
3639
                                Coordinate2D::new(0.0f64, 0.5),
1✔
3640
                                Coordinate2D::new(2., 1.0),
1✔
3641
                                Coordinate2D::new(2., 1.0),
1✔
3642
                                Coordinate2D::new(0.0f64, 0.5),
1✔
3643
                            ],
1✔
3644
                            vec![
1✔
3645
                                Coordinate2D::new(0.0f64, 0.5),
1✔
3646
                                Coordinate2D::new(2., 1.0),
1✔
3647
                                Coordinate2D::new(2., 1.0),
1✔
3648
                                Coordinate2D::new(0.0f64, 0.5),
1✔
3649
                            ],
1✔
3650
                        ],
1✔
3651
                        vec![
1✔
3652
                            vec![
1✔
3653
                                Coordinate2D::new(0.0f64, 0.5),
1✔
3654
                                Coordinate2D::new(2., 1.0),
1✔
3655
                                Coordinate2D::new(2., 1.0),
1✔
3656
                                Coordinate2D::new(0.0f64, 0.5),
1✔
3657
                            ],
1✔
3658
                            vec![
1✔
3659
                                Coordinate2D::new(0.0f64, 0.5),
1✔
3660
                                Coordinate2D::new(2., 1.0),
1✔
3661
                                Coordinate2D::new(2., 1.0),
1✔
3662
                                Coordinate2D::new(0.0f64, 0.5),
1✔
3663
                            ],
1✔
3664
                        ],
1✔
3665
                    ])
1✔
3666
                    .unwrap(),
1✔
3667
                ),
1✔
3668
            ],
1✔
3669
        )
1✔
3670
        .await;
1✔
3671

3672
        assert_sql_type(&pool, "int", [CacheTtlSeconds::new(100)]).await;
1✔
3673

3674
        assert_sql_type(
1✔
3675
            &pool,
1✔
3676
            "OgrSourceDataset",
1✔
3677
            [OgrSourceDataset {
1✔
3678
                file_name: "test".into(),
1✔
3679
                layer_name: "test".to_string(),
1✔
3680
                data_type: Some(VectorDataType::MultiPoint),
1✔
3681
                time: OgrSourceDatasetTimeType::Start {
1✔
3682
                    start_field: "start".to_string(),
1✔
3683
                    start_format: OgrSourceTimeFormat::Auto,
1✔
3684
                    duration: OgrSourceDurationSpec::Zero,
1✔
3685
                },
1✔
3686
                default_geometry: Some(TypedGeometry::MultiPoint(
1✔
3687
                    MultiPoint::new(vec![
1✔
3688
                        Coordinate2D::new(0.0f64, 0.5),
1✔
3689
                        Coordinate2D::new(2., 1.0),
1✔
3690
                    ])
1✔
3691
                    .unwrap(),
1✔
3692
                )),
1✔
3693
                columns: Some(OgrSourceColumnSpec {
1✔
3694
                    format_specifics: Some(FormatSpecifics::Csv {
1✔
3695
                        header: CsvHeader::Auto,
1✔
3696
                    }),
1✔
3697
                    x: "x".to_string(),
1✔
3698
                    y: Some("y".to_string()),
1✔
3699
                    int: vec!["int".to_string()],
1✔
3700
                    float: vec!["float".to_string()],
1✔
3701
                    text: vec!["text".to_string()],
1✔
3702
                    bool: vec!["bool".to_string()],
1✔
3703
                    datetime: vec!["datetime".to_string()],
1✔
3704
                    rename: Some(
1✔
3705
                        [
1✔
3706
                            ("xx".to_string(), "xx_renamed".to_string()),
1✔
3707
                            ("yx".to_string(), "yy_renamed".to_string()),
1✔
3708
                        ]
1✔
3709
                        .into(),
1✔
3710
                    ),
1✔
3711
                }),
1✔
3712
                force_ogr_time_filter: false,
1✔
3713
                force_ogr_spatial_filter: true,
1✔
3714
                on_error: OgrSourceErrorSpec::Abort,
1✔
3715
                sql_query: None,
1✔
3716
                attribute_query: Some("foo = 'bar'".to_string()),
1✔
3717
                cache_ttl: CacheTtlSeconds::new(5),
1✔
3718
            }],
1✔
3719
        )
1✔
3720
        .await;
1✔
3721

3722
        assert_sql_type(
1✔
3723
            &pool,
1✔
3724
            "MockMetaData",
1✔
3725
            [StaticMetaData::<
1✔
3726
                MockDatasetDataSourceLoadingInfo,
1✔
3727
                VectorResultDescriptor,
1✔
3728
                VectorQueryRectangle,
1✔
3729
            > {
1✔
3730
                loading_info: MockDatasetDataSourceLoadingInfo {
1✔
3731
                    points: vec![Coordinate2D::new(0.0f64, 0.5), Coordinate2D::new(2., 1.0)],
1✔
3732
                },
1✔
3733
                result_descriptor: VectorResultDescriptor {
1✔
3734
                    data_type: VectorDataType::MultiPoint,
1✔
3735
                    spatial_reference: SpatialReferenceOption::SpatialReference(
1✔
3736
                        SpatialReference::epsg_4326(),
1✔
3737
                    ),
1✔
3738
                    columns: [(
1✔
3739
                        "foo".to_string(),
1✔
3740
                        VectorColumnInfo {
1✔
3741
                            data_type: FeatureDataType::Int,
1✔
3742
                            measurement: Measurement::Unitless,
1✔
3743
                        },
1✔
3744
                    )]
1✔
3745
                    .into(),
1✔
3746
                    time: Some(TimeInterval::default()),
1✔
3747
                    bbox: Some(
1✔
3748
                        BoundingBox2D::new(
1✔
3749
                            Coordinate2D::new(0.0f64, 0.5),
1✔
3750
                            Coordinate2D::new(2., 1.0),
1✔
3751
                        )
1✔
3752
                        .unwrap(),
1✔
3753
                    ),
1✔
3754
                },
1✔
3755
                phantom: PhantomData,
1✔
3756
            }],
1✔
3757
        )
1✔
3758
        .await;
1✔
3759

3760
        assert_sql_type(
1✔
3761
            &pool,
1✔
3762
            "OgrMetaData",
1✔
3763
            [
1✔
3764
                StaticMetaData::<OgrSourceDataset, VectorResultDescriptor, VectorQueryRectangle> {
1✔
3765
                    loading_info: OgrSourceDataset {
1✔
3766
                        file_name: "test".into(),
1✔
3767
                        layer_name: "test".to_string(),
1✔
3768
                        data_type: Some(VectorDataType::MultiPoint),
1✔
3769
                        time: OgrSourceDatasetTimeType::Start {
1✔
3770
                            start_field: "start".to_string(),
1✔
3771
                            start_format: OgrSourceTimeFormat::Auto,
1✔
3772
                            duration: OgrSourceDurationSpec::Zero,
1✔
3773
                        },
1✔
3774
                        default_geometry: Some(TypedGeometry::MultiPoint(
1✔
3775
                            MultiPoint::new(vec![
1✔
3776
                                Coordinate2D::new(0.0f64, 0.5),
1✔
3777
                                Coordinate2D::new(2., 1.0),
1✔
3778
                            ])
1✔
3779
                            .unwrap(),
1✔
3780
                        )),
1✔
3781
                        columns: Some(OgrSourceColumnSpec {
1✔
3782
                            format_specifics: Some(FormatSpecifics::Csv {
1✔
3783
                                header: CsvHeader::Auto,
1✔
3784
                            }),
1✔
3785
                            x: "x".to_string(),
1✔
3786
                            y: Some("y".to_string()),
1✔
3787
                            int: vec!["int".to_string()],
1✔
3788
                            float: vec!["float".to_string()],
1✔
3789
                            text: vec!["text".to_string()],
1✔
3790
                            bool: vec!["bool".to_string()],
1✔
3791
                            datetime: vec!["datetime".to_string()],
1✔
3792
                            rename: Some(
1✔
3793
                                [
1✔
3794
                                    ("xx".to_string(), "xx_renamed".to_string()),
1✔
3795
                                    ("yx".to_string(), "yy_renamed".to_string()),
1✔
3796
                                ]
1✔
3797
                                .into(),
1✔
3798
                            ),
1✔
3799
                        }),
1✔
3800
                        force_ogr_time_filter: false,
1✔
3801
                        force_ogr_spatial_filter: true,
1✔
3802
                        on_error: OgrSourceErrorSpec::Abort,
1✔
3803
                        sql_query: None,
1✔
3804
                        attribute_query: Some("foo = 'bar'".to_string()),
1✔
3805
                        cache_ttl: CacheTtlSeconds::new(5),
1✔
3806
                    },
1✔
3807
                    result_descriptor: VectorResultDescriptor {
1✔
3808
                        data_type: VectorDataType::MultiPoint,
1✔
3809
                        spatial_reference: SpatialReferenceOption::SpatialReference(
1✔
3810
                            SpatialReference::epsg_4326(),
1✔
3811
                        ),
1✔
3812
                        columns: [(
1✔
3813
                            "foo".to_string(),
1✔
3814
                            VectorColumnInfo {
1✔
3815
                                data_type: FeatureDataType::Int,
1✔
3816
                                measurement: Measurement::Unitless,
1✔
3817
                            },
1✔
3818
                        )]
1✔
3819
                        .into(),
1✔
3820
                        time: Some(TimeInterval::default()),
1✔
3821
                        bbox: Some(
1✔
3822
                            BoundingBox2D::new(
1✔
3823
                                Coordinate2D::new(0.0f64, 0.5),
1✔
3824
                                Coordinate2D::new(2., 1.0),
1✔
3825
                            )
1✔
3826
                            .unwrap(),
1✔
3827
                        ),
1✔
3828
                    },
1✔
3829
                    phantom: PhantomData,
1✔
3830
                },
1✔
3831
            ],
1✔
3832
        )
1✔
3833
        .await;
1✔
3834

3835
        assert_sql_type(
1✔
3836
            &pool,
1✔
3837
            "GdalDatasetGeoTransform",
1✔
3838
            [GdalDatasetGeoTransform {
1✔
3839
                origin_coordinate: Coordinate2D::new(0.0f64, 0.5),
1✔
3840
                x_pixel_size: 1.0,
1✔
3841
                y_pixel_size: 2.0,
1✔
3842
            }],
1✔
3843
        )
1✔
3844
        .await;
1✔
3845

3846
        assert_sql_type(
1✔
3847
            &pool,
1✔
3848
            "FileNotFoundHandling",
1✔
3849
            [FileNotFoundHandling::NoData, FileNotFoundHandling::Error],
1✔
3850
        )
1✔
3851
        .await;
1✔
3852

3853
        assert_sql_type(
1✔
3854
            &pool,
1✔
3855
            "GdalMetadataMapping",
1✔
3856
            [GdalMetadataMapping {
1✔
3857
                source_key: RasterPropertiesKey {
1✔
3858
                    domain: None,
1✔
3859
                    key: "foo".to_string(),
1✔
3860
                },
1✔
3861
                target_key: RasterPropertiesKey {
1✔
3862
                    domain: Some("bar".to_string()),
1✔
3863
                    key: "foo".to_string(),
1✔
3864
                },
1✔
3865
                target_type: RasterPropertiesEntryType::String,
1✔
3866
            }],
1✔
3867
        )
1✔
3868
        .await;
1✔
3869

3870
        assert_sql_type(
1✔
3871
            &pool,
1✔
3872
            "StringPair",
1✔
3873
            [StringPair::from(("foo".to_string(), "bar".to_string()))],
1✔
3874
        )
1✔
3875
        .await;
1✔
3876

3877
        assert_sql_type(
1✔
3878
            &pool,
1✔
3879
            "GdalDatasetParameters",
1✔
3880
            [GdalDatasetParameters {
1✔
3881
                file_path: "text".into(),
1✔
3882
                rasterband_channel: 1,
1✔
3883
                geo_transform: GdalDatasetGeoTransform {
1✔
3884
                    origin_coordinate: Coordinate2D::new(0.0f64, 0.5),
1✔
3885
                    x_pixel_size: 1.0,
1✔
3886
                    y_pixel_size: 2.0,
1✔
3887
                },
1✔
3888
                width: 42,
1✔
3889
                height: 23,
1✔
3890
                file_not_found_handling: FileNotFoundHandling::NoData,
1✔
3891
                no_data_value: Some(42.0),
1✔
3892
                properties_mapping: Some(vec![GdalMetadataMapping {
1✔
3893
                    source_key: RasterPropertiesKey {
1✔
3894
                        domain: None,
1✔
3895
                        key: "foo".to_string(),
1✔
3896
                    },
1✔
3897
                    target_key: RasterPropertiesKey {
1✔
3898
                        domain: Some("bar".to_string()),
1✔
3899
                        key: "foo".to_string(),
1✔
3900
                    },
1✔
3901
                    target_type: RasterPropertiesEntryType::String,
1✔
3902
                }]),
1✔
3903
                gdal_open_options: Some(vec!["foo".to_string(), "bar".to_string()]),
1✔
3904
                gdal_config_options: Some(vec![("foo".to_string(), "bar".to_string())]),
1✔
3905
                allow_alphaband_as_mask: false,
1✔
3906
                retry: Some(GdalRetryOptions { max_retries: 3 }),
1✔
3907
            }],
1✔
3908
        )
1✔
3909
        .await;
1✔
3910

3911
        assert_sql_type(
1✔
3912
            &pool,
1✔
3913
            "GdalMetaDataRegular",
1✔
3914
            [GdalMetaDataRegular {
1✔
3915
                result_descriptor: RasterResultDescriptor {
1✔
3916
                    data_type: RasterDataType::U8,
1✔
3917
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
3918
                    time: TimeInterval::new_unchecked(0, 1).into(),
1✔
3919
                    bbox: Some(
1✔
3920
                        SpatialPartition2D::new(
1✔
3921
                            Coordinate2D::new(0.0f64, 1.),
1✔
3922
                            Coordinate2D::new(2., 0.5),
1✔
3923
                        )
1✔
3924
                        .unwrap(),
1✔
3925
                    ),
1✔
3926
                    resolution: Some(SpatialResolution::zero_point_one()),
1✔
3927
                    bands: RasterBandDescriptors::new(vec![RasterBandDescriptor::new(
1✔
3928
                        "band".into(),
1✔
3929
                        Measurement::Continuous(ContinuousMeasurement {
1✔
3930
                            measurement: "Temperature".to_string(),
1✔
3931
                            unit: Some("°C".to_string()),
1✔
3932
                        }),
1✔
3933
                    )])
1✔
3934
                    .unwrap(),
1✔
3935
                },
1✔
3936
                params: GdalDatasetParameters {
1✔
3937
                    file_path: "text".into(),
1✔
3938
                    rasterband_channel: 1,
1✔
3939
                    geo_transform: GdalDatasetGeoTransform {
1✔
3940
                        origin_coordinate: Coordinate2D::new(0.0f64, 0.5),
1✔
3941
                        x_pixel_size: 1.0,
1✔
3942
                        y_pixel_size: 2.0,
1✔
3943
                    },
1✔
3944
                    width: 42,
1✔
3945
                    height: 23,
1✔
3946
                    file_not_found_handling: FileNotFoundHandling::NoData,
1✔
3947
                    no_data_value: Some(42.0),
1✔
3948
                    properties_mapping: Some(vec![GdalMetadataMapping {
1✔
3949
                        source_key: RasterPropertiesKey {
1✔
3950
                            domain: None,
1✔
3951
                            key: "foo".to_string(),
1✔
3952
                        },
1✔
3953
                        target_key: RasterPropertiesKey {
1✔
3954
                            domain: Some("bar".to_string()),
1✔
3955
                            key: "foo".to_string(),
1✔
3956
                        },
1✔
3957
                        target_type: RasterPropertiesEntryType::String,
1✔
3958
                    }]),
1✔
3959
                    gdal_open_options: Some(vec!["foo".to_string(), "bar".to_string()]),
1✔
3960
                    gdal_config_options: Some(vec![("foo".to_string(), "bar".to_string())]),
1✔
3961
                    allow_alphaband_as_mask: false,
1✔
3962
                    retry: Some(GdalRetryOptions { max_retries: 3 }),
1✔
3963
                },
1✔
3964
                time_placeholders: [(
1✔
3965
                    "foo".to_string(),
1✔
3966
                    GdalSourceTimePlaceholder {
1✔
3967
                        format: geoengine_datatypes::primitives::DateTimeParseFormat::unix(),
1✔
3968
                        reference: TimeReference::Start,
1✔
3969
                    },
1✔
3970
                )]
1✔
3971
                .into(),
1✔
3972
                data_time: TimeInterval::new_unchecked(0, 1),
1✔
3973
                step: TimeStep {
1✔
3974
                    granularity: TimeGranularity::Millis,
1✔
3975
                    step: 1,
1✔
3976
                },
1✔
3977
                cache_ttl: CacheTtlSeconds::max(),
1✔
3978
            }],
1✔
3979
        )
1✔
3980
        .await;
1✔
3981

3982
        assert_sql_type(
1✔
3983
            &pool,
1✔
3984
            "GdalMetaDataStatic",
1✔
3985
            [GdalMetaDataStatic {
1✔
3986
                time: Some(TimeInterval::new_unchecked(0, 1)),
1✔
3987
                result_descriptor: RasterResultDescriptor {
1✔
3988
                    data_type: RasterDataType::U8,
1✔
3989
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
3990
                    time: TimeInterval::new_unchecked(0, 1).into(),
1✔
3991
                    bbox: Some(
1✔
3992
                        SpatialPartition2D::new(
1✔
3993
                            Coordinate2D::new(0.0f64, 1.),
1✔
3994
                            Coordinate2D::new(2., 0.5),
1✔
3995
                        )
1✔
3996
                        .unwrap(),
1✔
3997
                    ),
1✔
3998
                    resolution: Some(SpatialResolution::zero_point_one()),
1✔
3999
                    bands: RasterBandDescriptors::new(vec![RasterBandDescriptor::new(
1✔
4000
                        "band".into(),
1✔
4001
                        Measurement::Continuous(ContinuousMeasurement {
1✔
4002
                            measurement: "Temperature".to_string(),
1✔
4003
                            unit: Some("°C".to_string()),
1✔
4004
                        }),
1✔
4005
                    )])
1✔
4006
                    .unwrap(),
1✔
4007
                },
1✔
4008
                params: GdalDatasetParameters {
1✔
4009
                    file_path: "text".into(),
1✔
4010
                    rasterband_channel: 1,
1✔
4011
                    geo_transform: GdalDatasetGeoTransform {
1✔
4012
                        origin_coordinate: Coordinate2D::new(0.0f64, 0.5),
1✔
4013
                        x_pixel_size: 1.0,
1✔
4014
                        y_pixel_size: 2.0,
1✔
4015
                    },
1✔
4016
                    width: 42,
1✔
4017
                    height: 23,
1✔
4018
                    file_not_found_handling: FileNotFoundHandling::NoData,
1✔
4019
                    no_data_value: Some(42.0),
1✔
4020
                    properties_mapping: Some(vec![GdalMetadataMapping {
1✔
4021
                        source_key: RasterPropertiesKey {
1✔
4022
                            domain: None,
1✔
4023
                            key: "foo".to_string(),
1✔
4024
                        },
1✔
4025
                        target_key: RasterPropertiesKey {
1✔
4026
                            domain: Some("bar".to_string()),
1✔
4027
                            key: "foo".to_string(),
1✔
4028
                        },
1✔
4029
                        target_type: RasterPropertiesEntryType::String,
1✔
4030
                    }]),
1✔
4031
                    gdal_open_options: Some(vec!["foo".to_string(), "bar".to_string()]),
1✔
4032
                    gdal_config_options: Some(vec![("foo".to_string(), "bar".to_string())]),
1✔
4033
                    allow_alphaband_as_mask: false,
1✔
4034
                    retry: Some(GdalRetryOptions { max_retries: 3 }),
1✔
4035
                },
1✔
4036
                cache_ttl: CacheTtlSeconds::max(),
1✔
4037
            }],
1✔
4038
        )
1✔
4039
        .await;
1✔
4040

4041
        assert_sql_type(
1✔
4042
            &pool,
1✔
4043
            "GdalMetadataNetCdfCf",
1✔
4044
            [GdalMetadataNetCdfCf {
1✔
4045
                result_descriptor: RasterResultDescriptor {
1✔
4046
                    data_type: RasterDataType::U8,
1✔
4047
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
4048
                    time: TimeInterval::new_unchecked(0, 1).into(),
1✔
4049
                    bbox: Some(
1✔
4050
                        SpatialPartition2D::new(
1✔
4051
                            Coordinate2D::new(0.0f64, 1.),
1✔
4052
                            Coordinate2D::new(2., 0.5),
1✔
4053
                        )
1✔
4054
                        .unwrap(),
1✔
4055
                    ),
1✔
4056
                    resolution: Some(SpatialResolution::zero_point_one()),
1✔
4057
                    bands: RasterBandDescriptors::new(vec![RasterBandDescriptor::new(
1✔
4058
                        "band".into(),
1✔
4059
                        Measurement::Continuous(ContinuousMeasurement {
1✔
4060
                            measurement: "Temperature".to_string(),
1✔
4061
                            unit: Some("°C".to_string()),
1✔
4062
                        }),
1✔
4063
                    )])
1✔
4064
                    .unwrap(),
1✔
4065
                },
1✔
4066
                params: GdalDatasetParameters {
1✔
4067
                    file_path: "text".into(),
1✔
4068
                    rasterband_channel: 1,
1✔
4069
                    geo_transform: GdalDatasetGeoTransform {
1✔
4070
                        origin_coordinate: Coordinate2D::new(0.0f64, 0.5),
1✔
4071
                        x_pixel_size: 1.0,
1✔
4072
                        y_pixel_size: 2.0,
1✔
4073
                    },
1✔
4074
                    width: 42,
1✔
4075
                    height: 23,
1✔
4076
                    file_not_found_handling: FileNotFoundHandling::NoData,
1✔
4077
                    no_data_value: Some(42.0),
1✔
4078
                    properties_mapping: Some(vec![GdalMetadataMapping {
1✔
4079
                        source_key: RasterPropertiesKey {
1✔
4080
                            domain: None,
1✔
4081
                            key: "foo".to_string(),
1✔
4082
                        },
1✔
4083
                        target_key: RasterPropertiesKey {
1✔
4084
                            domain: Some("bar".to_string()),
1✔
4085
                            key: "foo".to_string(),
1✔
4086
                        },
1✔
4087
                        target_type: RasterPropertiesEntryType::String,
1✔
4088
                    }]),
1✔
4089
                    gdal_open_options: Some(vec!["foo".to_string(), "bar".to_string()]),
1✔
4090
                    gdal_config_options: Some(vec![("foo".to_string(), "bar".to_string())]),
1✔
4091
                    allow_alphaband_as_mask: false,
1✔
4092
                    retry: Some(GdalRetryOptions { max_retries: 3 }),
1✔
4093
                },
1✔
4094
                start: TimeInstance::from_millis(0).unwrap(),
1✔
4095
                end: TimeInstance::from_millis(1000).unwrap(),
1✔
4096
                cache_ttl: CacheTtlSeconds::max(),
1✔
4097
                step: TimeStep {
1✔
4098
                    granularity: TimeGranularity::Millis,
1✔
4099
                    step: 1,
1✔
4100
                },
1✔
4101
                band_offset: 3,
1✔
4102
            }],
1✔
4103
        )
1✔
4104
        .await;
1✔
4105

4106
        assert_sql_type(
1✔
4107
            &pool,
1✔
4108
            "GdalMetaDataList",
1✔
4109
            [GdalMetaDataList {
1✔
4110
                result_descriptor: RasterResultDescriptor {
1✔
4111
                    data_type: RasterDataType::U8,
1✔
4112
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
4113
                    time: TimeInterval::new_unchecked(0, 1).into(),
1✔
4114
                    bbox: Some(
1✔
4115
                        SpatialPartition2D::new(
1✔
4116
                            Coordinate2D::new(0.0f64, 1.),
1✔
4117
                            Coordinate2D::new(2., 0.5),
1✔
4118
                        )
1✔
4119
                        .unwrap(),
1✔
4120
                    ),
1✔
4121
                    resolution: Some(SpatialResolution::zero_point_one()),
1✔
4122
                    bands: RasterBandDescriptors::new(vec![RasterBandDescriptor::new(
1✔
4123
                        "band".into(),
1✔
4124
                        Measurement::Continuous(ContinuousMeasurement {
1✔
4125
                            measurement: "Temperature".to_string(),
1✔
4126
                            unit: Some("°C".to_string()),
1✔
4127
                        }),
1✔
4128
                    )])
1✔
4129
                    .unwrap(),
1✔
4130
                },
1✔
4131
                params: vec![GdalLoadingInfoTemporalSlice {
1✔
4132
                    time: TimeInterval::new_unchecked(0, 1),
1✔
4133
                    params: Some(GdalDatasetParameters {
1✔
4134
                        file_path: "text".into(),
1✔
4135
                        rasterband_channel: 1,
1✔
4136
                        geo_transform: GdalDatasetGeoTransform {
1✔
4137
                            origin_coordinate: Coordinate2D::new(0.0f64, 0.5),
1✔
4138
                            x_pixel_size: 1.0,
1✔
4139
                            y_pixel_size: 2.0,
1✔
4140
                        },
1✔
4141
                        width: 42,
1✔
4142
                        height: 23,
1✔
4143
                        file_not_found_handling: FileNotFoundHandling::NoData,
1✔
4144
                        no_data_value: Some(42.0),
1✔
4145
                        properties_mapping: Some(vec![GdalMetadataMapping {
1✔
4146
                            source_key: RasterPropertiesKey {
1✔
4147
                                domain: None,
1✔
4148
                                key: "foo".to_string(),
1✔
4149
                            },
1✔
4150
                            target_key: RasterPropertiesKey {
1✔
4151
                                domain: Some("bar".to_string()),
1✔
4152
                                key: "foo".to_string(),
1✔
4153
                            },
1✔
4154
                            target_type: RasterPropertiesEntryType::String,
1✔
4155
                        }]),
1✔
4156
                        gdal_open_options: Some(vec!["foo".to_string(), "bar".to_string()]),
1✔
4157
                        gdal_config_options: Some(vec![("foo".to_string(), "bar".to_string())]),
1✔
4158
                        allow_alphaband_as_mask: false,
1✔
4159
                        retry: Some(GdalRetryOptions { max_retries: 3 }),
1✔
4160
                    }),
1✔
4161
                    cache_ttl: CacheTtlSeconds::max(),
1✔
4162
                }],
1✔
4163
            }],
1✔
4164
        )
1✔
4165
        .await;
1✔
4166

4167
        assert_sql_type(
1✔
4168
            &pool,
1✔
4169
            "MetaDataDefinition",
1✔
4170
            [
1✔
4171
                MetaDataDefinition::MockMetaData(StaticMetaData::<
1✔
4172
                    MockDatasetDataSourceLoadingInfo,
1✔
4173
                    VectorResultDescriptor,
1✔
4174
                    VectorQueryRectangle,
1✔
4175
                > {
1✔
4176
                    loading_info: MockDatasetDataSourceLoadingInfo {
1✔
4177
                        points: vec![Coordinate2D::new(0.0f64, 0.5), Coordinate2D::new(2., 1.0)],
1✔
4178
                    },
1✔
4179
                    result_descriptor: VectorResultDescriptor {
1✔
4180
                        data_type: VectorDataType::MultiPoint,
1✔
4181
                        spatial_reference: SpatialReferenceOption::SpatialReference(
1✔
4182
                            SpatialReference::epsg_4326(),
1✔
4183
                        ),
1✔
4184
                        columns: [(
1✔
4185
                            "foo".to_string(),
1✔
4186
                            VectorColumnInfo {
1✔
4187
                                data_type: FeatureDataType::Int,
1✔
4188
                                measurement: Measurement::Unitless,
1✔
4189
                            },
1✔
4190
                        )]
1✔
4191
                        .into(),
1✔
4192
                        time: Some(TimeInterval::default()),
1✔
4193
                        bbox: Some(
1✔
4194
                            BoundingBox2D::new(
1✔
4195
                                Coordinate2D::new(0.0f64, 0.5),
1✔
4196
                                Coordinate2D::new(2., 1.0),
1✔
4197
                            )
1✔
4198
                            .unwrap(),
1✔
4199
                        ),
1✔
4200
                    },
1✔
4201
                    phantom: PhantomData,
1✔
4202
                }),
1✔
4203
                MetaDataDefinition::OgrMetaData(StaticMetaData::<
1✔
4204
                    OgrSourceDataset,
1✔
4205
                    VectorResultDescriptor,
1✔
4206
                    VectorQueryRectangle,
1✔
4207
                > {
1✔
4208
                    loading_info: OgrSourceDataset {
1✔
4209
                        file_name: "test".into(),
1✔
4210
                        layer_name: "test".to_string(),
1✔
4211
                        data_type: Some(VectorDataType::MultiPoint),
1✔
4212
                        time: OgrSourceDatasetTimeType::Start {
1✔
4213
                            start_field: "start".to_string(),
1✔
4214
                            start_format: OgrSourceTimeFormat::Auto,
1✔
4215
                            duration: OgrSourceDurationSpec::Zero,
1✔
4216
                        },
1✔
4217
                        default_geometry: Some(TypedGeometry::MultiPoint(
1✔
4218
                            MultiPoint::new(vec![
1✔
4219
                                Coordinate2D::new(0.0f64, 0.5),
1✔
4220
                                Coordinate2D::new(2., 1.0),
1✔
4221
                            ])
1✔
4222
                            .unwrap(),
1✔
4223
                        )),
1✔
4224
                        columns: Some(OgrSourceColumnSpec {
1✔
4225
                            format_specifics: Some(FormatSpecifics::Csv {
1✔
4226
                                header: CsvHeader::Auto,
1✔
4227
                            }),
1✔
4228
                            x: "x".to_string(),
1✔
4229
                            y: Some("y".to_string()),
1✔
4230
                            int: vec!["int".to_string()],
1✔
4231
                            float: vec!["float".to_string()],
1✔
4232
                            text: vec!["text".to_string()],
1✔
4233
                            bool: vec!["bool".to_string()],
1✔
4234
                            datetime: vec!["datetime".to_string()],
1✔
4235
                            rename: Some(
1✔
4236
                                [
1✔
4237
                                    ("xx".to_string(), "xx_renamed".to_string()),
1✔
4238
                                    ("yx".to_string(), "yy_renamed".to_string()),
1✔
4239
                                ]
1✔
4240
                                .into(),
1✔
4241
                            ),
1✔
4242
                        }),
1✔
4243
                        force_ogr_time_filter: false,
1✔
4244
                        force_ogr_spatial_filter: true,
1✔
4245
                        on_error: OgrSourceErrorSpec::Abort,
1✔
4246
                        sql_query: None,
1✔
4247
                        attribute_query: Some("foo = 'bar'".to_string()),
1✔
4248
                        cache_ttl: CacheTtlSeconds::new(5),
1✔
4249
                    },
1✔
4250
                    result_descriptor: VectorResultDescriptor {
1✔
4251
                        data_type: VectorDataType::MultiPoint,
1✔
4252
                        spatial_reference: SpatialReferenceOption::SpatialReference(
1✔
4253
                            SpatialReference::epsg_4326(),
1✔
4254
                        ),
1✔
4255
                        columns: [(
1✔
4256
                            "foo".to_string(),
1✔
4257
                            VectorColumnInfo {
1✔
4258
                                data_type: FeatureDataType::Int,
1✔
4259
                                measurement: Measurement::Unitless,
1✔
4260
                            },
1✔
4261
                        )]
1✔
4262
                        .into(),
1✔
4263
                        time: Some(TimeInterval::default()),
1✔
4264
                        bbox: Some(
1✔
4265
                            BoundingBox2D::new(
1✔
4266
                                Coordinate2D::new(0.0f64, 0.5),
1✔
4267
                                Coordinate2D::new(2., 1.0),
1✔
4268
                            )
1✔
4269
                            .unwrap(),
1✔
4270
                        ),
1✔
4271
                    },
1✔
4272
                    phantom: PhantomData,
1✔
4273
                }),
1✔
4274
                MetaDataDefinition::GdalMetaDataRegular(GdalMetaDataRegular {
1✔
4275
                    result_descriptor: RasterResultDescriptor {
1✔
4276
                        data_type: RasterDataType::U8,
1✔
4277
                        spatial_reference: SpatialReference::epsg_4326().into(),
1✔
4278
                        time: TimeInterval::new_unchecked(0, 1).into(),
1✔
4279
                        bbox: Some(
1✔
4280
                            SpatialPartition2D::new(
1✔
4281
                                Coordinate2D::new(0.0f64, 1.),
1✔
4282
                                Coordinate2D::new(2., 0.5),
1✔
4283
                            )
1✔
4284
                            .unwrap(),
1✔
4285
                        ),
1✔
4286
                        resolution: Some(SpatialResolution::zero_point_one()),
1✔
4287
                        bands: RasterBandDescriptors::new(vec![RasterBandDescriptor::new(
1✔
4288
                            "band".into(),
1✔
4289
                            Measurement::Continuous(ContinuousMeasurement {
1✔
4290
                                measurement: "Temperature".to_string(),
1✔
4291
                                unit: Some("°C".to_string()),
1✔
4292
                            }),
1✔
4293
                        )])
1✔
4294
                        .unwrap(),
1✔
4295
                    },
1✔
4296
                    params: GdalDatasetParameters {
1✔
4297
                        file_path: "text".into(),
1✔
4298
                        rasterband_channel: 1,
1✔
4299
                        geo_transform: GdalDatasetGeoTransform {
1✔
4300
                            origin_coordinate: Coordinate2D::new(0.0f64, 0.5),
1✔
4301
                            x_pixel_size: 1.0,
1✔
4302
                            y_pixel_size: 2.0,
1✔
4303
                        },
1✔
4304
                        width: 42,
1✔
4305
                        height: 23,
1✔
4306
                        file_not_found_handling: FileNotFoundHandling::NoData,
1✔
4307
                        no_data_value: Some(42.0),
1✔
4308
                        properties_mapping: Some(vec![GdalMetadataMapping {
1✔
4309
                            source_key: RasterPropertiesKey {
1✔
4310
                                domain: None,
1✔
4311
                                key: "foo".to_string(),
1✔
4312
                            },
1✔
4313
                            target_key: RasterPropertiesKey {
1✔
4314
                                domain: Some("bar".to_string()),
1✔
4315
                                key: "foo".to_string(),
1✔
4316
                            },
1✔
4317
                            target_type: RasterPropertiesEntryType::String,
1✔
4318
                        }]),
1✔
4319
                        gdal_open_options: Some(vec!["foo".to_string(), "bar".to_string()]),
1✔
4320
                        gdal_config_options: Some(vec![("foo".to_string(), "bar".to_string())]),
1✔
4321
                        allow_alphaband_as_mask: false,
1✔
4322
                        retry: Some(GdalRetryOptions { max_retries: 3 }),
1✔
4323
                    },
1✔
4324
                    time_placeholders: [(
1✔
4325
                        "foo".to_string(),
1✔
4326
                        GdalSourceTimePlaceholder {
1✔
4327
                            format: DateTimeParseFormat::unix(),
1✔
4328
                            reference: TimeReference::Start,
1✔
4329
                        },
1✔
4330
                    )]
1✔
4331
                    .into(),
1✔
4332
                    data_time: TimeInterval::new_unchecked(0, 1),
1✔
4333
                    step: TimeStep {
1✔
4334
                        granularity: TimeGranularity::Millis,
1✔
4335
                        step: 1,
1✔
4336
                    },
1✔
4337
                    cache_ttl: CacheTtlSeconds::max(),
1✔
4338
                }),
1✔
4339
                MetaDataDefinition::GdalStatic(GdalMetaDataStatic {
1✔
4340
                    time: Some(TimeInterval::new_unchecked(0, 1)),
1✔
4341
                    result_descriptor: RasterResultDescriptor {
1✔
4342
                        data_type: RasterDataType::U8,
1✔
4343
                        spatial_reference: SpatialReference::epsg_4326().into(),
1✔
4344
                        time: TimeInterval::new_unchecked(0, 1).into(),
1✔
4345
                        bbox: Some(
1✔
4346
                            SpatialPartition2D::new(
1✔
4347
                                Coordinate2D::new(0.0f64, 1.),
1✔
4348
                                Coordinate2D::new(2., 0.5),
1✔
4349
                            )
1✔
4350
                            .unwrap(),
1✔
4351
                        ),
1✔
4352
                        resolution: Some(SpatialResolution::zero_point_one()),
1✔
4353
                        bands: RasterBandDescriptors::new(vec![RasterBandDescriptor::new(
1✔
4354
                            "band".into(),
1✔
4355
                            Measurement::Continuous(ContinuousMeasurement {
1✔
4356
                                measurement: "Temperature".to_string(),
1✔
4357
                                unit: Some("°C".to_string()),
1✔
4358
                            }),
1✔
4359
                        )])
1✔
4360
                        .unwrap(),
1✔
4361
                    },
1✔
4362
                    params: GdalDatasetParameters {
1✔
4363
                        file_path: "text".into(),
1✔
4364
                        rasterband_channel: 1,
1✔
4365
                        geo_transform: GdalDatasetGeoTransform {
1✔
4366
                            origin_coordinate: Coordinate2D::new(0.0f64, 0.5),
1✔
4367
                            x_pixel_size: 1.0,
1✔
4368
                            y_pixel_size: 2.0,
1✔
4369
                        },
1✔
4370
                        width: 42,
1✔
4371
                        height: 23,
1✔
4372
                        file_not_found_handling: FileNotFoundHandling::NoData,
1✔
4373
                        no_data_value: Some(42.0),
1✔
4374
                        properties_mapping: Some(vec![GdalMetadataMapping {
1✔
4375
                            source_key: RasterPropertiesKey {
1✔
4376
                                domain: None,
1✔
4377
                                key: "foo".to_string(),
1✔
4378
                            },
1✔
4379
                            target_key: RasterPropertiesKey {
1✔
4380
                                domain: Some("bar".to_string()),
1✔
4381
                                key: "foo".to_string(),
1✔
4382
                            },
1✔
4383
                            target_type: RasterPropertiesEntryType::String,
1✔
4384
                        }]),
1✔
4385
                        gdal_open_options: Some(vec!["foo".to_string(), "bar".to_string()]),
1✔
4386
                        gdal_config_options: Some(vec![("foo".to_string(), "bar".to_string())]),
1✔
4387
                        allow_alphaband_as_mask: false,
1✔
4388
                        retry: Some(GdalRetryOptions { max_retries: 3 }),
1✔
4389
                    },
1✔
4390
                    cache_ttl: CacheTtlSeconds::max(),
1✔
4391
                }),
1✔
4392
                MetaDataDefinition::GdalMetadataNetCdfCf(GdalMetadataNetCdfCf {
1✔
4393
                    result_descriptor: RasterResultDescriptor {
1✔
4394
                        data_type: RasterDataType::U8,
1✔
4395
                        spatial_reference: SpatialReference::epsg_4326().into(),
1✔
4396
                        time: TimeInterval::new_unchecked(0, 1).into(),
1✔
4397
                        bbox: Some(
1✔
4398
                            SpatialPartition2D::new(
1✔
4399
                                Coordinate2D::new(0.0f64, 1.),
1✔
4400
                                Coordinate2D::new(2., 0.5),
1✔
4401
                            )
1✔
4402
                            .unwrap(),
1✔
4403
                        ),
1✔
4404
                        resolution: Some(SpatialResolution::zero_point_one()),
1✔
4405
                        bands: RasterBandDescriptors::new(vec![RasterBandDescriptor::new(
1✔
4406
                            "band".into(),
1✔
4407
                            Measurement::Continuous(ContinuousMeasurement {
1✔
4408
                                measurement: "Temperature".to_string(),
1✔
4409
                                unit: Some("°C".to_string()),
1✔
4410
                            }),
1✔
4411
                        )])
1✔
4412
                        .unwrap(),
1✔
4413
                    },
1✔
4414
                    params: GdalDatasetParameters {
1✔
4415
                        file_path: "text".into(),
1✔
4416
                        rasterband_channel: 1,
1✔
4417
                        geo_transform: GdalDatasetGeoTransform {
1✔
4418
                            origin_coordinate: Coordinate2D::new(0.0f64, 0.5),
1✔
4419
                            x_pixel_size: 1.0,
1✔
4420
                            y_pixel_size: 2.0,
1✔
4421
                        },
1✔
4422
                        width: 42,
1✔
4423
                        height: 23,
1✔
4424
                        file_not_found_handling: FileNotFoundHandling::NoData,
1✔
4425
                        no_data_value: Some(42.0),
1✔
4426
                        properties_mapping: Some(vec![GdalMetadataMapping {
1✔
4427
                            source_key: RasterPropertiesKey {
1✔
4428
                                domain: None,
1✔
4429
                                key: "foo".to_string(),
1✔
4430
                            },
1✔
4431
                            target_key: RasterPropertiesKey {
1✔
4432
                                domain: Some("bar".to_string()),
1✔
4433
                                key: "foo".to_string(),
1✔
4434
                            },
1✔
4435
                            target_type: RasterPropertiesEntryType::String,
1✔
4436
                        }]),
1✔
4437
                        gdal_open_options: Some(vec!["foo".to_string(), "bar".to_string()]),
1✔
4438
                        gdal_config_options: Some(vec![("foo".to_string(), "bar".to_string())]),
1✔
4439
                        allow_alphaband_as_mask: false,
1✔
4440
                        retry: Some(GdalRetryOptions { max_retries: 3 }),
1✔
4441
                    },
1✔
4442
                    start: TimeInstance::from_millis(0).unwrap(),
1✔
4443
                    end: TimeInstance::from_millis(1000).unwrap(),
1✔
4444
                    cache_ttl: CacheTtlSeconds::max(),
1✔
4445
                    step: TimeStep {
1✔
4446
                        granularity: TimeGranularity::Millis,
1✔
4447
                        step: 1,
1✔
4448
                    },
1✔
4449
                    band_offset: 3,
1✔
4450
                }),
1✔
4451
                MetaDataDefinition::GdalMetaDataList(GdalMetaDataList {
1✔
4452
                    result_descriptor: RasterResultDescriptor {
1✔
4453
                        data_type: RasterDataType::U8,
1✔
4454
                        spatial_reference: SpatialReference::epsg_4326().into(),
1✔
4455
                        time: TimeInterval::new_unchecked(0, 1).into(),
1✔
4456
                        bbox: Some(
1✔
4457
                            SpatialPartition2D::new(
1✔
4458
                                Coordinate2D::new(0.0f64, 1.),
1✔
4459
                                Coordinate2D::new(2., 0.5),
1✔
4460
                            )
1✔
4461
                            .unwrap(),
1✔
4462
                        ),
1✔
4463
                        resolution: Some(SpatialResolution::zero_point_one()),
1✔
4464
                        bands: RasterBandDescriptors::new(vec![RasterBandDescriptor::new(
1✔
4465
                            "band".into(),
1✔
4466
                            Measurement::Continuous(ContinuousMeasurement {
1✔
4467
                                measurement: "Temperature".to_string(),
1✔
4468
                                unit: Some("°C".to_string()),
1✔
4469
                            }),
1✔
4470
                        )])
1✔
4471
                        .unwrap(),
1✔
4472
                    },
1✔
4473
                    params: vec![GdalLoadingInfoTemporalSlice {
1✔
4474
                        time: TimeInterval::new_unchecked(0, 1),
1✔
4475
                        params: Some(GdalDatasetParameters {
1✔
4476
                            file_path: "text".into(),
1✔
4477
                            rasterband_channel: 1,
1✔
4478
                            geo_transform: GdalDatasetGeoTransform {
1✔
4479
                                origin_coordinate: Coordinate2D::new(0.0f64, 0.5),
1✔
4480
                                x_pixel_size: 1.0,
1✔
4481
                                y_pixel_size: 2.0,
1✔
4482
                            },
1✔
4483
                            width: 42,
1✔
4484
                            height: 23,
1✔
4485
                            file_not_found_handling: FileNotFoundHandling::NoData,
1✔
4486
                            no_data_value: Some(42.0),
1✔
4487
                            properties_mapping: Some(vec![GdalMetadataMapping {
1✔
4488
                                source_key: RasterPropertiesKey {
1✔
4489
                                    domain: None,
1✔
4490
                                    key: "foo".to_string(),
1✔
4491
                                },
1✔
4492
                                target_key: RasterPropertiesKey {
1✔
4493
                                    domain: Some("bar".to_string()),
1✔
4494
                                    key: "foo".to_string(),
1✔
4495
                                },
1✔
4496
                                target_type: RasterPropertiesEntryType::String,
1✔
4497
                            }]),
1✔
4498
                            gdal_open_options: Some(vec!["foo".to_string(), "bar".to_string()]),
1✔
4499
                            gdal_config_options: Some(vec![("foo".to_string(), "bar".to_string())]),
1✔
4500
                            allow_alphaband_as_mask: false,
1✔
4501
                            retry: None,
1✔
4502
                        }),
1✔
4503
                        cache_ttl: CacheTtlSeconds::max(),
1✔
4504
                    }],
1✔
4505
                }),
1✔
4506
            ],
1✔
4507
        )
1✔
4508
        .await;
1✔
4509

4510
        assert_sql_type(
1✔
4511
            &pool,
1✔
4512
            "bytea",
1✔
4513
            [U96::from(
1✔
4514
                arr![u8; 13, 227, 191, 247, 123, 193, 214, 165, 185, 37, 101, 24],
1✔
4515
            )],
1✔
4516
        )
1✔
4517
        .await;
1✔
4518

4519
        test_data_provider_definition_types(&pool).await;
1✔
4520
    }
1✔
4521

4522
    #[test]
4523
    fn test_postgres_config_translation() {
1✔
4524
        let host = "localhost";
1✔
4525
        let port = 8095;
1✔
4526
        let ge_default = "geoengine";
1✔
4527
        let schema = "geoengine";
1✔
4528

1✔
4529
        let db_config = config::Postgres {
1✔
4530
            host: host.to_string(),
1✔
4531
            port,
1✔
4532
            database: ge_default.to_string(),
1✔
4533
            schema: schema.to_string(),
1✔
4534
            user: ge_default.to_string(),
1✔
4535
            password: ge_default.to_string(),
1✔
4536
            clear_database_on_start: false,
1✔
4537
        };
1✔
4538

1✔
4539
        let pg_config = Config::try_from(db_config).unwrap();
1✔
4540

1✔
4541
        assert_eq!(ge_default, pg_config.get_user().unwrap());
1✔
4542
        assert_eq!(
1✔
4543
            <str as AsRef<[u8]>>::as_ref(ge_default).to_vec(),
1✔
4544
            pg_config.get_password().unwrap()
1✔
4545
        );
1✔
4546
        assert_eq!(ge_default, pg_config.get_dbname().unwrap());
1✔
4547
        assert_eq!(
1✔
4548
            &format!("-c search_path={schema}"),
1✔
4549
            pg_config.get_options().unwrap()
1✔
4550
        );
1✔
4551
        assert_eq!(vec![Host::Tcp(host.to_string())], pg_config.get_hosts());
1✔
4552
        assert_eq!(vec![port], pg_config.get_ports());
1✔
4553
    }
1✔
4554

4555
    #[allow(clippy::too_many_lines)]
4556
    async fn test_data_provider_definition_types(
1✔
4557
        pool: &PooledConnection<'_, PostgresConnectionManager<tokio_postgres::NoTls>>,
1✔
4558
    ) {
1✔
4559
        assert_sql_type(
1✔
4560
            pool,
1✔
4561
            "ArunaDataProviderDefinition",
1✔
4562
            [ArunaDataProviderDefinition {
1✔
4563
                id: DataProviderId::from_str("86a7f7ce-1bab-4ce9-a32b-172c0f958ee0").unwrap(),
1✔
4564
                name: "NFDI".to_string(),
1✔
4565
                description: "NFDI".to_string(),
1✔
4566
                priority: Some(33),
1✔
4567
                api_url: "http://test".to_string(),
1✔
4568
                project_id: "project".to_string(),
1✔
4569
                api_token: "api_token".to_string(),
1✔
4570
                filter_label: "filter".to_string(),
1✔
4571
                cache_ttl: CacheTtlSeconds::new(0),
1✔
4572
            }],
1✔
4573
        )
1✔
4574
        .await;
1✔
4575

4576
        assert_sql_type(
1✔
4577
            pool,
1✔
4578
            "GbifDataProviderDefinition",
1✔
4579
            [GbifDataProviderDefinition {
1✔
4580
                name: "GBIF".to_string(),
1✔
4581
                description: "GFBio".to_string(),
1✔
4582
                priority: None,
1✔
4583
                db_config: DatabaseConnectionConfig {
1✔
4584
                    host: "testhost".to_string(),
1✔
4585
                    port: 1234,
1✔
4586
                    database: "testdb".to_string(),
1✔
4587
                    schema: "testschema".to_string(),
1✔
4588
                    user: "testuser".to_string(),
1✔
4589
                    password: "testpass".to_string(),
1✔
4590
                },
1✔
4591
                cache_ttl: CacheTtlSeconds::new(0),
1✔
4592
                autocomplete_timeout: 3,
1✔
4593
                columns: GbifDataProvider::all_columns(),
1✔
4594
            }],
1✔
4595
        )
1✔
4596
        .await;
1✔
4597

4598
        assert_sql_type(
1✔
4599
            pool,
1✔
4600
            "GfbioAbcdDataProviderDefinition",
1✔
4601
            [GfbioAbcdDataProviderDefinition {
1✔
4602
                name: "GFbio".to_string(),
1✔
4603
                description: "GFBio".to_string(),
1✔
4604
                priority: None,
1✔
4605
                db_config: DatabaseConnectionConfig {
1✔
4606
                    host: "testhost".to_string(),
1✔
4607
                    port: 1234,
1✔
4608
                    database: "testdb".to_string(),
1✔
4609
                    schema: "testschema".to_string(),
1✔
4610
                    user: "testuser".to_string(),
1✔
4611
                    password: "testpass".to_string(),
1✔
4612
                },
1✔
4613
                cache_ttl: CacheTtlSeconds::new(0),
1✔
4614
            }],
1✔
4615
        )
1✔
4616
        .await;
1✔
4617

4618
        assert_sql_type(
1✔
4619
            pool,
1✔
4620
            "GfbioCollectionsDataProviderDefinition",
1✔
4621
            [GfbioCollectionsDataProviderDefinition {
1✔
4622
                name: "GFbio".to_string(),
1✔
4623
                description: "GFBio".to_string(),
1✔
4624
                priority: None,
1✔
4625
                collection_api_url: "http://testhost".try_into().unwrap(),
1✔
4626
                collection_api_auth_token: "token".to_string(),
1✔
4627
                abcd_db_config: DatabaseConnectionConfig {
1✔
4628
                    host: "testhost".to_string(),
1✔
4629
                    port: 1234,
1✔
4630
                    database: "testdb".to_string(),
1✔
4631
                    schema: "testschema".to_string(),
1✔
4632
                    user: "testuser".to_string(),
1✔
4633
                    password: "testpass".to_string(),
1✔
4634
                },
1✔
4635
                pangaea_url: "http://panaea".try_into().unwrap(),
1✔
4636
                cache_ttl: CacheTtlSeconds::new(0),
1✔
4637
            }],
1✔
4638
        )
1✔
4639
        .await;
1✔
4640

4641
        assert_sql_type(pool, "\"PropertyType\"[]", [Vec::<Property>::new()]).await;
1✔
4642

4643
        assert_sql_type(
1✔
4644
            pool,
1✔
4645
            "EbvPortalDataProviderDefinition",
1✔
4646
            [EbvPortalDataProviderDefinition {
1✔
4647
                name: "ebv".to_string(),
1✔
4648
                description: "EBV".to_string(),
1✔
4649
                priority: None,
1✔
4650
                data: "a_path".into(),
1✔
4651
                base_url: "http://base".try_into().unwrap(),
1✔
4652
                overviews: "another_path".into(),
1✔
4653
                cache_ttl: CacheTtlSeconds::new(0),
1✔
4654
            }],
1✔
4655
        )
1✔
4656
        .await;
1✔
4657

4658
        assert_sql_type(
1✔
4659
            pool,
1✔
4660
            "NetCdfCfDataProviderDefinition",
1✔
4661
            [NetCdfCfDataProviderDefinition {
1✔
4662
                name: "netcdfcf".to_string(),
1✔
4663
                description: "netcdfcf".to_string(),
1✔
4664
                priority: Some(33),
1✔
4665
                data: "a_path".into(),
1✔
4666
                overviews: "another_path".into(),
1✔
4667
                cache_ttl: CacheTtlSeconds::new(0),
1✔
4668
            }],
1✔
4669
        )
1✔
4670
        .await;
1✔
4671

4672
        assert_sql_type(
1✔
4673
            pool,
1✔
4674
            "PangaeaDataProviderDefinition",
1✔
4675
            [PangaeaDataProviderDefinition {
1✔
4676
                name: "pangaea".to_string(),
1✔
4677
                description: "pangaea".to_string(),
1✔
4678
                priority: None,
1✔
4679
                base_url: "http://base".try_into().unwrap(),
1✔
4680
                cache_ttl: CacheTtlSeconds::new(0),
1✔
4681
            }],
1✔
4682
        )
1✔
4683
        .await;
1✔
4684

4685
        assert_sql_type(
1✔
4686
            pool,
1✔
4687
            "DataProviderDefinition",
1✔
4688
            [
1✔
4689
                TypedDataProviderDefinition::ArunaDataProviderDefinition(
1✔
4690
                    ArunaDataProviderDefinition {
1✔
4691
                        id: DataProviderId::from_str("86a7f7ce-1bab-4ce9-a32b-172c0f958ee0")
1✔
4692
                            .unwrap(),
1✔
4693
                        name: "NFDI".to_string(),
1✔
4694
                        description: "NFDI".to_string(),
1✔
4695
                        priority: Some(33),
1✔
4696
                        api_url: "http://test".to_string(),
1✔
4697
                        project_id: "project".to_string(),
1✔
4698
                        api_token: "api_token".to_string(),
1✔
4699
                        filter_label: "filter".to_string(),
1✔
4700
                        cache_ttl: CacheTtlSeconds::new(0),
1✔
4701
                    },
1✔
4702
                ),
1✔
4703
                TypedDataProviderDefinition::GbifDataProviderDefinition(
1✔
4704
                    GbifDataProviderDefinition {
1✔
4705
                        name: "GBIF".to_string(),
1✔
4706
                        description: "GFBio".to_string(),
1✔
4707
                        priority: None,
1✔
4708
                        db_config: DatabaseConnectionConfig {
1✔
4709
                            host: "testhost".to_string(),
1✔
4710
                            port: 1234,
1✔
4711
                            database: "testdb".to_string(),
1✔
4712
                            schema: "testschema".to_string(),
1✔
4713
                            user: "testuser".to_string(),
1✔
4714
                            password: "testpass".to_string(),
1✔
4715
                        },
1✔
4716
                        cache_ttl: CacheTtlSeconds::new(0),
1✔
4717
                        autocomplete_timeout: 3,
1✔
4718
                        columns: GbifDataProvider::all_columns(),
1✔
4719
                    },
1✔
4720
                ),
1✔
4721
                TypedDataProviderDefinition::GfbioAbcdDataProviderDefinition(
1✔
4722
                    GfbioAbcdDataProviderDefinition {
1✔
4723
                        name: "GFbio".to_string(),
1✔
4724
                        description: "GFBio".to_string(),
1✔
4725
                        priority: None,
1✔
4726
                        db_config: DatabaseConnectionConfig {
1✔
4727
                            host: "testhost".to_string(),
1✔
4728
                            port: 1234,
1✔
4729
                            database: "testdb".to_string(),
1✔
4730
                            schema: "testschema".to_string(),
1✔
4731
                            user: "testuser".to_string(),
1✔
4732
                            password: "testpass".to_string(),
1✔
4733
                        },
1✔
4734
                        cache_ttl: CacheTtlSeconds::new(0),
1✔
4735
                    },
1✔
4736
                ),
1✔
4737
                TypedDataProviderDefinition::GfbioCollectionsDataProviderDefinition(
1✔
4738
                    GfbioCollectionsDataProviderDefinition {
1✔
4739
                        name: "GFbio".to_string(),
1✔
4740
                        description: "GFBio".to_string(),
1✔
4741
                        priority: None,
1✔
4742
                        collection_api_url: "http://testhost".try_into().unwrap(),
1✔
4743
                        collection_api_auth_token: "token".to_string(),
1✔
4744
                        abcd_db_config: DatabaseConnectionConfig {
1✔
4745
                            host: "testhost".to_string(),
1✔
4746
                            port: 1234,
1✔
4747
                            database: "testdb".to_string(),
1✔
4748
                            schema: "testschema".to_string(),
1✔
4749
                            user: "testuser".to_string(),
1✔
4750
                            password: "testpass".to_string(),
1✔
4751
                        },
1✔
4752
                        pangaea_url: "http://panaea".try_into().unwrap(),
1✔
4753
                        cache_ttl: CacheTtlSeconds::new(0),
1✔
4754
                    },
1✔
4755
                ),
1✔
4756
                TypedDataProviderDefinition::EbvPortalDataProviderDefinition(
1✔
4757
                    EbvPortalDataProviderDefinition {
1✔
4758
                        name: "ebv".to_string(),
1✔
4759
                        description: "ebv".to_string(),
1✔
4760
                        priority: Some(33),
1✔
4761
                        data: "a_path".into(),
1✔
4762
                        base_url: "http://base".try_into().unwrap(),
1✔
4763
                        overviews: "another_path".into(),
1✔
4764
                        cache_ttl: CacheTtlSeconds::new(0),
1✔
4765
                    },
1✔
4766
                ),
1✔
4767
                TypedDataProviderDefinition::NetCdfCfDataProviderDefinition(
1✔
4768
                    NetCdfCfDataProviderDefinition {
1✔
4769
                        name: "netcdfcf".to_string(),
1✔
4770
                        description: "netcdfcf".to_string(),
1✔
4771
                        priority: Some(33),
1✔
4772
                        data: "a_path".into(),
1✔
4773
                        overviews: "another_path".into(),
1✔
4774
                        cache_ttl: CacheTtlSeconds::new(0),
1✔
4775
                    },
1✔
4776
                ),
1✔
4777
                TypedDataProviderDefinition::PangaeaDataProviderDefinition(
1✔
4778
                    PangaeaDataProviderDefinition {
1✔
4779
                        name: "pangaea".to_string(),
1✔
4780
                        description: "pangaea".to_string(),
1✔
4781
                        priority: None,
1✔
4782
                        base_url: "http://base".try_into().unwrap(),
1✔
4783
                        cache_ttl: CacheTtlSeconds::new(0),
1✔
4784
                    },
1✔
4785
                ),
1✔
4786
            ],
1✔
4787
        )
1✔
4788
        .await;
1✔
4789
    }
1✔
4790
}
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