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

geo-engine / geoengine / 13075333516

31 Jan 2025 03:04PM UTC coverage: 90.073% (+0.05%) from 90.027%
13075333516

push

github

web-flow
Merge pull request #1007 from geo-engine/add_ml_permissions

Add ml permissions

401 of 440 new or added lines in 9 files covered. (91.14%)

3 existing lines in 3 files now uncovered.

125966 of 139849 relevant lines covered (90.07%)

57556.87 hits per line

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

27.12
/services/src/error.rs
1
use crate::api::model::datatypes::{
2
    DatasetId, SpatialReference, SpatialReferenceOption, TimeInstance,
3
};
4
use crate::api::model::responses::ErrorResponse;
5
use crate::datasets::external::aruna::error::ArunaProviderError;
6
use crate::datasets::external::netcdfcf::NetCdfCf4DProviderError;
7
use crate::{layers::listing::LayerCollectionId, workflows::workflow::WorkflowId};
8
use actix_web::http::StatusCode;
9
use actix_web::HttpResponse;
10
use geoengine_datatypes::dataset::LayerId;
11
use geoengine_datatypes::error::ErrorSource;
12
use geoengine_datatypes::util::helpers::ge_report;
13
use ordered_float::FloatIsNan;
14
use snafu::prelude::*;
15
use std::fmt;
16
use std::path::PathBuf;
17
use strum::IntoStaticStr;
18

19
pub type Result<T, E = Error> = std::result::Result<T, E>;
20

21
#[derive(Snafu, IntoStaticStr)]
41✔
22
#[snafu(visibility(pub(crate)))]
23
#[snafu(context(suffix(false)))] // disables default `Snafu` suffix
24
pub enum Error {
25
    #[snafu(transparent)]
26
    DataType {
27
        source: geoengine_datatypes::error::Error,
28
    },
29
    #[snafu(transparent)]
30
    Operator {
31
        source: geoengine_operators::error::Error,
32
    },
33
    #[snafu(display("Uuid error: {source}"))]
34
    Uuid {
35
        source: uuid::Error,
36
    },
37
    #[snafu(display("Serde json error: {source}"))]
38
    SerdeJson {
39
        source: serde_json::Error,
40
    },
41
    Io {
42
        source: std::io::Error,
43
    },
44
    TokioJoin {
45
        source: tokio::task::JoinError,
46
    },
47

48
    TokioSignal {
49
        source: std::io::Error,
50
    },
51

52
    Reqwest {
53
        source: reqwest::Error,
54
    },
55
    #[snafu(display("Unable to parse url: {source}"))]
56
    Url {
57
        source: url::ParseError,
58
    },
59
    Proj {
60
        source: proj::ProjError,
61
    },
62

63
    #[snafu(context(false))]
64
    Trace {
65
        source: opentelemetry::trace::TraceError,
66
    },
67

68
    TokioChannelSend,
69

70
    #[snafu(display("Unable to parse query string: {}", source))]
71
    UnableToParseQueryString {
72
        source: serde_urlencoded::de::Error,
73
    },
74

75
    ServerStartup,
76

77
    #[snafu(display("Registration failed: {}", reason))]
78
    RegistrationFailed {
79
        reason: String,
80
    },
81
    #[snafu(display("Tried to create duplicate: {}", reason))]
82
    Duplicate {
83
        reason: String,
84
    },
85
    #[snafu(display("User does not exist or password is wrong."))]
86
    LoginFailed,
87
    LogoutFailed,
88
    #[snafu(display("The session id is invalid."))]
89
    InvalidSession,
90
    #[snafu(display("Invalid admin token"))]
91
    InvalidAdminToken,
92
    #[snafu(display("Header with authorization token not provided."))]
93
    MissingAuthorizationHeader,
94
    #[snafu(display("Authentication scheme must be Bearer."))]
95
    InvalidAuthorizationScheme,
96

97
    #[snafu(display("Authorization error: {}", source))]
98
    Unauthorized {
99
        source: Box<Error>,
100
    },
101
    AccessDenied,
102
    #[snafu(display("Failed to create the project."))]
103
    ProjectCreateFailed,
104
    #[snafu(display("Failed to list projects."))]
105
    ProjectListFailed,
106
    #[snafu(display("The project failed to load."))]
107
    ProjectLoadFailed,
108
    #[snafu(display("Failed to update the project."))]
109
    ProjectUpdateFailed,
110
    #[snafu(display("Failed to delete the project."))]
111
    ProjectDeleteFailed,
112
    PermissionFailed,
113
    #[snafu(display("A permission error occured: {source}."))]
114
    PermissionDb {
115
        source: Box<dyn ErrorSource>,
116
    },
117
    #[snafu(display("A role error occured: {source}."))]
118
    RoleDb {
119
        source: Box<dyn ErrorSource>,
120
    },
121
    ProjectDbUnauthorized,
122

123
    InvalidNamespace,
124

125
    InvalidSpatialReference,
126
    #[snafu(display("SpatialReferenceMissmatch: Found {}, expected: {}", found, expected))]
127
    SpatialReferenceMissmatch {
128
        found: SpatialReferenceOption,
129
        expected: SpatialReferenceOption,
130
    },
131

132
    InvalidWfsTypeNames,
133

134
    NoWorkflowForGivenId,
135

136
    TokioPostgres {
137
        source: bb8_postgres::tokio_postgres::Error,
138
    },
139

140
    TokioPostgresTimeout,
141

142
    #[snafu(display(
143
        "Database cannot be cleared on startup because it was started without that setting before."
144
    ))]
145
    ClearDatabaseOnStartupNotAllowed,
146

147
    #[snafu(display("Database schema must not be `public`."))]
148
    InvalidDatabaseSchema,
149

150
    #[snafu(display("Identifier does not have the right format."))]
151
    InvalidUuid,
152
    SessionNotInitialized,
153

154
    ConfigLockFailed,
155

156
    Config {
157
        source: config::ConfigError,
158
    },
159

160
    #[snafu(display("Unable to parse IP address: {source}"))]
161
    AddrParse {
162
        source: std::net::AddrParseError,
163
    },
164

165
    #[snafu(display("Missing working directory"))]
166
    MissingWorkingDirectory {
167
        source: std::io::Error,
168
    },
169

170
    MissingSettingsDirectory,
171

172
    DataIdTypeMissMatch,
173
    UnknownDataId,
174
    UnknownProviderId,
175
    MissingDatasetId,
176

177
    UnknownDatasetId,
178

179
    OperationRequiresAdminPrivilige,
180
    OperationRequiresOwnerPermission,
181

182
    #[snafu(display("Permission denied for dataset with id {:?}", dataset))]
183
    DatasetPermissionDenied {
184
        dataset: DatasetId,
185
    },
186

187
    #[snafu(display("Updating permission ({}, {:?}, {}) denied", role, dataset, permission))]
188
    UpdateDatasetPermission {
189
        role: String,
190
        dataset: DatasetId,
191
        permission: String,
192
    },
193

194
    #[snafu(display("Permission ({}, {:?}, {}) already exists", role, dataset, permission))]
195
    DuplicateDatasetPermission {
196
        role: String,
197
        dataset: DatasetId,
198
        permission: String,
199
    },
200

201
    // TODO: move to pro folder, because permissions are pro only
202
    #[snafu(display("Permission denied"))]
203
    PermissionDenied,
204

205
    #[snafu(display("Parameter {} must have length between {} and {}", parameter, min, max))]
206
    InvalidStringLength {
207
        parameter: String,
208
        min: usize,
209
        max: usize,
210
    },
211

212
    #[snafu(display("Limit must be <= {}", limit))]
213
    InvalidListLimit {
214
        limit: usize,
215
    },
216

217
    UploadFieldMissingFileName,
218
    UnknownUploadId,
219
    PathIsNotAFile,
220
    #[snafu(display("Failed loading multipart body: {reason}"))]
221
    Multipart {
222
        // TODO: this error is not send, so this does not work
223
        // source: actix_multipart::MultipartError,
224
        reason: String,
225
    },
226
    InvalidUploadFileName,
227
    InvalidDatasetIdNamespace,
228
    DuplicateDatasetId,
229
    #[snafu(display("Dataset name '{}' already exists", dataset_name))]
230
    DatasetNameAlreadyExists {
231
        dataset_name: String,
232
        dataset_id: DatasetId,
233
    },
234
    #[snafu(display("Dataset name '{}' does not exist", dataset_name))]
235
    UnknownDatasetName {
236
        dataset_name: String,
237
    },
238
    InvalidDatasetName,
239
    #[snafu(display("Layer name '{layer_name}' is invalid"))]
240
    DatasetInvalidLayerName {
241
        layer_name: String,
242
    },
243
    DatasetHasNoAutoImportableLayer,
244
    #[snafu(display("GdalError: {}", source))]
245
    Gdal {
246
        source: gdal::errors::GdalError,
247
    },
248
    EmptyDatasetCannotBeImported,
249
    NoMainFileCandidateFound,
250
    NoFeatureDataTypeForColumnDataType,
251

252
    #[snafu(display("Spatial reference '{srs_string}' is unknown"))]
253
    UnknownSpatialReference {
254
        srs_string: String,
255
    },
256

257
    NotYetImplemented,
258

259
    #[snafu(display("Band '{band_name}' does not exist"))]
260
    StacNoSuchBand {
261
        band_name: String,
262
    },
263
    StacInvalidGeoTransform,
264
    StacInvalidBbox,
265
    #[snafu(display(
266
        "Failed to parse stac response from '{url}'. Error: {error}\nOriginal Response: {response}"
267
    ))]
268
    StacJsonResponse {
269
        url: String,
270
        response: String,
271
        error: serde_json::Error,
272
    },
273
    RasterDataTypeNotSupportByGdal,
274

275
    MissingSpatialReference,
276

277
    WcsVersionNotSupported,
278
    WcsGridOriginMustEqualBoundingboxUpperLeft,
279
    WcsBoundingboxCrsMustEqualGridBaseCrs,
280
    WcsInvalidGridOffsets,
281

282
    InvalidDatasetId,
283

284
    PangaeaNoTsv,
285
    GfbioMissingAbcdField,
286
    #[snafu(display("The response from the EDR server does not match the expected format."))]
287
    EdrInvalidMetadataFormat,
288
    ExpectedExternalDataId,
289
    InvalidDataId,
290

291
    Nature40UnknownRasterDbname,
292
    Nature40WcsDatasetMissingLabelInMetadata,
293

294
    #[snafu(display("FlexiLogger initialization error"))]
295
    Logger {
296
        source: flexi_logger::FlexiLoggerError,
297
    },
298

299
    #[snafu(display("Spatial reference system '{srs_string}' is unknown"))]
300
    UnknownSrsString {
301
        srs_string: String,
302
    },
303

304
    #[snafu(display("Axis ordering is unknown for SRS '{srs_string}'"))]
305
    AxisOrderingNotKnownForSrs {
306
        srs_string: String,
307
    },
308

309
    #[snafu(display("Anonymous access is disabled, please log in"))]
310
    AnonymousAccessDisabled,
311

312
    #[snafu(display("User registration is disabled"))]
313
    UserRegistrationDisabled,
314

315
    UserDoesNotExist,
316
    RoleDoesNotExist,
317
    RoleWithNameAlreadyExists,
318
    RoleAlreadyAssigned,
319
    RoleNotAssigned,
320

321
    #[snafu(display(
322
        "WCS request endpoint {} must match identifier {}",
323
        endpoint,
324
        identifier
325
    ))]
326
    WCSEndpointIdentifierMissmatch {
327
        endpoint: WorkflowId,
328
        identifier: WorkflowId,
329
    },
330
    #[snafu(display(
331
        "WCS request endpoint {} must match identifiers {}",
332
        endpoint,
333
        identifiers
334
    ))]
335
    WCSEndpointIdentifiersMissmatch {
336
        endpoint: WorkflowId,
337
        identifiers: WorkflowId,
338
    },
339
    #[snafu(display("WMS request endpoint {} must match layer {}", endpoint, layer))]
340
    WMSEndpointLayerMissmatch {
341
        endpoint: WorkflowId,
342
        layer: WorkflowId,
343
    },
344
    #[snafu(display(
345
        "WFS request endpoint {} must match type_names {}",
346
        endpoint,
347
        type_names
348
    ))]
349
    WFSEndpointTypeNamesMissmatch {
350
        endpoint: WorkflowId,
351
        type_names: WorkflowId,
352
    },
353

354
    #[snafu(context(false))]
355
    ArunaProvider {
356
        source: ArunaProviderError,
357
    },
358

359
    #[snafu(context(false))]
360
    NetCdfCf4DProvider {
361
        source: NetCdfCf4DProviderError,
362
    },
363

364
    AbcdUnitIdColumnMissingInDatabase,
365

366
    BaseUrlMustEndWithSlash,
367

368
    #[snafu(context(false))]
369
    LayerDb {
370
        source: crate::layers::LayerDbError,
371
    },
372

373
    #[snafu(display("Operator '{operator}' is unknown"))]
374
    UnknownOperator {
375
        operator: String,
376
    },
377

378
    #[snafu(display("The id is expected to be an uuid, but it is '{found}'."))]
379
    IdStringMustBeUuid {
380
        found: String,
381
    },
382

383
    #[snafu(context(false), display("TaskError: {}", source))]
384
    Task {
385
        source: crate::tasks::TaskError,
386
    },
387

388
    #[snafu(display("'{id}' is not a known layer collection id"))]
389
    UnknownLayerCollectionId {
390
        id: LayerCollectionId,
391
    },
392
    #[snafu(display("'{id}' is not a known layer id"))]
393
    UnknownLayerId {
394
        id: LayerId,
395
    },
396
    InvalidLayerCollectionId,
397
    InvalidLayerId,
398

399
    #[snafu(context(false))]
400
    WorkflowApi {
401
        source: crate::api::handlers::workflows::WorkflowApiError,
402
    },
403

404
    #[snafu(display("The sub path '{}' escapes the base path '{}'", sub_path.display(), base.display()))]
405
    SubPathMustNotEscapeBasePath {
406
        base: PathBuf,
407
        sub_path: PathBuf,
408
    },
409

410
    #[snafu(display("The sub path '{}' contains references to the parent '{}'", sub_path.display(), base.display()))]
411
    PathMustNotContainParentReferences {
412
        base: PathBuf,
413
        sub_path: PathBuf,
414
    },
415

416
    #[snafu(display("Time instance must be between {} and {}, but is {}", min.inner(), max.inner(), is))]
417
    InvalidTimeInstance {
418
        min: TimeInstance,
419
        max: TimeInstance,
420
        is: i64,
421
    },
422

423
    #[snafu(display("ParseU32: {}", source))]
424
    ParseU32 {
425
        source: <u32 as std::str::FromStr>::Err,
426
    },
427
    #[snafu(display("InvalidSpatialReferenceString: {}", spatial_reference_string))]
428
    InvalidSpatialReferenceString {
429
        spatial_reference_string: String,
430
    },
431

432
    #[snafu(context(false), display("OidcError: {}", source))]
433
    Oidc {
434
        source: crate::users::OidcError,
435
    },
436

437
    #[snafu(display(
438
        "Could not resolve a Proj string for this SpatialReference: {}",
439
        spatial_ref
440
    ))]
441
    ProjStringUnresolvable {
442
        spatial_ref: SpatialReference,
443
    },
444

445
    #[snafu(display(
446
        "Cannot resolve the query's BBOX ({:?}) in the selected SRS ({})",
447
        query_bbox,
448
        query_srs
449
    ))]
450
    UnresolvableQueryBoundingBox2DInSrs {
451
        query_srs: SpatialReference,
452
        query_bbox: crate::api::model::datatypes::BoundingBox2D,
453
    },
454

455
    #[snafu(display("Result Descriptor field '{}' {}", field, cause))]
456
    LayerResultDescriptorMissingFields {
457
        field: String,
458
        cause: String,
459
    },
460

461
    ProviderDoesNotSupportBrowsing,
462

463
    InvalidPath,
464

465
    InvalidWorkflowOutputType,
466

467
    #[snafu(display("Functionality is not implemented: '{}'", message))]
468
    NotImplemented {
469
        message: String,
470
    },
471

472
    #[snafu(display("NotNan error: {}", source))]
473
    InvalidNotNanFloatKey {
474
        source: ordered_float::FloatIsNan,
475
    },
476

477
    UnexpectedInvalidDbTypeConversion,
478

479
    #[snafu(display(
480
        "Unexpected database version during migration, expected `{}` but found `{}`",
481
        expected,
482
        found
483
    ))]
484
    UnexpectedDatabaseVersionDuringMigration {
485
        expected: String,
486
        found: String,
487
    },
488

489
    #[snafu(display("Raster band names must be unique. Found {duplicate_key} more than once."))]
490
    RasterBandNamesMustBeUnique {
491
        duplicate_key: String,
492
    },
493
    #[snafu(display("Raster band names must not be empty"))]
494
    RasterBandNameMustNotBeEmpty,
495
    #[snafu(display("Raster band names must not be longer than 256 bytes"))]
496
    RasterBandNameTooLong,
497

498
    #[snafu(display("Resource id is invalid: type: {}, id: {}", resource_type, resource_id))]
499
    InvalidResourceId {
500
        resource_type: String,
501
        resource_id: String,
502
    },
503

504
    #[snafu(display("Unknown volume name: {}", volume_name))]
505
    UnknownVolumeName {
506
        volume_name: String,
507
    },
508

509
    #[snafu(display("Cannot access path of volume with name {}", volume_name))]
510
    CannotAccessVolumePath {
511
        volume_name: String,
512
    },
513

514
    #[snafu(display("Unknown resource name {} of kind {}", name, kind))]
515
    UnknownResource {
516
        kind: String,
517
        name: String,
518
    },
519

520
    #[snafu(display("MachineLearning error: {}", source))]
521
    MachineLearning {
522
        // TODO: make `source: MachineLearningError`, once pro features is removed
523
        source: Box<dyn ErrorSource>,
524
    },
525

526
    DatasetName {
527
        source: crate::datasets::DatasetNameError,
528
    },
529

530
    MlModelName {
531
        source: geoengine_datatypes::machine_learning::MlModelNameError,
532
    },
533
}
534

535
impl actix_web::error::ResponseError for Error {
536
    fn error_response(&self) -> HttpResponse {
31✔
537
        HttpResponse::build(self.status_code()).json(ErrorResponse::from_service_error(self))
31✔
538
    }
31✔
539

540
    fn status_code(&self) -> StatusCode {
31✔
541
        match self {
31✔
542
            Error::Unauthorized { source: _ } => StatusCode::UNAUTHORIZED,
15✔
543
            Error::Duplicate { reason: _ } => StatusCode::CONFLICT,
1✔
544
            _ => StatusCode::BAD_REQUEST,
15✔
545
        }
546
    }
31✔
547
}
548

549
impl fmt::Debug for Error {
550
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31✔
551
        write!(f, "{}", ge_report(self))
31✔
552
    }
31✔
553
}
554

555
impl From<bb8_postgres::bb8::RunError<<bb8_postgres::PostgresConnectionManager<bb8_postgres::tokio_postgres::NoTls> as bb8_postgres::bb8::ManageConnection>::Error>> for Error {
556
    fn from(e: bb8_postgres::bb8::RunError<<bb8_postgres::PostgresConnectionManager<bb8_postgres::tokio_postgres::NoTls> as bb8_postgres::bb8::ManageConnection>::Error>) -> Self {
×
557
        match e {
×
558
            bb8_postgres::bb8::RunError::User(e) => Self::TokioPostgres { source: e },
×
559
            bb8_postgres::bb8::RunError::TimedOut => Self::TokioPostgresTimeout,
×
560
        }
561
    }
×
562
}
563

564
// TODO: remove automatic conversion to our Error because we do not want to leak database internals in the API
565

566
impl From<bb8_postgres::tokio_postgres::error::Error> for Error {
567
    fn from(e: bb8_postgres::tokio_postgres::error::Error) -> Self {
8✔
568
        Self::TokioPostgres { source: e }
8✔
569
    }
8✔
570
}
571

572
impl From<serde_json::Error> for Error {
573
    fn from(e: serde_json::Error) -> Self {
×
574
        Self::SerdeJson { source: e }
×
575
    }
×
576
}
577

578
impl From<std::io::Error> for Error {
579
    fn from(e: std::io::Error) -> Self {
×
580
        Self::Io { source: e }
×
581
    }
×
582
}
583

584
impl From<gdal::errors::GdalError> for Error {
585
    fn from(gdal_error: gdal::errors::GdalError) -> Self {
×
586
        Self::Gdal { source: gdal_error }
×
587
    }
×
588
}
589

590
impl From<reqwest::Error> for Error {
591
    fn from(source: reqwest::Error) -> Self {
×
592
        Self::Reqwest { source }
×
593
    }
×
594
}
595

596
impl From<actix_multipart::MultipartError> for Error {
597
    fn from(source: actix_multipart::MultipartError) -> Self {
×
598
        Self::Multipart {
×
599
            reason: source.to_string(),
×
600
        }
×
601
    }
×
602
}
603

604
impl From<url::ParseError> for Error {
605
    fn from(source: url::ParseError) -> Self {
×
606
        Self::Url { source }
×
607
    }
×
608
}
609

610
impl From<flexi_logger::FlexiLoggerError> for Error {
611
    fn from(source: flexi_logger::FlexiLoggerError) -> Self {
×
612
        Self::Logger { source }
×
613
    }
×
614
}
615

616
impl From<proj::ProjError> for Error {
617
    fn from(source: proj::ProjError) -> Self {
×
618
        Self::Proj { source }
×
619
    }
×
620
}
621

622
impl From<tokio::task::JoinError> for Error {
623
    fn from(source: tokio::task::JoinError) -> Self {
×
624
        Error::TokioJoin { source }
×
625
    }
×
626
}
627

628
impl From<ordered_float::FloatIsNan> for Error {
629
    fn from(source: FloatIsNan) -> Self {
×
630
        Error::InvalidNotNanFloatKey { source }
×
631
    }
×
632
}
633

634
impl From<crate::datasets::DatasetNameError> for Error {
NEW
635
    fn from(source: crate::datasets::DatasetNameError) -> Self {
×
NEW
636
        Error::DatasetName { source }
×
NEW
637
    }
×
638
}
639

640
impl From<geoengine_datatypes::machine_learning::MlModelNameError> for Error {
NEW
641
    fn from(source: geoengine_datatypes::machine_learning::MlModelNameError) -> Self {
×
NEW
642
        Error::MlModelName { source }
×
NEW
643
    }
×
644
}
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