added admin area to delete feeds

This commit is contained in:
2026-06-09 21:55:07 +02:00
parent 6ae6490dec
commit 400648c3d1
5 changed files with 401 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
use actix_web::http::StatusCode;
use actix_web::{HttpResponse, Responder};
use serde::Serialize;
#[derive(Serialize)]
pub struct FeedInfo {
pub id: i32,
pub title: String,
pub url: String,
}
#[derive(Serialize)]
pub struct FeedInfoList {
pub feeds: Vec<FeedInfo>,
}
impl Responder for FeedInfoList {
type Body = String;
fn respond_to(self, _req: &actix_web::HttpRequest) -> actix_web::HttpResponse<Self::Body> {
let body = serde_json::to_string(&self).unwrap();
HttpResponse::with_body(StatusCode::OK, body)
}
}
+124
View File
@@ -0,0 +1,124 @@
use actix_web::{web, HttpResponse};
use diesel::prelude::*;
use crate::{
database::establish_connection,
schema::{feed, feed_item},
};
pub async fn delete_feed(path: web::Path<i32>) -> HttpResponse {
let feed_id = path.into_inner();
let mut connection = establish_connection();
let exists = feed::table
.find(feed_id)
.count()
.get_result::<i64>(&mut connection)
.unwrap_or(0);
if exists == 0 {
return HttpResponse::NotFound().finish();
}
diesel::delete(feed_item::table.filter(feed_item::feed_id.eq(feed_id)))
.execute(&mut connection)
.ok();
diesel::delete(feed::table.filter(feed::id.eq(feed_id)))
.execute(&mut connection)
.ok();
HttpResponse::NoContent().finish()
}
#[cfg(test)]
mod tests {
use actix_web::http::StatusCode;
use actix_web::{test, web, App};
use diesel::prelude::*;
use super::delete_feed;
use crate::database::establish_connection;
use crate::schema::{feed, feed_item};
use crate::test_helpers::{
delete_feed as cleanup_feed, delete_user, insert_feed, insert_feed_item, insert_user,
};
#[actix_web::test]
async fn delete_feed_removes_feed_and_items() {
let mut connection = establish_connection();
let user = insert_user(&mut connection, "secret");
let f = insert_feed(&mut connection, user.id);
let item = insert_feed_item(&mut connection, f.id, false);
let app = test::init_service(
App::new().route("/feed/{feed_id}", web::delete().to(delete_feed)),
)
.await;
let req = test::TestRequest::delete()
.uri(&format!("/feed/{}", f.id))
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(StatusCode::NO_CONTENT, resp.status());
let feed_exists: i64 = feed::table
.filter(feed::id.eq(f.id))
.count()
.get_result(&mut connection)
.unwrap();
assert_eq!(0, feed_exists);
let item_exists: i64 = feed_item::table
.filter(feed_item::id.eq(item.id))
.count()
.get_result(&mut connection)
.unwrap();
assert_eq!(0, item_exists);
delete_user(&mut connection, user.id);
}
#[actix_web::test]
async fn delete_feed_returns_404_for_nonexistent_feed() {
let app = test::init_service(
App::new().route("/feed/{feed_id}", web::delete().to(delete_feed)),
)
.await;
let req = test::TestRequest::delete()
.uri("/feed/999999999")
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(StatusCode::NOT_FOUND, resp.status());
}
#[actix_web::test]
async fn delete_feed_does_not_affect_other_feeds() {
let mut connection = establish_connection();
let user = insert_user(&mut connection, "secret");
let feed_a = insert_feed(&mut connection, user.id);
let feed_b = insert_feed(&mut connection, user.id);
let app = test::init_service(
App::new().route("/feed/{feed_id}", web::delete().to(delete_feed)),
)
.await;
let req = test::TestRequest::delete()
.uri(&format!("/feed/{}", feed_a.id))
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(StatusCode::NO_CONTENT, resp.status());
let feed_b_exists: i64 = feed::table
.filter(feed::id.eq(feed_b.id))
.count()
.get_result(&mut connection)
.unwrap();
assert_eq!(1, feed_b_exists);
cleanup_feed(&mut connection, feed_b.id);
delete_user(&mut connection, user.id);
}
}
+111
View File
@@ -0,0 +1,111 @@
use actix_web::{web, HttpRequest, Responder};
use diesel::prelude::*;
use crate::{
database::establish_connection,
json_serialization::feed_info::{FeedInfo, FeedInfoList},
json_serialization::user::JsonUser,
schema::feed::{self, user_id},
};
pub async fn list_feeds(path: web::Path<JsonUser>, req: HttpRequest) -> impl Responder {
let request = req.clone();
let req_user_id = path.user_id;
let mut connection = establish_connection();
let feeds = feed::table
.filter(user_id.eq(req_user_id))
.select((feed::id, feed::title, feed::url))
.load::<(i32, String, String)>(&mut connection)
.unwrap();
let feed_list: Vec<FeedInfo> = feeds
.into_iter()
.map(|(id, title, url)| FeedInfo { id, title, url })
.collect();
FeedInfoList { feeds: feed_list }.respond_to(&request)
}
#[cfg(test)]
mod tests {
use actix_web::http::StatusCode;
use actix_web::{test, web, App};
use super::list_feeds;
use crate::database::establish_connection;
use crate::test_helpers::{delete_feed, delete_user, insert_feed, insert_user};
#[actix_web::test]
async fn list_feeds_returns_feeds_for_user() {
let mut connection = establish_connection();
let user = insert_user(&mut connection, "secret");
let feed = insert_feed(&mut connection, user.id);
let app = test::init_service(
App::new().route("/feeds/{user_id}", web::get().to(list_feeds)),
)
.await;
let req = test::TestRequest::get()
.uri(&format!("/feeds/{}", user.id))
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(StatusCode::OK, resp.status());
let body = test::read_body(resp).await;
let body_str = String::from_utf8(body.to_vec()).unwrap();
assert!(body_str.contains(&feed.title));
assert!(body_str.contains(&feed.url));
delete_feed(&mut connection, feed.id);
delete_user(&mut connection, user.id);
}
#[actix_web::test]
async fn list_feeds_returns_empty_list_for_user_with_no_feeds() {
let mut connection = establish_connection();
let user = insert_user(&mut connection, "secret");
let app = test::init_service(
App::new().route("/feeds/{user_id}", web::get().to(list_feeds)),
)
.await;
let req = test::TestRequest::get()
.uri(&format!("/feeds/{}", user.id))
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(StatusCode::OK, resp.status());
let body = test::read_body(resp).await;
let body_str = String::from_utf8(body.to_vec()).unwrap();
assert!(body_str.contains("\"feeds\":[]"));
delete_user(&mut connection, user.id);
}
#[actix_web::test]
async fn list_feeds_does_not_return_other_users_feeds() {
let mut connection = establish_connection();
let user_a = insert_user(&mut connection, "secret");
let user_b = insert_user(&mut connection, "secret");
let feed_b = insert_feed(&mut connection, user_b.id);
let app = test::init_service(
App::new().route("/feeds/{user_id}", web::get().to(list_feeds)),
)
.await;
let req = test::TestRequest::get()
.uri(&format!("/feeds/{}", user_a.id))
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(StatusCode::OK, resp.status());
let body = test::read_body(resp).await;
let body_str = String::from_utf8(body.to_vec()).unwrap();
assert!(!body_str.contains(&feed_b.title));
delete_feed(&mut connection, feed_b.id);
delete_user(&mut connection, user_a.id);
delete_user(&mut connection, user_b.id);
}
}