modkit/api/odata/
error.rs1use crate::api::problem::Problem;
7use modkit_odata::Error as ODataError;
8
9#[inline]
11fn current_trace_id() -> Option<String> {
12 tracing::Span::current()
13 .id()
14 .map(|id| id.into_u64().to_string())
15}
16
17pub fn odata_error_to_problem(
28 err: &ODataError,
29 instance: &str,
30 trace_id: Option<String>,
31) -> Problem {
32 use modkit_odata::Error as OE;
33
34 match err {
36 OE::Db(msg) => {
37 tracing::error!(error = %msg, "Unexpected database error in OData layer");
38 }
39 OE::ParsingUnavailable(msg) => {
40 tracing::error!(error = %msg, "OData parsing unavailable");
41 }
42 _ => {}
43 }
44
45 let mut problem: Problem = err.clone().into();
47
48 problem = problem.with_instance(instance);
50
51 let trace_id = trace_id.or_else(current_trace_id);
52 if let Some(tid) = trace_id {
53 problem = problem.with_trace_id(tid);
54 }
55
56 problem
57}
58
59#[cfg(test)]
60#[cfg_attr(coverage_nightly, coverage(off))]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn test_filter_error_mapping() {
66 use http::StatusCode;
67
68 let error = ODataError::InvalidFilter("malformed expression".to_owned());
69 let problem = odata_error_to_problem(&error, "/user-management/v1/users", None);
70
71 assert_eq!(problem.status, StatusCode::UNPROCESSABLE_ENTITY);
72 assert!(problem.code.contains("invalid_filter"));
73 assert_eq!(problem.instance, "/user-management/v1/users");
74 }
75
76 #[test]
77 fn test_orderby_error_mapping() {
78 use http::StatusCode;
79
80 let error = ODataError::InvalidOrderByField("unknown_field".to_owned());
81 let problem = odata_error_to_problem(&error, "/user-management/v1/users", None);
82
83 assert_eq!(problem.status, StatusCode::UNPROCESSABLE_ENTITY);
84 assert!(problem.code.contains("invalid_orderby"));
85 }
86
87 #[test]
88 fn test_cursor_error_mapping() {
89 use http::StatusCode;
90
91 let error = ODataError::CursorInvalidBase64;
92 let problem = odata_error_to_problem(
93 &error,
94 "/user-management/v1/users",
95 Some("trace123".to_owned()),
96 );
97
98 assert_eq!(problem.status, StatusCode::UNPROCESSABLE_ENTITY);
99 assert!(problem.code.contains("invalid_cursor"));
100 assert_eq!(problem.trace_id, Some("trace123".to_owned()));
101 }
102
103 #[test]
104 fn test_gts_code_format() {
105 let error = ODataError::InvalidFilter("test".to_owned());
106 let problem = odata_error_to_problem(&error, "/user-management/v1/test", None);
107
108 assert!(problem.code.starts_with("gts.hx.core.errors.err.v1~"));
110 assert!(problem.code.contains("odata"));
111 }
112}