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

geo-engine / geoengine / 12417045631

19 Dec 2024 04:45PM UTC coverage: 90.354% (-0.2%) from 90.512%
12417045631

Pull #998

github

web-flow
Merge 9e7b54661 into 34e12969f
Pull Request #998: quota logging wip

834 of 1211 new or added lines in 66 files covered. (68.87%)

227 existing lines in 20 files now uncovered.

133835 of 148123 relevant lines covered (90.35%)

54353.34 hits per line

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

30.19
/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)]
166✔
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
    #[snafu(display("Database error: {source}"))]
137
    TokioPostgres {
138
        source: bb8_postgres::tokio_postgres::Error,
139
    },
140

141
    TokioPostgresTimeout,
142

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

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

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

155
    ConfigLockFailed,
156

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

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

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

171
    MissingSettingsDirectory,
172

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

178
    UnknownDatasetId,
179

180
    OperationRequiresAdminPrivilige,
181
    OperationRequiresOwnerPermission,
182

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

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

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

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

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

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

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

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

259
    NotYetImplemented,
260

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

277
    MissingSpatialReference,
278

279
    WcsVersionNotSupported,
280
    WcsGridOriginMustEqualBoundingboxUpperLeft,
281
    WcsBoundingboxCrsMustEqualGridBaseCrs,
282
    WcsInvalidGridOffsets,
283

284
    InvalidDatasetId,
285

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

293
    Nature40UnknownRasterDbname,
294
    Nature40WcsDatasetMissingLabelInMetadata,
295

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

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

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

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

314
    #[snafu(display("User registration is disabled"))]
315
    UserRegistrationDisabled,
316

317
    UserDoesNotExist,
318
    RoleDoesNotExist,
319
    RoleWithNameAlreadyExists,
320
    RoleAlreadyAssigned,
321
    RoleNotAssigned,
322

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

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

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

366
    AbcdUnitIdColumnMissingInDatabase,
367

368
    BaseUrlMustEndWithSlash,
369

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

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

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

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

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

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

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

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

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

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

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

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

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

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

463
    ProviderDoesNotSupportBrowsing,
464

465
    InvalidPath,
466

467
    InvalidWorkflowOutputType,
468

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

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

479
    UnexpectedInvalidDbTypeConversion,
480

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

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

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

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

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

517
impl actix_web::error::ResponseError for Error {
518
    fn error_response(&self) -> HttpResponse {
33✔
519
        HttpResponse::build(self.status_code()).json(ErrorResponse::from_service_error(self))
33✔
520
    }
33✔
521

522
    fn status_code(&self) -> StatusCode {
33✔
523
        match self {
33✔
524
            Error::Unauthorized { source: _ } => StatusCode::UNAUTHORIZED,
16✔
525
            Error::Duplicate { reason: _ } => StatusCode::CONFLICT,
1✔
526
            _ => StatusCode::BAD_REQUEST,
16✔
527
        }
528
    }
33✔
529
}
530

531
impl fmt::Debug for Error {
532
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16✔
533
        write!(f, "{}", ge_report(self))
16✔
534
    }
16✔
535
}
536

537
impl From<bb8_postgres::bb8::RunError<<bb8_postgres::PostgresConnectionManager<bb8_postgres::tokio_postgres::NoTls> as bb8_postgres::bb8::ManageConnection>::Error>> for Error {
538
    fn from(e: bb8_postgres::bb8::RunError<<bb8_postgres::PostgresConnectionManager<bb8_postgres::tokio_postgres::NoTls> as bb8_postgres::bb8::ManageConnection>::Error>) -> Self {
×
539
        match e {
×
540
            bb8_postgres::bb8::RunError::User(e) => Self::TokioPostgres { source: e },
×
UNCOV
541
            bb8_postgres::bb8::RunError::TimedOut => Self::TokioPostgresTimeout,
×
542
        }
UNCOV
543
    }
×
544
}
545

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

548
impl From<bb8_postgres::tokio_postgres::error::Error> for Error {
549
    fn from(e: bb8_postgres::tokio_postgres::error::Error) -> Self {
16✔
550
        Self::TokioPostgres { source: e }
16✔
551
    }
16✔
552
}
553

554
impl From<serde_json::Error> for Error {
555
    fn from(e: serde_json::Error) -> Self {
×
556
        Self::SerdeJson { source: e }
×
UNCOV
557
    }
×
558
}
559

560
impl From<std::io::Error> for Error {
561
    fn from(e: std::io::Error) -> Self {
×
562
        Self::Io { source: e }
×
UNCOV
563
    }
×
564
}
565

566
impl From<gdal::errors::GdalError> for Error {
567
    fn from(gdal_error: gdal::errors::GdalError) -> Self {
×
568
        Self::Gdal { source: gdal_error }
×
UNCOV
569
    }
×
570
}
571

572
impl From<reqwest::Error> for Error {
573
    fn from(source: reqwest::Error) -> Self {
×
574
        Self::Reqwest { source }
×
UNCOV
575
    }
×
576
}
577

578
impl From<actix_multipart::MultipartError> for Error {
579
    fn from(source: actix_multipart::MultipartError) -> Self {
×
580
        Self::Multipart {
×
581
            reason: source.to_string(),
×
582
        }
×
UNCOV
583
    }
×
584
}
585

586
impl From<url::ParseError> for Error {
587
    fn from(source: url::ParseError) -> Self {
×
588
        Self::Url { source }
×
UNCOV
589
    }
×
590
}
591

592
impl From<flexi_logger::FlexiLoggerError> for Error {
593
    fn from(source: flexi_logger::FlexiLoggerError) -> Self {
×
594
        Self::Logger { source }
×
UNCOV
595
    }
×
596
}
597

598
impl From<proj::ProjError> for Error {
599
    fn from(source: proj::ProjError) -> Self {
×
600
        Self::Proj { source }
×
UNCOV
601
    }
×
602
}
603

604
impl From<tokio::task::JoinError> for Error {
605
    fn from(source: tokio::task::JoinError) -> Self {
×
606
        Error::TokioJoin { source }
×
UNCOV
607
    }
×
608
}
609

610
impl From<ordered_float::FloatIsNan> for Error {
611
    fn from(source: FloatIsNan) -> Self {
×
612
        Error::InvalidNotNanFloatKey { source }
×
UNCOV
613
    }
×
614
}
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