use std::collections::HashMap;
use axum::extract::{Path, Query, State};
use axum::response::{IntoResponse, Response};
use axum::Json;
use serde_json::Value as Json_;
use crate::auth::Authority;
use crate::body_credentials::MethodOverride;
use crate::params::Params;
use crate::response::{HttpError, ParseErrorResponse};
use crate::routes::dispatch::{self, Incoming, Route, RouteError};
use crate::state::AppState;
async fn run(state: &AppState, authority: &Authority, incoming: Incoming) -> Response {
let authority = &match incoming.route {
Route::Login => Authority {
session_token: None,
..authority.clone()
},
_ => authority.clone(),
};
let rc = match state.request_context(authority).await {
Ok(rc) => rc,
Err(e) => return ParseErrorResponse(e).into_response(),
};
let outcome = dispatch::dispatch(state, &rc, authority, &incoming).await;
match outcome {
Ok(response) => (response.status, Json(response.body)).into_response(),
Err(RouteError::Parse(e)) => ParseErrorResponse(e).into_response(),
Err(RouteError::Http(e)) => e.into_response(),
Err(RouteError::NotFound { method, path }) => HttpError {
status: http::StatusCode::NOT_FOUND,
message: format!("cannot route {method} {path}"),
}
.into_response(),
}
}
fn effective_method(
transport: http::Method,
override_: Option<axum::Extension<MethodOverride>>,
) -> http::Method {
match override_ {
Some(axum::Extension(MethodOverride(m))) => m,
None => transport,
}
}
fn params(query: HashMap<String, String>) -> Params {
Params::from_map(query)
}
pub async fn health(State(state): State<AppState>) -> Response {
let _ = state;
Json(crate::routes::health::body()).into_response()
}
pub async fn server_info(State(state): State<AppState>, authority: Authority) -> Response {
if !authority.is_master() {
return HttpError::master_key_required(state.config().error_detail()).into_response();
}
Json(crate::routes::features::server_info_body(state.config())).into_response()
}
pub async fn users_collection(
State(state): State<AppState>,
authority: Authority,
method: Option<axum::Extension<MethodOverride>>,
body: Option<Json<Json_>>,
) -> Response {
let method = effective_method(http::Method::POST, method);
run(
&state,
&authority,
Incoming {
method,
route: Route::Users,
params: Params::default(),
body: body.map(|b| b.0),
path: "/users".to_string(),
},
)
.await
}
pub async fn users_me(
State(state): State<AppState>,
authority: Authority,
method: Option<axum::Extension<MethodOverride>>,
transport: http::Method,
) -> Response {
let method = effective_method(transport, method);
run(
&state,
&authority,
Incoming {
method,
route: Route::UsersMe,
params: Params::default(),
body: None,
path: "/users/me".to_string(),
},
)
.await
}
pub async fn login(
State(state): State<AppState>,
authority: Authority,
body: Option<Json<Json_>>,
) -> Response {
run(
&state,
&authority,
Incoming {
method: http::Method::POST,
route: Route::Login,
params: Params::default(),
body: body.map(|b| b.0),
path: "/login".to_string(),
},
)
.await
}
pub async fn logout(State(state): State<AppState>, authority: Authority) -> Response {
run(
&state,
&authority,
Incoming {
method: http::Method::POST,
route: Route::Logout,
params: Params::default(),
body: None,
path: "/logout".to_string(),
},
)
.await
}
pub async fn classes_collection(
State(state): State<AppState>,
authority: Authority,
Path(class_name): Path<String>,
Query(query): Query<HashMap<String, String>>,
method: Option<axum::Extension<MethodOverride>>,
transport: http::Method,
body: Option<Json<Json_>>,
) -> Response {
let method = effective_method(transport, method);
let path = format!("/classes/{class_name}");
run(
&state,
&authority,
Incoming {
method,
route: Route::Classes { class_name },
params: params(query),
body: body.map(|b| b.0),
path,
},
)
.await
}
pub async fn classes_object(
State(state): State<AppState>,
authority: Authority,
Path((class_name, object_id)): Path<(String, String)>,
Query(query): Query<HashMap<String, String>>,
method: Option<axum::Extension<MethodOverride>>,
transport: http::Method,
body: Option<Json<Json_>>,
) -> Response {
let method = effective_method(transport, method);
let path = format!("/classes/{class_name}/{object_id}");
run(
&state,
&authority,
Incoming {
method,
route: Route::ClassObject {
class_name,
object_id,
},
params: params(query),
body: body.map(|b| b.0),
path,
},
)
.await
}
pub async fn roles_collection(
State(state): State<AppState>,
authority: Authority,
Query(query): Query<HashMap<String, String>>,
method: Option<axum::Extension<MethodOverride>>,
transport: http::Method,
body: Option<Json<Json_>>,
) -> Response {
let method = effective_method(transport, method);
run(
&state,
&authority,
Incoming {
method,
route: Route::Roles,
params: params(query),
body: body.map(|b| b.0),
path: "/roles".to_string(),
},
)
.await
}
pub async fn roles_object(
State(state): State<AppState>,
authority: Authority,
Path(object_id): Path<String>,
Query(query): Query<HashMap<String, String>>,
method: Option<axum::Extension<MethodOverride>>,
transport: http::Method,
body: Option<Json<Json_>>,
) -> Response {
let method = effective_method(transport, method);
let path = format!("/roles/{object_id}");
run(
&state,
&authority,
Incoming {
method,
route: Route::RoleObject { object_id },
params: params(query),
body: body.map(|b| b.0),
path,
},
)
.await
}
pub async fn sessions_collection(
State(state): State<AppState>,
authority: Authority,
Query(query): Query<HashMap<String, String>>,
method: Option<axum::Extension<MethodOverride>>,
transport: http::Method,
) -> Response {
let method = effective_method(transport, method);
run(
&state,
&authority,
Incoming {
method,
route: Route::Sessions,
params: params(query),
body: None,
path: "/sessions".to_string(),
},
)
.await
}
pub async fn sessions_me(
State(state): State<AppState>,
authority: Authority,
method: Option<axum::Extension<MethodOverride>>,
transport: http::Method,
) -> Response {
let method = effective_method(transport, method);
run(
&state,
&authority,
Incoming {
method,
route: Route::SessionsMe,
params: Params::default(),
body: None,
path: "/sessions/me".to_string(),
},
)
.await
}
pub async fn sessions_object(
State(state): State<AppState>,
authority: Authority,
Path(object_id): Path<String>,
Query(query): Query<HashMap<String, String>>,
method: Option<axum::Extension<MethodOverride>>,
transport: http::Method,
) -> Response {
let method = effective_method(transport, method);
let path = format!("/sessions/{object_id}");
run(
&state,
&authority,
Incoming {
method,
route: Route::SessionObject { object_id },
params: params(query),
body: None,
path,
},
)
.await
}
pub async fn schemas_collection(
State(state): State<AppState>,
authority: Authority,
method: Option<axum::Extension<MethodOverride>>,
transport: http::Method,
body: Option<Json<Json_>>,
) -> Response {
let method = effective_method(transport, method);
run(
&state,
&authority,
Incoming {
method,
route: Route::Schemas,
params: Params::default(),
body: body.map(|b| b.0),
path: "/schemas".to_string(),
},
)
.await
}
pub async fn schemas_class(
State(state): State<AppState>,
authority: Authority,
Path(class_name): Path<String>,
method: Option<axum::Extension<MethodOverride>>,
transport: http::Method,
body: Option<Json<Json_>>,
) -> Response {
let method = effective_method(transport, method);
let path = format!("/schemas/{class_name}");
run(
&state,
&authority,
Incoming {
method,
route: Route::SchemaClass { class_name },
params: Params::default(),
body: body.map(|b| b.0),
path,
},
)
.await
}
pub async fn purge(
State(state): State<AppState>,
authority: Authority,
Path(class_name): Path<String>,
method: Option<axum::Extension<MethodOverride>>,
transport: http::Method,
) -> Response {
let method = effective_method(transport, method);
let path = format!("/purge/{class_name}");
run(
&state,
&authority,
Incoming {
method,
route: Route::Purge { class_name },
params: Params::default(),
body: None,
path,
},
)
.await
}
pub async fn batch(
State(state): State<AppState>,
authority: Authority,
body: Option<Json<Json_>>,
) -> Response {
let rc = match state.request_context(&authority).await {
Ok(rc) => rc,
Err(e) => return ParseErrorResponse(e).into_response(),
};
let body = body.map(|b| b.0);
let mount_path = state.config().mount_path.clone();
match crate::routes::batch::handle(&state, &rc, &authority, &mount_path, body.as_ref()).await {
Ok(results) => Json(results).into_response(),
Err(e) => ParseErrorResponse(e).into_response(),
}
}