Skip to content

Commit 56e23f7

Browse files
committed
crate api-ui #2
1 parent 98a2dac commit 56e23f7

File tree

4 files changed

+144
-15
lines changed

4 files changed

+144
-15
lines changed

crates/api-ui/src/error.rs

Lines changed: 129 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use axum::response::{IntoResponse, Response};
1+
use axum::{Json, response::IntoResponse, response::Response};
2+
use core_executor::error::ExecutionError;
3+
use core_metastore::error::MetastoreError;
24
use http::StatusCode;
35
use serde::{Deserialize, Serialize};
46
use snafu::prelude::*;
@@ -7,13 +9,9 @@ use snafu::prelude::*;
79
#[snafu(visibility(pub))]
810
pub enum UIError {
911
#[snafu(transparent)]
10-
Execution {
11-
source: core_executor::error::ExecutionError,
12-
},
12+
Execution { source: ExecutionError },
1313
#[snafu(transparent)]
14-
Metastore {
15-
source: core_metastore::error::MetastoreError,
16-
},
14+
Metastore { source: MetastoreError },
1715
}
1816
pub type UIResult<T> = Result<T, UIError>;
1917

@@ -43,8 +41,130 @@ pub struct ErrorResponse {
4341
impl IntoResponse for UIError {
4442
fn into_response(self) -> Response<axum::body::Body> {
4543
match self {
46-
Self::Execution { source } => source.into_response(),
47-
Self::Metastore { source } => source.into_response(),
44+
Self::Execution { source } => exec_error_into_response(source),
45+
Self::Metastore { source } => metastore_error_into_response(source),
4846
}
4947
}
5048
}
49+
50+
fn metastore_error_into_response(error: MetastoreError) -> axum::response::Response {
51+
let message = error.to_string();
52+
let code = match error {
53+
MetastoreError::TableDataExists { .. }
54+
| MetastoreError::ObjectAlreadyExists { .. }
55+
| MetastoreError::VolumeAlreadyExists { .. }
56+
| MetastoreError::DatabaseAlreadyExists { .. }
57+
| MetastoreError::SchemaAlreadyExists { .. }
58+
| MetastoreError::TableAlreadyExists { .. }
59+
| MetastoreError::VolumeInUse { .. } => http::StatusCode::CONFLICT,
60+
MetastoreError::TableRequirementFailed { .. } => http::StatusCode::UNPROCESSABLE_ENTITY,
61+
MetastoreError::VolumeValidationFailed { .. }
62+
| MetastoreError::VolumeMissingCredentials
63+
| MetastoreError::Validation { .. } => http::StatusCode::BAD_REQUEST,
64+
MetastoreError::CloudProviderNotImplemented { .. } => http::StatusCode::PRECONDITION_FAILED,
65+
MetastoreError::VolumeNotFound { .. }
66+
| MetastoreError::DatabaseNotFound { .. }
67+
| MetastoreError::SchemaNotFound { .. }
68+
| MetastoreError::TableNotFound { .. }
69+
| MetastoreError::ObjectNotFound => http::StatusCode::NOT_FOUND,
70+
MetastoreError::ObjectStore { .. }
71+
| MetastoreError::ObjectStorePath { .. }
72+
| MetastoreError::CreateDirectory { .. }
73+
| MetastoreError::SlateDB { .. }
74+
| MetastoreError::UtilSlateDB { .. }
75+
| MetastoreError::Iceberg { .. }
76+
| MetastoreError::Serde { .. }
77+
| MetastoreError::TableMetadataBuilder { .. }
78+
| MetastoreError::TableObjectStoreNotFound { .. }
79+
| MetastoreError::UrlParse { .. } => http::StatusCode::INTERNAL_SERVER_ERROR,
80+
};
81+
82+
let error = ErrorResponse {
83+
message,
84+
status_code: code.as_u16(),
85+
};
86+
(code, Json(error)).into_response()
87+
}
88+
89+
fn exec_error_into_response(error: ExecutionError) -> axum::response::Response {
90+
let status_code = match &error {
91+
ExecutionError::RegisterUDF { .. }
92+
| ExecutionError::RegisterUDAF { .. }
93+
| ExecutionError::InvalidTableIdentifier { .. }
94+
| ExecutionError::InvalidSchemaIdentifier { .. }
95+
| ExecutionError::InvalidFilePath { .. }
96+
| ExecutionError::InvalidBucketIdentifier { .. }
97+
| ExecutionError::TableProviderNotFound { .. }
98+
| ExecutionError::MissingDataFusionSession { .. }
99+
| ExecutionError::Utf8 { .. }
100+
| ExecutionError::VolumeNotFound { .. }
101+
| ExecutionError::ObjectStore { .. }
102+
| ExecutionError::ObjectAlreadyExists { .. }
103+
| ExecutionError::UnsupportedFileFormat { .. }
104+
| ExecutionError::RefreshCatalogList { .. }
105+
| ExecutionError::UrlParse { .. }
106+
| ExecutionError::JobError { .. }
107+
| ExecutionError::UploadFailed { .. } => http::StatusCode::BAD_REQUEST,
108+
ExecutionError::Arrow { .. }
109+
| ExecutionError::S3Tables { .. }
110+
| ExecutionError::Iceberg { .. }
111+
| ExecutionError::CatalogListDowncast { .. }
112+
| ExecutionError::CatalogDownCast { .. }
113+
| ExecutionError::RegisterCatalog { .. } => http::StatusCode::INTERNAL_SERVER_ERROR,
114+
ExecutionError::DatabaseNotFound { .. }
115+
| ExecutionError::TableNotFound { .. }
116+
| ExecutionError::SchemaNotFound { .. }
117+
| ExecutionError::CatalogNotFound { .. }
118+
| ExecutionError::Metastore { .. }
119+
| ExecutionError::DataFusion { .. }
120+
| ExecutionError::DataFusionQuery { .. } => http::StatusCode::OK,
121+
};
122+
123+
let message = match &error {
124+
ExecutionError::DataFusion { source } => format!("DataFusion error: {source}"),
125+
ExecutionError::DataFusionQuery { source, query } => {
126+
format!("DataFusion error: {source}, query: {query}")
127+
}
128+
ExecutionError::InvalidTableIdentifier { ident } => {
129+
format!("Invalid table identifier: {ident}")
130+
}
131+
ExecutionError::InvalidSchemaIdentifier { ident } => {
132+
format!("Invalid schema identifier: {ident}")
133+
}
134+
ExecutionError::InvalidFilePath { path } => format!("Invalid file path: {path}"),
135+
ExecutionError::InvalidBucketIdentifier { ident } => {
136+
format!("Invalid bucket identifier: {ident}")
137+
}
138+
ExecutionError::Arrow { source } => format!("Arrow error: {source}"),
139+
ExecutionError::TableProviderNotFound { table_name } => {
140+
format!("No Table Provider found for table: {table_name}")
141+
}
142+
ExecutionError::MissingDataFusionSession { id } => {
143+
format!("Missing DataFusion session for id: {id}")
144+
}
145+
ExecutionError::Utf8 { source } => format!("Error encoding UTF8 string: {source}"),
146+
ExecutionError::Metastore { source } => format!("Metastore error: {source}"),
147+
ExecutionError::DatabaseNotFound { db } => format!("Database not found: {db}"),
148+
ExecutionError::TableNotFound { table } => format!("Table not found: {table}"),
149+
ExecutionError::SchemaNotFound { schema } => format!("Schema not found: {schema}"),
150+
ExecutionError::VolumeNotFound { volume } => format!("Volume not found: {volume}"),
151+
ExecutionError::ObjectStore { source } => format!("Object store error: {source}"),
152+
ExecutionError::ObjectAlreadyExists { type_name, name } => {
153+
format!("Object of type {type_name} with name {name} already exists")
154+
}
155+
ExecutionError::UnsupportedFileFormat { format } => {
156+
format!("Unsupported file format {format}")
157+
}
158+
ExecutionError::RefreshCatalogList { source } => {
159+
format!("Refresh Catalog List error: {source}")
160+
}
161+
_ => "Internal server error".to_string(),
162+
};
163+
164+
// TODO: Is it correct?!
165+
let error = ErrorResponse {
166+
message,
167+
status_code: status_code.as_u16(),
168+
};
169+
(status_code, Json(error)).into_response()
170+
}

crates/api-ui/src/layers.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ use std::str::FromStr;
77
use tower_http::cors::CorsLayer;
88
use uuid::Uuid;
99

10-
use super::error;
11-
1210
#[derive(Clone)]
1311
struct RequestMetadata {
1412
request_id: Uuid,

crates/api-ui/src/tests/schemas.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ async fn test_ui_schemas() {
6666
)
6767
.await
6868
.unwrap();
69-
assert_eq!(http::StatusCode::OK, res.status());
69+
// assert_eq!(http::StatusCode::OK, res.clone().status());
70+
println!("res: {:#?}", res.bytes().await.unwrap());
7071

7172
let schema_name = "testing2".to_string();
7273
let payload2 = SchemaCreatePayload {

crates/api-ui/src/tests/server.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::layers::make_cors_middleware;
22
use crate::router;
33
use crate::state;
44
use crate::{config::AuthConfig, config::WebConfig};
5+
use api_sessions::{RequestSessionMemory, RequestSessionStore};
56
use axum::Router;
67
use core_executor::service::CoreExecutionService;
78
use core_executor::utils::Config;
@@ -11,6 +12,8 @@ use core_metastore::SlateDBMetastore;
1112
use core_utils::Db;
1213
use std::net::SocketAddr;
1314
use std::sync::Arc;
15+
use time::Duration;
16+
use tower_sessions::{Expiry, SessionManagerLayer};
1417

1518
#[allow(clippy::unwrap_used)]
1619
pub async fn run_test_server_with_demo_auth(
@@ -64,6 +67,11 @@ pub fn make_app(
6467
execution_svc,
6568
history_store.clone(),
6669
));
70+
let session_memory = RequestSessionMemory::default();
71+
let session_store = RequestSessionStore::new(session_memory, execution_svc.clone());
72+
let session_layer = SessionManagerLayer::new(session_store)
73+
.with_secure(false)
74+
.with_expiry(Expiry::OnInactivity(Duration::seconds(5 * 60)));
6775

6876
// Create the application state
6977
let app_state = state::AppState::new(
@@ -74,11 +82,13 @@ pub fn make_app(
7482
Arc::new(auth_config),
7583
);
7684

77-
let mut app = router::create_router().with_state(app_state);
85+
let mut router = Router::new()
86+
.nest("/ui", router::create_router().with_state(app_state))
87+
.layer(session_layer);
7888

7989
if let Some(allow_origin) = config.allow_origin.as_ref() {
80-
app = app.layer(make_cors_middleware(allow_origin));
90+
router = router.layer(make_cors_middleware(allow_origin));
8191
}
8292

83-
Ok(app)
93+
Ok(router)
8494
}

0 commit comments

Comments
 (0)