Skip to content

Commit 26377da

Browse files
authored
feat: Add context info to DB errors everywhere else (#340)
Add DB error context information for the remaining providers.
1 parent 8a810f1 commit 26377da

File tree

29 files changed

+288
-136
lines changed

29 files changed

+288
-136
lines changed

.github/workflows/linters.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ concurrency:
1616

1717
env:
1818
CARGO_TERM_COLOR: always
19-
rust_min: 1.89.0
19+
rust_min: 1.90.0
2020

2121
jobs:
2222
rustfmt:

src/assignment/backends/error.rs

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,30 +40,45 @@ pub enum AssignmentDatabaseError {
4040
source: RoleBuilderError,
4141
},
4242

43-
#[error(transparent)]
44-
Database { source: sea_orm::DbErr },
45-
4643
/// Conflict
47-
#[error("{0}")]
48-
Conflict(String),
44+
#[error("{message}")]
45+
Conflict { message: String, context: String },
4946

5047
/// SqlError
51-
#[error("{0}")]
52-
Sql(String),
48+
#[error("{message}")]
49+
Sql { message: String, context: String },
50+
51+
/// Database error
52+
#[error("Database error while {context}")]
53+
Database {
54+
source: sea_orm::DbErr,
55+
context: String,
56+
},
5357

5458
#[error("{0}")]
5559
InvalidAssignmentType(String),
5660
}
5761

58-
impl From<sea_orm::DbErr> for AssignmentDatabaseError {
59-
fn from(err: sea_orm::DbErr) -> Self {
60-
err.sql_err().map_or_else(
61-
|| Self::Database { source: err },
62-
|err| match err {
63-
SqlErr::UniqueConstraintViolation(descr) => Self::Conflict(descr),
64-
SqlErr::ForeignKeyConstraintViolation(descr) => Self::Conflict(descr),
65-
other => Self::Sql(other.to_string()),
62+
/// Convert the DB error into the [AssignmentDatabaseError] with the context information.
63+
pub fn db_err(e: sea_orm::DbErr, context: &str) -> AssignmentDatabaseError {
64+
e.sql_err().map_or_else(
65+
|| AssignmentDatabaseError::Database {
66+
source: e,
67+
context: context.to_string(),
68+
},
69+
|err| match err {
70+
SqlErr::UniqueConstraintViolation(descr) => AssignmentDatabaseError::Conflict {
71+
message: descr.to_string(),
72+
context: context.to_string(),
73+
},
74+
SqlErr::ForeignKeyConstraintViolation(descr) => AssignmentDatabaseError::Conflict {
75+
message: descr.to_string(),
76+
context: context.to_string(),
77+
},
78+
other => AssignmentDatabaseError::Sql {
79+
message: other.to_string(),
80+
context: context.to_string(),
6681
},
67-
)
68-
}
82+
},
83+
)
6984
}

src/assignment/backends/sql/assignment.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use sea_orm::prelude::Expr;
1818
use sea_orm::query::*;
1919
use std::collections::{BTreeMap, HashMap};
2020

21-
use crate::assignment::backends::error::AssignmentDatabaseError;
21+
use crate::assignment::backends::error::{AssignmentDatabaseError, db_err};
2222
use crate::assignment::types::*;
2323
use crate::config::Config;
2424
use crate::db::entity::{
@@ -71,15 +71,19 @@ pub async fn list(
7171
}
7272

7373
let results: Result<Vec<Assignment>, _> = if let Some(true) = &params.include_names {
74-
let db_assignments: Vec<(db_assignment::Model, Option<db_role::Model>)> =
75-
select_assignment.find_also_related(DbRole).all(db).await?;
74+
let db_assignments: Vec<(db_assignment::Model, Option<db_role::Model>)> = select_assignment
75+
.find_also_related(DbRole)
76+
.all(db)
77+
.await
78+
.map_err(|err| db_err(err, "fetching role assignments with roles"))?;
7679
let db_system_assignments: Vec<(db_system_assignment::Model, Option<db_role::Model>)> =
7780
if params.project_id.is_none() && params.domain_id.is_none() {
7881
// get system scope assignments only when no project or domain is specified
7982
select_system_assignment
8083
.find_also_related(DbRole)
8184
.all(db)
82-
.await?
85+
.await
86+
.map_err(|err| db_err(err, "fetching system role assignments with roles"))?
8387
} else {
8488
Vec::new()
8589
};
@@ -93,11 +97,17 @@ pub async fn list(
9397
)
9498
.collect()
9599
} else {
96-
let db_assignments: Vec<db_assignment::Model> = select_assignment.all(db).await?;
100+
let db_assignments: Vec<db_assignment::Model> = select_assignment
101+
.all(db)
102+
.await
103+
.map_err(|err| db_err(err, "fetching role assignments"))?;
97104
let db_system_assignments: Vec<db_system_assignment::Model> =
98105
if params.project_id.is_none() && params.domain_id.is_none() {
99106
// get system scope assignments only when no project or domain is specified
100-
select_system_assignment.all(db).await?
107+
select_system_assignment
108+
.all(db)
109+
.await
110+
.map_err(|err| db_err(err, "fetching system role assignments"))?
101111
} else {
102112
Vec::new()
103113
};
@@ -153,7 +163,12 @@ pub async fn list_for_multiple_actors_and_targets(
153163

154164
let mut db_assignments: BTreeMap<String, db_assignment::Model> = BTreeMap::new();
155165
// Get assignments resolving the roles inference
156-
for assignment in select.all(db).await? {
166+
for assignment in select.all(db).await.map_err(|err| {
167+
db_err(
168+
err,
169+
"fetching role assignments for multiple actors and targets",
170+
)
171+
})? {
157172
db_assignments.insert(assignment.role_id.clone(), assignment.clone());
158173
if let Some(implies) = imply_rules.get(&assignment.role_id) {
159174
let mut implied_assignment = assignment.clone();
@@ -173,7 +188,8 @@ pub async fn list_for_multiple_actors_and_targets(
173188
.filter(Expr::col(db_role::Column::Id).is_in(db_assignments.keys()))
174189
.into_tuple()
175190
.all(db)
176-
.await?,
191+
.await
192+
.map_err(|err| db_err(err, "fetching roles by ids"))?,
177193
);
178194
let results: Result<Vec<Assignment>, _> = db_assignments
179195
.values()

src/assignment/backends/sql/implied_role.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use sea_orm::DatabaseConnection;
1616
use sea_orm::entity::*;
1717
use std::collections::{BTreeMap, BTreeSet};
1818

19-
use crate::assignment::backends::error::AssignmentDatabaseError;
19+
use crate::assignment::backends::error::{AssignmentDatabaseError, db_err};
2020
use crate::db::entity::prelude::ImpliedRole as DbImpliedRole;
2121

2222
/// Build a resolved tree of role inference
@@ -52,7 +52,11 @@ pub async fn list_rules(
5252
resolve: bool,
5353
) -> Result<BTreeMap<String, BTreeSet<String>>, AssignmentDatabaseError> {
5454
let mut implied_rules: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
55-
for imply in DbImpliedRole::find().all(db).await? {
55+
for imply in DbImpliedRole::find()
56+
.all(db)
57+
.await
58+
.map_err(|err| db_err(err, "fetching implied roles"))?
59+
{
5660
implied_rules
5761
.entry(imply.prior_role_id)
5862
.and_modify(|x| {

src/assignment/backends/sql/role.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use sea_orm::entity::*;
1717
use sea_orm::query::*;
1818
use serde_json::Value;
1919

20-
use crate::assignment::backends::error::AssignmentDatabaseError;
20+
use crate::assignment::backends::error::{AssignmentDatabaseError, db_err};
2121
use crate::assignment::types::*;
2222
use crate::config::Config;
2323
use crate::db::entity::{prelude::Role as DbRole, role as db_role};
@@ -31,7 +31,10 @@ pub async fn get<I: AsRef<str>>(
3131
) -> Result<Option<Role>, AssignmentDatabaseError> {
3232
let role_select = DbRole::find_by_id(id.as_ref());
3333

34-
let entry: Option<db_role::Model> = role_select.one(db).await?;
34+
let entry: Option<db_role::Model> = role_select
35+
.one(db)
36+
.await
37+
.map_err(|err| db_err(err, "fetching role by id"))?;
3538
entry.map(TryInto::try_into).transpose()
3639
}
3740

@@ -49,7 +52,10 @@ pub async fn list(
4952
select = select.filter(db_role::Column::Name.eq(name));
5053
}
5154

52-
let db_roles: Vec<db_role::Model> = select.all(db).await?;
55+
let db_roles: Vec<db_role::Model> = select
56+
.all(db)
57+
.await
58+
.map_err(|err| db_err(err, "listing roles"))?;
5359
let results: Result<Vec<Role>, _> = db_roles
5460
.into_iter()
5561
.map(TryInto::<Role>::try_into)

src/assignment/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ pub enum AssignmentProviderError {
7272
impl From<AssignmentDatabaseError> for AssignmentProviderError {
7373
fn from(source: AssignmentDatabaseError) -> Self {
7474
match source {
75-
AssignmentDatabaseError::Conflict(x) => Self::Conflict(x),
75+
AssignmentDatabaseError::Conflict { message, .. } => Self::Conflict(message),
7676
AssignmentDatabaseError::RoleNotFound(x) => Self::RoleNotFound(x),
7777
AssignmentDatabaseError::Serde { source } => Self::Serde { source },
7878
_ => Self::AssignmentDatabaseError { source },

src/catalog/backends/error.rs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,27 +41,41 @@ pub enum CatalogDatabaseError {
4141
ServiceNotFound(String),
4242

4343
/// Conflict
44-
#[error("{0}")]
45-
Conflict(String),
44+
#[error("{message}")]
45+
Conflict { message: String, context: String },
4646

4747
/// SqlError
48-
#[error("{0}")]
49-
Sql(String),
48+
#[error("{message}")]
49+
Sql { message: String, context: String },
5050

5151
/// Database error
52-
#[error(transparent)]
53-
Database { source: sea_orm::DbErr },
52+
#[error("Database error while {context}")]
53+
Database {
54+
source: sea_orm::DbErr,
55+
context: String,
56+
},
5457
}
5558

56-
impl From<sea_orm::DbErr> for CatalogDatabaseError {
57-
fn from(err: sea_orm::DbErr) -> Self {
58-
err.sql_err().map_or_else(
59-
|| Self::Database { source: err },
60-
|err| match err {
61-
SqlErr::UniqueConstraintViolation(descr) => Self::Conflict(descr),
62-
SqlErr::ForeignKeyConstraintViolation(descr) => Self::Conflict(descr),
63-
other => Self::Sql(other.to_string()),
59+
/// Convert the DB error into the [CatalogDatabaseError] with the context information.
60+
pub fn db_err(e: sea_orm::DbErr, context: &str) -> CatalogDatabaseError {
61+
e.sql_err().map_or_else(
62+
|| CatalogDatabaseError::Database {
63+
source: e,
64+
context: context.to_string(),
65+
},
66+
|err| match err {
67+
SqlErr::UniqueConstraintViolation(descr) => CatalogDatabaseError::Conflict {
68+
message: descr.to_string(),
69+
context: context.to_string(),
70+
},
71+
SqlErr::ForeignKeyConstraintViolation(descr) => CatalogDatabaseError::Conflict {
72+
message: descr.to_string(),
73+
context: context.to_string(),
74+
},
75+
other => CatalogDatabaseError::Sql {
76+
message: other.to_string(),
77+
context: context.to_string(),
6478
},
65-
)
66-
}
79+
},
80+
)
6781
}

src/catalog/backends/sql.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use sea_orm::query::*;
1919

2020
use super::super::types::*;
2121
use crate::catalog::CatalogProviderError;
22-
use crate::catalog::backends::error::CatalogDatabaseError;
22+
use crate::catalog::backends::error::{CatalogDatabaseError, db_err};
2323
use crate::config::Config;
2424
use crate::db::entity::{
2525
endpoint as db_endpoint,
@@ -104,7 +104,8 @@ async fn get_catalog(
104104
.find_with_related(DbEndpoint)
105105
.filter(db_endpoint::Column::Enabled.eq(enabled))
106106
.all(db)
107-
.await?;
107+
.await
108+
.map_err(|err| db_err(err, "fetching catalog"))?;
108109

109110
let mut res: Vec<(Service, Vec<Endpoint>)> = Vec::new();
110111
for (srv, db_endpoints) in db_entities.into_iter() {

src/catalog/backends/sql/endpoint.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use sea_orm::entity::*;
1717
use sea_orm::query::*;
1818
use serde_json::Value;
1919

20-
use crate::catalog::backends::error::CatalogDatabaseError;
20+
use crate::catalog::backends::error::{CatalogDatabaseError, db_err};
2121
use crate::catalog::types::*;
2222
use crate::config::Config;
2323
use crate::db::entity::{endpoint as db_endpoint, prelude::Endpoint as DbEndpoint};
@@ -29,7 +29,10 @@ pub async fn get<I: AsRef<str>>(
2929
) -> Result<Option<Endpoint>, CatalogDatabaseError> {
3030
let select = DbEndpoint::find_by_id(id.as_ref());
3131

32-
let entry: Option<db_endpoint::Model> = select.one(db).await?;
32+
let entry: Option<db_endpoint::Model> = select
33+
.one(db)
34+
.await
35+
.map_err(|err| db_err(err, "fetching service endpoint by id"))?;
3336
entry.map(TryInto::try_into).transpose()
3437
}
3538

@@ -50,7 +53,10 @@ pub async fn list(
5053
select = select.filter(db_endpoint::Column::RegionId.eq(val));
5154
}
5255

53-
let db_entities: Vec<db_endpoint::Model> = select.all(db).await?;
56+
let db_entities: Vec<db_endpoint::Model> = select
57+
.all(db)
58+
.await
59+
.map_err(|err| db_err(err, "fetching endpoints"))?;
5460
let results: Result<Vec<Endpoint>, _> = db_entities
5561
.into_iter()
5662
.map(TryInto::<Endpoint>::try_into)

src/catalog/backends/sql/service.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use sea_orm::entity::*;
1717
use sea_orm::query::*;
1818
use serde_json::Value;
1919

20-
use crate::catalog::backends::error::CatalogDatabaseError;
20+
use crate::catalog::backends::error::{CatalogDatabaseError, db_err};
2121
use crate::catalog::types::*;
2222
use crate::config::Config;
2323
use crate::db::entity::{prelude::Service as DbService, service as db_service};
@@ -29,7 +29,10 @@ pub async fn get<I: AsRef<str>>(
2929
) -> Result<Option<Service>, CatalogDatabaseError> {
3030
let select = DbService::find_by_id(id.as_ref());
3131

32-
let entry: Option<db_service::Model> = select.one(db).await?;
32+
let entry: Option<db_service::Model> = select
33+
.one(db)
34+
.await
35+
.map_err(|err| db_err(err, "fetching service by ID"))?;
3336
entry.map(TryInto::try_into).transpose()
3437
}
3538

@@ -44,7 +47,10 @@ pub async fn list(
4447
select = select.filter(db_service::Column::Type.eq(typ));
4548
}
4649

47-
let db_services: Vec<db_service::Model> = select.all(db).await?;
50+
let db_services: Vec<db_service::Model> = select
51+
.all(db)
52+
.await
53+
.map_err(|err| db_err(err, "fetching services"))?;
4854
let results: Result<Vec<Service>, _> = db_services
4955
.into_iter()
5056
.map(TryInto::<Service>::try_into)

0 commit comments

Comments
 (0)