use crate::prelude::*;
use axum::{
extract::{Path, State},
http::StatusCode,
Json,
};
use serde_json::json;
use super::sessions::ErrorResponse;
use crate::web::github::{CreateGistRequest, UpdateGistRequest};
use crate::web::state::WebState;
fn no_github() -> (StatusCode, Json<ErrorResponse>) {
(
StatusCode::UNAUTHORIZED,
Json(ErrorResponse {
error: "GitHub token not configured".to_string(),
}),
)
}
fn github_error(e: anyhow::Error) -> (StatusCode, Json<ErrorResponse>) {
let message = e.to_string();
if message.contains("status 401") || message.contains("status 403") {
return (
StatusCode::UNAUTHORIZED,
Json(ErrorResponse {
error: "GitHub token is invalid or lacks required access".to_string(),
}),
);
}
(
StatusCode::BAD_GATEWAY,
Json(ErrorResponse { error: message }),
)
}
pub async fn list_gists(
State(state): State<WebState>,
) -> Result<Json<serde_json::Value>, (StatusCode, Json<ErrorResponse>)> {
let github = state.github.read().await;
let client = github.as_ref().ok_or_else(no_github)?;
let gists = client.list_gists().await.map_err(github_error)?;
Ok(Json(json!({ "gists": gists })))
}
pub async fn get_gist(
State(state): State<WebState>,
Path(gist_id): Path<String>,
) -> Result<Json<serde_json::Value>, (StatusCode, Json<ErrorResponse>)> {
let github = state.github.read().await;
let client = github.as_ref().ok_or_else(no_github)?;
let gist = client.get_gist(&gist_id).await.map_err(github_error)?;
Ok(Json(json!({ "gist": gist })))
}
pub async fn create_gist(
State(state): State<WebState>,
Json(body): Json<CreateGistRequest>,
) -> Result<Json<serde_json::Value>, (StatusCode, Json<ErrorResponse>)> {
let github = state.github.read().await;
let client = github.as_ref().ok_or_else(no_github)?;
let gist = client.create_gist(&body).await.map_err(github_error)?;
Ok(Json(json!({ "gist": gist })))
}
pub async fn update_gist(
State(state): State<WebState>,
Path(gist_id): Path<String>,
Json(body): Json<UpdateGistRequest>,
) -> Result<Json<serde_json::Value>, (StatusCode, Json<ErrorResponse>)> {
let github = state.github.read().await;
let client = github.as_ref().ok_or_else(no_github)?;
let gist = client
.update_gist(&gist_id, &body)
.await
.map_err(github_error)?;
Ok(Json(json!({ "gist": gist })))
}
#[cfg(test)]
mod tests {
use super::github_error;
use axum::http::StatusCode;
#[test]
fn list_gists_response_shape_wraps_gists() {
let payload = serde_json::json!({
"gists": [{
"id": "123",
"description": "notes"
}]
});
assert!(payload.get("gists").unwrap().is_array());
}
#[test]
fn get_gist_response_shape_wraps_gist() {
let payload = serde_json::json!({
"gist": {
"id": "123",
"files": {
"notes.md": {
"content": "hello"
}
}
}
});
assert!(payload.get("gist").unwrap().is_object());
}
#[test]
fn github_auth_errors_map_to_unauthorized() {
let (status, body) = github_error(anyhow::anyhow!("failed with GitHub API status 401"));
assert_eq!(status, StatusCode::UNAUTHORIZED);
assert_eq!(
body.0.error,
"GitHub token is invalid or lacks required access"
);
}
}