Add proper error handling to the web service
Return 500 when shit happens. Return 404 when the feed is not found
This commit is contained in:
parent
fadf882a1f
commit
96480b6fb9
|
@ -13,7 +13,7 @@ Heavily WIP. Doesn't work yet at all, but does read the stream of posts as they
|
||||||
- [x] Keep subscription state to not lose messages
|
- [x] Keep subscription state to not lose messages
|
||||||
- [x] Serve the feed
|
- [x] Serve the feed
|
||||||
- [x] Handle deleting of posts
|
- [x] Handle deleting of posts
|
||||||
- [ ] Handle errors in the web service gracefully
|
- [x] Handle errors in the web service gracefully
|
||||||
- [x] Handle missing profiles in the profile classifier
|
- [x] Handle missing profiles in the profile classifier
|
||||||
- [ ] Add a way to mark a profile as being from a certain country manually
|
- [ ] Add a way to mark a profile as being from a certain country manually
|
||||||
- [ ] Handle reconnecting to websocket somehow
|
- [ ] Handle reconnecting to websocket somehow
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
mod endpoints;
|
mod endpoints;
|
||||||
mod server;
|
mod server;
|
||||||
mod state;
|
mod state;
|
||||||
|
mod errors;
|
||||||
|
|
||||||
pub use server::FeedServer;
|
pub use server::FeedServer;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::anyhow;
|
||||||
use atrium_api::app::bsky::feed::defs::SkeletonFeedPost;
|
use atrium_api::app::bsky::feed::defs::SkeletonFeedPost;
|
||||||
use atrium_api::app::bsky::feed::get_feed_skeleton::{
|
use atrium_api::app::bsky::feed::get_feed_skeleton::{
|
||||||
Output as FeedSkeleton, Parameters as FeedSkeletonQuery,
|
Output as FeedSkeleton, Parameters as FeedSkeletonQuery,
|
||||||
|
@ -8,29 +8,27 @@ use axum::Json;
|
||||||
use chrono::{DateTime, TimeZone, Utc};
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
|
|
||||||
use crate::processes::feed_server::state::FeedServerState;
|
use crate::processes::feed_server::state::FeedServerState;
|
||||||
|
use crate::processes::feed_server::errors::AppError;
|
||||||
|
|
||||||
pub async fn get_feed_skeleton(
|
pub async fn get_feed_skeleton(
|
||||||
State(state): State<FeedServerState>,
|
State(state): State<FeedServerState>,
|
||||||
query: Query<FeedSkeletonQuery>,
|
query: Query<FeedSkeletonQuery>,
|
||||||
) -> Json<FeedSkeleton> {
|
) -> Result<Json<FeedSkeleton>, AppError> {
|
||||||
let algo = state
|
let algo = state
|
||||||
.algos
|
.algos
|
||||||
.get_by_name(&query.feed)
|
.get_by_name(&query.feed)
|
||||||
.ok_or_else(|| anyhow!("Feed {} not found", query.feed))
|
.ok_or_else(|| AppError::FeedNotFound(query.feed.clone()))?;
|
||||||
.unwrap(); // TODO: handle error
|
|
||||||
|
|
||||||
let limit = query.limit.unwrap_or(20);
|
let limit = query.limit.unwrap_or(20);
|
||||||
let earlier_than = query
|
let earlier_than = query
|
||||||
.cursor
|
.cursor
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.map(parse_cursor)
|
.map(parse_cursor)
|
||||||
.transpose()
|
.transpose()?;
|
||||||
.unwrap(); // TODO: handle error
|
|
||||||
|
|
||||||
let posts = algo
|
let posts = algo
|
||||||
.fetch_posts(&state.database, limit, earlier_than)
|
.fetch_posts(&state.database, limit, earlier_than)
|
||||||
.await
|
.await?;
|
||||||
.unwrap(); // TODO: handle error
|
|
||||||
|
|
||||||
let feed = posts
|
let feed = posts
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -42,21 +40,21 @@ pub async fn get_feed_skeleton(
|
||||||
|
|
||||||
let cursor = posts.last().map(|p| make_cursor(&p.indexed_at, &p.cid));
|
let cursor = posts.last().map(|p| make_cursor(&p.indexed_at, &p.cid));
|
||||||
|
|
||||||
Json(FeedSkeleton { cursor, feed })
|
Ok(Json(FeedSkeleton { cursor, feed }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_cursor(date: &DateTime<Utc>, cid: &str) -> String {
|
fn make_cursor(date: &DateTime<Utc>, cid: &str) -> String {
|
||||||
format!("{}::{}", date.timestamp() * 1000, cid)
|
format!("{}::{}", date.timestamp() * 1000, cid)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_cursor(cursor: &str) -> Result<(DateTime<Utc>, &str)> {
|
fn parse_cursor(cursor: &str) -> anyhow::Result<(DateTime<Utc>, &str)> {
|
||||||
let mut parts = cursor.split("::");
|
let mut parts = cursor.split("::");
|
||||||
|
|
||||||
let indexed_at = parts.next().ok_or_else(|| anyhow!("Malformed cursor"))?;
|
let indexed_at = parts.next().ok_or_else(|| anyhow!("Malformed cursor: {cursor}"))?;
|
||||||
let cid = parts.next().ok_or_else(|| anyhow!("Malformed cursor"))?;
|
let cid = parts.next().ok_or_else(|| anyhow!("Malformed cursor: {cursor}"))?;
|
||||||
|
|
||||||
if parts.next().is_some() {
|
if parts.next().is_some() {
|
||||||
return Err(anyhow!("Malformed cursor"));
|
return Err(anyhow!("Malformed cursor: {cursor}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let indexed_at: i64 = indexed_at.parse()?;
|
let indexed_at: i64 = indexed_at.parse()?;
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
use axum::response::{Response, IntoResponse};
|
||||||
|
use axum::http::StatusCode;
|
||||||
|
|
||||||
|
pub enum AppError {
|
||||||
|
FeedNotFound(String),
|
||||||
|
Other(anyhow::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoResponse for AppError {
|
||||||
|
fn into_response(self) -> Response {
|
||||||
|
match self {
|
||||||
|
Self::FeedNotFound(name) => (StatusCode::NOT_FOUND, format!("Feed not found: {}", name)),
|
||||||
|
Self::Other(e) => (
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
format!("Something went wrong: {}", e),
|
||||||
|
)
|
||||||
|
}.into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> From<E> for AppError
|
||||||
|
where
|
||||||
|
E: Into<anyhow::Error>,
|
||||||
|
{
|
||||||
|
fn from(err: E) -> Self {
|
||||||
|
Self::Other(err.into())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue