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

geo-engine / geoengine / 3676601107

pending completion
3676601107

push

github

GitHub
Merge #695

42001 of 50014 relevant lines covered (83.98%)

2.01 hits per line

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

50.0
/services/src/error.rs
1
use crate::api::model::datatypes::{
2
    DataProviderId, DatasetId, LayerId, SpatialReference, SpatialReferenceOption, TimeInstance,
3
};
4
#[cfg(feature = "ebv")]
5
use crate::datasets::external::netcdfcf::NetCdfCf4DProviderError;
6
use crate::handlers::ErrorResponse;
7
use crate::{layers::listing::LayerCollectionId, workflows::workflow::WorkflowId};
8
use actix_web::http::StatusCode;
9
use actix_web::HttpResponse;
10
use snafu::prelude::*;
11
use std::path::PathBuf;
12
use strum::IntoStaticStr;
13
use tonic::Status;
14

15
pub type Result<T, E = Error> = std::result::Result<T, E>;
16

17
#[derive(Debug, Snafu, IntoStaticStr)]
18
#[snafu(visibility(pub(crate)))]
19
#[snafu(context(suffix(false)))] // disables default `Snafu` suffix
20
pub enum Error {
21
    DataType {
22
        source: geoengine_datatypes::error::Error,
23
    },
24
    Operator {
25
        source: geoengine_operators::error::Error,
26
    },
27
    Uuid {
28
        source: uuid::Error,
29
    },
30
    SerdeJson {
31
        source: serde_json::Error,
32
    },
33
    Io {
34
        source: std::io::Error,
35
    },
36
    TokioJoin {
37
        source: tokio::task::JoinError,
38
    },
39

40
    TokioSignal {
41
        source: std::io::Error,
42
    },
43

44
    Reqwest {
45
        source: reqwest::Error,
46
    },
47

48
    Url {
49
        source: url::ParseError,
50
    },
51

52
    #[cfg(feature = "xml")]
53
    QuickXml {
54
        source: quick_xml::Error,
55
    },
56
    Proj {
57
        source: proj::ProjError,
58
    },
59
    #[snafu(context(false))]
60
    Trace {
61
        source: opentelemetry::trace::TraceError,
62
    },
63

64
    TokioChannelSend,
65

66
    #[snafu(display("Unable to parse query string: {}", source))]
67
    UnableToParseQueryString {
68
        source: serde_urlencoded::de::Error,
69
    },
70

71
    ServerStartup,
72

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

93
    #[snafu(display("Authorization error: {:?}", source))]
94
    Authorization {
95
        source: Box<Error>,
96
    },
97
    AccessDenied,
98
    #[snafu(display("Failed to create the project."))]
99
    ProjectCreateFailed,
100
    #[snafu(display("Failed to list projects."))]
101
    ProjectListFailed,
102
    #[snafu(display("The project failed to load."))]
103
    ProjectLoadFailed,
104
    #[snafu(display("Failed to update the project."))]
105
    ProjectUpdateFailed,
106
    #[snafu(display("Failed to delete the project."))]
107
    ProjectDeleteFailed,
108
    PermissionFailed,
109
    ProjectDbUnauthorized,
110

111
    InvalidNamespace,
112

113
    InvalidSpatialReference,
114
    #[snafu(display("SpatialReferenceMissmatch: Found {}, expected: {}", found, expected))]
115
    SpatialReferenceMissmatch {
116
        found: SpatialReferenceOption,
117
        expected: SpatialReferenceOption,
118
    },
119

120
    InvalidWfsTypeNames,
121

122
    NoWorkflowForGivenId,
123

124
    #[cfg(feature = "postgres")]
125
    TokioPostgres {
126
        source: bb8_postgres::tokio_postgres::Error,
127
    },
128

129
    TokioPostgresTimeout,
130

131
    #[snafu(display("Identifier does not have the right format."))]
132
    InvalidUuid,
133
    SessionNotInitialized,
134

135
    ConfigLockFailed,
136

137
    Config {
138
        source: config::ConfigError,
139
    },
140

141
    AddrParse {
142
        source: std::net::AddrParseError,
143
    },
144

145
    MissingWorkingDirectory {
146
        source: std::io::Error,
147
    },
148

149
    MissingSettingsDirectory,
150

151
    DataIdTypeMissMatch,
152
    UnknownDataId,
153
    UnknownProviderId,
154
    MissingDatasetId,
155

156
    UnknownDatasetId,
157

158
    #[snafu(display("Permission denied for dataset with id {:?}", dataset))]
159
    DatasetPermissionDenied {
160
        dataset: DatasetId,
161
    },
162

163
    #[snafu(display("Updating permission ({}, {:?}, {}) denied", role, dataset, permission))]
164
    UpateDatasetPermission {
165
        role: String,
166
        dataset: DatasetId,
167
        permission: String,
168
    },
169

170
    #[snafu(display("Permission ({}, {:?}, {}) already exists", role, dataset, permission))]
171
    DuplicateDatasetPermission {
172
        role: String,
173
        dataset: DatasetId,
174
        permission: String,
175
    },
176

177
    #[snafu(display("Parameter {} must have length between {} and {}", parameter, min, max))]
178
    InvalidStringLength {
179
        parameter: String,
180
        min: usize,
181
        max: usize,
182
    },
183

184
    #[snafu(display("Limit must be <= {}", limit))]
185
    InvalidListLimit {
186
        limit: usize,
187
    },
188

189
    UploadFieldMissingFileName,
190
    UnknownUploadId,
191
    PathIsNotAFile,
192
    Multipart {
193
        source: actix_multipart::MultipartError,
194
    },
195
    InvalidUploadFileName,
196
    InvalidDatasetName,
197
    DatasetHasNoAutoImportableLayer,
198
    #[snafu(display("GdalError: {}", source))]
199
    Gdal {
200
        source: gdal::errors::GdalError,
201
    },
202
    EmptyDatasetCannotBeImported,
203
    NoMainFileCandidateFound,
204
    NoFeatureDataTypeForColumnDataType,
205

206
    UnknownSpatialReference {
207
        srs_string: String,
208
    },
209

210
    NotYetImplemented,
211

212
    StacNoSuchBand {
213
        band_name: String,
214
    },
215
    StacInvalidGeoTransform,
216
    StacInvalidBbox,
217
    StacJsonResponse {
218
        url: String,
219
        response: String,
220
        error: serde_json::Error,
221
    },
222
    RasterDataTypeNotSupportByGdal,
223

224
    MissingSpatialReference,
225

226
    WcsVersionNotSupported,
227
    WcsGridOriginMustEqualBoundingboxUpperLeft,
228
    WcsBoundingboxCrsMustEqualGridBaseCrs,
229
    WcsInvalidGridOffsets,
230

231
    InvalidDatasetId,
232

233
    PangaeaNoTsv,
234
    GfbioMissingAbcdField,
235
    ExpectedExternalDataId,
236
    InvalidExternalDataId {
237
        provider: DataProviderId,
238
    },
239
    InvalidDataId,
240

241
    #[cfg(feature = "nature40")]
242
    Nature40UnknownRasterDbname,
243
    #[cfg(feature = "nature40")]
244
    Nature40WcsDatasetMissingLabelInMetadata,
245

246
    Logger {
247
        source: flexi_logger::FlexiLoggerError,
248
    },
249

250
    #[cfg(feature = "odm")]
251
    Odm {
252
        reason: String,
253
    },
254
    #[cfg(feature = "odm")]
255
    OdmInvalidResponse {
256
        reason: String,
257
    },
258
    #[cfg(feature = "odm")]
259
    OdmMissingContentTypeHeader,
260

261
    UnknownSrsString {
262
        srs_string: String,
263
    },
264

265
    AxisOrderingNotKnownForSrs {
266
        srs_string: String,
267
    },
268

269
    #[snafu(display("Anonymous access is disabled, please log in"))]
270
    AnonymousAccessDisabled,
271

272
    #[snafu(display("User registration is disabled"))]
273
    UserRegistrationDisabled,
274

275
    #[snafu(display(
276
        "WCS request endpoint {} must match identifier {}",
277
        endpoint,
278
        identifier
279
    ))]
280
    WCSEndpointIdentifierMissmatch {
281
        endpoint: WorkflowId,
282
        identifier: WorkflowId,
283
    },
284
    #[snafu(display(
285
        "WCS request endpoint {} must match identifiers {}",
286
        endpoint,
287
        identifiers
288
    ))]
289
    WCSEndpointIdentifiersMissmatch {
290
        endpoint: WorkflowId,
291
        identifiers: WorkflowId,
292
    },
293
    #[snafu(display("WMS request endpoint {} must match layer {}", endpoint, layer))]
294
    WMSEndpointLayerMissmatch {
295
        endpoint: WorkflowId,
296
        layer: WorkflowId,
297
    },
298
    #[snafu(display(
299
        "WFS request endpoint {} must match type_names {}",
300
        endpoint,
301
        type_names
302
    ))]
303
    WFSEndpointTypeNamesMissmatch {
304
        endpoint: WorkflowId,
305
        type_names: WorkflowId,
306
    },
307

308
    Tonic {
309
        source: tonic::Status,
310
    },
311

312
    TonicTransport {
313
        source: tonic::transport::Error,
314
    },
315

316
    InvalidUri {
317
        uri_string: String,
318
    },
319

320
    InvalidAPIToken {
321
        message: String,
322
    },
323
    MissingNFDIMetaData,
324

325
    #[cfg(feature = "ebv")]
326
    #[snafu(context(false))]
327
    NetCdfCf4DProvider {
328
        source: NetCdfCf4DProviderError,
329
    },
330
    #[cfg(feature = "nfdi")]
331
    #[snafu(display("Could not parse GFBio basket: {}", message,))]
332
    GFBioBasketParse {
333
        message: String,
334
    },
335

336
    BaseUrlMustEndWithSlash,
337

338
    #[snafu(context(false))]
339
    LayerDb {
340
        source: crate::layers::LayerDbError,
341
    },
342

343
    UnknownOperator {
344
        operator: String,
345
    },
346

347
    IdStringMustBeUuid {
348
        found: String,
349
    },
350

351
    #[snafu(context(false))]
352
    TaskError {
353
        source: crate::tasks::TaskError,
354
    },
355

356
    UnknownLayerCollectionId {
357
        id: LayerCollectionId,
358
    },
359
    UnknownLayerId {
360
        id: LayerId,
361
    },
362
    InvalidLayerCollectionId,
363
    InvalidLayerId,
364

365
    #[snafu(context(false))]
366
    WorkflowApi {
367
        source: crate::handlers::workflows::WorkflowApiError,
368
    },
369

370
    SubPathMustNotEscapeBasePath {
371
        base: PathBuf,
372
        sub_path: PathBuf,
373
    },
374

375
    PathMustNotContainParentReferences {
376
        base: PathBuf,
377
        sub_path: PathBuf,
378
    },
379

380
    #[snafu(display("Time instance must be between {} and {}, but is {}", min.inner(), max.inner(), is))]
381
    InvalidTimeInstance {
382
        min: TimeInstance,
383
        max: TimeInstance,
384
        is: i64,
385
    },
386

387
    #[snafu(display("ParseU32: {}", source))]
388
    ParseU32 {
389
        source: <u32 as std::str::FromStr>::Err,
390
    },
391
    #[snafu(display("InvalidSpatialReferenceString: {}", spatial_reference_string))]
392
    InvalidSpatialReferenceString {
393
        spatial_reference_string: String,
394
    },
395

396
    #[cfg(feature = "pro")]
397
    #[snafu(context(false))]
398
    OidcError {
399
        source: crate::pro::users::OidcError,
400
    },
401

402
    #[snafu(display(
403
        "Could not resolve a Proj string for this SpatialReference: {}",
404
        spatial_ref
405
    ))]
406
    ProjStringUnresolvable {
407
        spatial_ref: SpatialReference,
408
    },
409

410
    #[snafu(display(
411
        "Cannot resolve the query's BBOX ({:?}) in the selected SRS ({})",
412
        query_bbox,
413
        query_srs
414
    ))]
415
    UnresolvableQueryBoundingBox2DInSrs {
416
        query_srs: SpatialReference,
417
        query_bbox: crate::api::model::datatypes::BoundingBox2D,
418
    },
419
}
420

421
impl actix_web::error::ResponseError for Error {
422
    fn error_response(&self) -> HttpResponse {
1✔
423
        // TODO: rethink this error handling since errors
424
        // only have `Display`, `Debug` and `Error` implementations
425
        let (error, message) = match self {
2✔
426
            Error::Authorization { source } => (
2✔
427
                Into::<&str>::into(source.as_ref()).to_string(),
2✔
428
                source.to_string(),
1✔
429
            ),
430
            _ => (Into::<&str>::into(self).to_string(), self.to_string()),
1✔
431
        };
432

433
        HttpResponse::build(self.status_code()).json(ErrorResponse { error, message })
2✔
434
    }
435

436
    fn status_code(&self) -> StatusCode {
1✔
437
        match self {
1✔
438
            Error::Authorization { source: _ } => StatusCode::UNAUTHORIZED,
1✔
439
            Error::Duplicate { reason: _ } => StatusCode::CONFLICT,
1✔
440
            _ => StatusCode::BAD_REQUEST,
1✔
441
        }
442
    }
443
}
444

445
impl From<geoengine_datatypes::error::Error> for Error {
446
    fn from(e: geoengine_datatypes::error::Error) -> Self {
×
447
        Self::DataType { source: e }
448
    }
449
}
450

451
impl From<geoengine_operators::error::Error> for Error {
452
    fn from(e: geoengine_operators::error::Error) -> Self {
1✔
453
        Self::Operator { source: e }
454
    }
455
}
456

457
#[cfg(feature = "postgres")]
458
impl From<bb8_postgres::bb8::RunError<<bb8_postgres::PostgresConnectionManager<bb8_postgres::tokio_postgres::NoTls> as bb8_postgres::bb8::ManageConnection>::Error>> for Error {
459
    fn from(e: bb8_postgres::bb8::RunError<<bb8_postgres::PostgresConnectionManager<bb8_postgres::tokio_postgres::NoTls> as bb8_postgres::bb8::ManageConnection>::Error>) -> Self {
×
460
        match e {
×
461
            bb8_postgres::bb8::RunError::User(e) => Self::TokioPostgres { source: e },
462
            bb8_postgres::bb8::RunError::TimedOut => Self::TokioPostgresTimeout,
×
463
        }
464
    }
465
}
466

467
// TODO: remove automatic conversion to our Error because we do not want to leak database internals in the API
468
#[cfg(feature = "postgres")]
469
impl From<bb8_postgres::tokio_postgres::error::Error> for Error {
470
    fn from(e: bb8_postgres::tokio_postgres::error::Error) -> Self {
1✔
471
        Self::TokioPostgres { source: e }
472
    }
473
}
474

475
impl From<serde_json::Error> for Error {
476
    fn from(e: serde_json::Error) -> Self {
1✔
477
        Self::SerdeJson { source: e }
478
    }
479
}
480

481
impl From<std::io::Error> for Error {
482
    fn from(e: std::io::Error) -> Self {
×
483
        Self::Io { source: e }
484
    }
485
}
486

487
impl From<gdal::errors::GdalError> for Error {
488
    fn from(gdal_error: gdal::errors::GdalError) -> Self {
×
489
        Self::Gdal { source: gdal_error }
490
    }
491
}
492

493
impl From<reqwest::Error> for Error {
494
    fn from(source: reqwest::Error) -> Self {
×
495
        Self::Reqwest { source }
496
    }
497
}
498

499
impl From<actix_multipart::MultipartError> for Error {
500
    fn from(source: actix_multipart::MultipartError) -> Self {
×
501
        Self::Multipart { source }
502
    }
503
}
504

505
impl From<url::ParseError> for Error {
506
    fn from(source: url::ParseError) -> Self {
×
507
        Self::Url { source }
508
    }
509
}
510

511
#[cfg(feature = "xml")]
512
impl From<quick_xml::Error> for Error {
513
    fn from(source: quick_xml::Error) -> Self {
×
514
        Self::QuickXml { source }
515
    }
516
}
517

518
impl From<flexi_logger::FlexiLoggerError> for Error {
519
    fn from(source: flexi_logger::FlexiLoggerError) -> Self {
×
520
        Self::Logger { source }
521
    }
522
}
523

524
impl From<proj::ProjError> for Error {
525
    fn from(source: proj::ProjError) -> Self {
×
526
        Self::Proj { source }
527
    }
528
}
529

530
impl From<tonic::Status> for Error {
531
    fn from(source: Status) -> Self {
×
532
        Self::Tonic { source }
533
    }
534
}
535

536
impl From<tonic::transport::Error> for Error {
537
    fn from(source: tonic::transport::Error) -> Self {
×
538
        Self::TonicTransport { source }
539
    }
540
}
541

542
impl From<tokio::task::JoinError> for Error {
543
    fn from(source: tokio::task::JoinError) -> Self {
×
544
        Error::TokioJoin { source }
545
    }
546
}
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