use crate::auth::extractor::AuthUser; use crate::error::AppError; use crate::json_serialization::user::JsonUser; use crate::models::feed::rss_feed::Feed; use crate::models::feed_item::rss_feed_item::FeedItem; use crate::reader::structs::feed::FeedAggregate; use crate::schema::feed_item::{feed_id, id, read}; use crate::{ database::establish_connection, json_serialization::articles::Articles, schema::feed::{self, user_id}, schema::feed_item, }; use actix_web::{web, HttpRequest, HttpResponse, Responder}; use chrono::Local; use diesel::prelude::*; use super::structs::article::Article; pub async fn get( path: web::Path, req: HttpRequest, auth_user: AuthUser, ) -> Result { let request = req.clone(); let req_user_id = path.user_id; log::info!("Received user_id: {}", req_user_id); if auth_user.0 != req_user_id { return Ok(HttpResponse::Forbidden().finish()); } let mut connection: diesel::PgConnection = establish_connection(); let feeds: Vec = feed::table .filter(user_id.eq(req_user_id)) .load::(&mut connection)?; let mut feed_aggregates: Vec = Vec::new(); for feed in feeds { let existing_item: Vec = feed_item::table .filter(feed_id.eq(feed.id)) .filter(read.eq(false)) .order(id.asc()) .load(&mut connection)?; log::info!( "Load {} feed items for feed: {}", existing_item.len(), feed.url ); let article_list: Vec
= existing_item .into_iter() .map(|feed_item: FeedItem| { let time: String = match feed_item.created_ts { Some(r) => r.to_string(), None => Local::now().naive_local().to_string(), }; Article { title: feed_item.title, content: feed_item.content, url: feed_item.url, timestamp: time, id: feed_item.id, } }) .collect(); log::info!("article list with {} items generated.", article_list.len()); feed_aggregates.push(FeedAggregate { title: feed.title, items: article_list, }) } let articles: Articles = Articles { feeds: feed_aggregates, }; Ok(articles.respond_to(&request).map_into_boxed_body()) } #[cfg(test)] mod tests { use actix_service::Service; use actix_web::http::StatusCode; use actix_web::{test, web, App, HttpMessage}; use super::get; use crate::auth::extractor::AuthUser; use crate::database::establish_connection; use crate::test_helpers::{ delete_feed, delete_feed_item, delete_user, insert_feed, insert_feed_item, insert_user, }; #[actix_web::test] async fn get_returns_only_unread_items() { let mut connection = establish_connection(); let user = insert_user(&mut connection, "secret"); let feed = insert_feed(&mut connection, user.id); let unread = insert_feed_item(&mut connection, feed.id, false); let read = insert_feed_item(&mut connection, feed.id, true); let user_id = user.id; let app = test::init_service( App::new() .wrap_fn(move |req, srv| { req.extensions_mut().insert(AuthUser(user_id)); srv.call(req) }) .route("/get/{user_id}", web::get().to(get)), ) .await; let req = test::TestRequest::get() .uri(&format!("/get/{}", 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(&unread.title)); assert!(!body_str.contains(&read.title)); delete_feed_item(&mut connection, unread.id); delete_feed_item(&mut connection, read.id); delete_feed(&mut connection, feed.id); delete_user(&mut connection, user.id); } #[actix_web::test] async fn get_rejects_requests_for_another_user() { let mut connection = establish_connection(); let user_a = insert_user(&mut connection, "secret"); let user_b = insert_user(&mut connection, "secret"); let app = test::init_service( App::new() .wrap_fn(move |req, srv| { req.extensions_mut().insert(AuthUser(user_a.id)); srv.call(req) }) .route("/get/{user_id}", web::get().to(get)), ) .await; let req = test::TestRequest::get() .uri(&format!("/get/{}", user_b.id)) .to_request(); let resp = test::call_service(&app, req).await; assert_eq!(StatusCode::FORBIDDEN, resp.status()); delete_user(&mut connection, user_a.id); delete_user(&mut connection, user_b.id); } }