150 lines
5.0 KiB
Rust
150 lines
5.0 KiB
Rust
use crate::auth::extractor::AuthUser;
|
|
use crate::error::AppError;
|
|
use crate::schema::feed_item::{id, read};
|
|
use crate::{
|
|
database::establish_connection,
|
|
json_serialization::read_feed_item::ReadItem,
|
|
models::feed_item::rss_feed_item::FeedItem,
|
|
schema::{feed, feed_item},
|
|
};
|
|
use actix_web::{web, HttpRequest, HttpResponse, Responder};
|
|
use diesel::RunQueryDsl;
|
|
use diesel::{ExpressionMethods, OptionalExtension, QueryDsl};
|
|
|
|
pub async fn mark_read(
|
|
_req: HttpRequest,
|
|
path: web::Path<ReadItem>,
|
|
auth_user: AuthUser,
|
|
) -> Result<impl Responder, AppError> {
|
|
let mut connection = establish_connection();
|
|
log::info!("Id: {}", path.id);
|
|
|
|
// Join through to `feed` so we can confirm the item belongs to the caller
|
|
// before mutating it. "Doesn't exist" and "not yours" both return 404.
|
|
let owned_item: Option<(FeedItem, i32)> = feed_item::table
|
|
.inner_join(feed::table)
|
|
.filter(id.eq(path.id))
|
|
.select((feed_item::all_columns, feed::user_id))
|
|
.first(&mut connection)
|
|
.optional()?;
|
|
|
|
let feed_item = match owned_item {
|
|
Some((feed_item, owner_id)) if owner_id == auth_user.0 => feed_item,
|
|
_ => return Ok(HttpResponse::NotFound().finish()),
|
|
};
|
|
|
|
let result = diesel::update(&feed_item)
|
|
.set(read.eq(true))
|
|
.execute(&mut connection)?;
|
|
|
|
log::info!("Mark as read: {:?}", result);
|
|
|
|
Ok(HttpResponse::Ok().finish())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use actix_service::Service;
|
|
use actix_web::http::StatusCode;
|
|
use actix_web::{test, web, App, HttpMessage};
|
|
use diesel::prelude::*;
|
|
|
|
use super::mark_read;
|
|
use crate::auth::extractor::AuthUser;
|
|
use crate::database::establish_connection;
|
|
use crate::models::feed_item::rss_feed_item::FeedItem;
|
|
use crate::schema::feed_item;
|
|
use crate::test_helpers::{
|
|
delete_feed, delete_feed_item, delete_user, insert_feed, insert_feed_item, insert_user,
|
|
};
|
|
|
|
#[actix_web::test]
|
|
async fn mark_read_flips_the_read_flag() {
|
|
let mut connection = establish_connection();
|
|
let user = insert_user(&mut connection, "secret");
|
|
let feed = insert_feed(&mut connection, user.id);
|
|
let item = insert_feed_item(&mut connection, feed.id, false);
|
|
|
|
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("/read/{id}", web::put().to(mark_read)),
|
|
)
|
|
.await;
|
|
let req = test::TestRequest::put()
|
|
.uri(&format!("/read/{}", item.id))
|
|
.to_request();
|
|
let resp = test::call_service(&app, req).await;
|
|
|
|
assert_eq!(StatusCode::OK, resp.status());
|
|
|
|
let updated: FeedItem = feed_item::table
|
|
.find(item.id)
|
|
.first(&mut connection)
|
|
.unwrap();
|
|
assert!(updated.read);
|
|
|
|
delete_feed_item(&mut connection, item.id);
|
|
delete_feed(&mut connection, feed.id);
|
|
delete_user(&mut connection, user.id);
|
|
}
|
|
|
|
#[actix_web::test]
|
|
async fn mark_read_returns_not_found_for_unknown_id() {
|
|
let app = test::init_service(
|
|
App::new()
|
|
.wrap_fn(move |req, srv| {
|
|
req.extensions_mut().insert(AuthUser(1));
|
|
srv.call(req)
|
|
})
|
|
.route("/read/{id}", web::put().to(mark_read)),
|
|
)
|
|
.await;
|
|
let req = test::TestRequest::put().uri("/read/999999999").to_request();
|
|
let resp = test::call_service(&app, req).await;
|
|
|
|
assert_eq!(StatusCode::NOT_FOUND, resp.status());
|
|
}
|
|
|
|
#[actix_web::test]
|
|
async fn mark_read_rejects_other_users_item() {
|
|
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 item_b = insert_feed_item(&mut connection, feed_b.id, false);
|
|
|
|
let user_a_id = user_a.id;
|
|
let app = test::init_service(
|
|
App::new()
|
|
.wrap_fn(move |req, srv| {
|
|
req.extensions_mut().insert(AuthUser(user_a_id));
|
|
srv.call(req)
|
|
})
|
|
.route("/read/{id}", web::put().to(mark_read)),
|
|
)
|
|
.await;
|
|
let req = test::TestRequest::put()
|
|
.uri(&format!("/read/{}", item_b.id))
|
|
.to_request();
|
|
let resp = test::call_service(&app, req).await;
|
|
|
|
assert_eq!(StatusCode::NOT_FOUND, resp.status());
|
|
|
|
let updated: FeedItem = feed_item::table
|
|
.find(item_b.id)
|
|
.first(&mut connection)
|
|
.unwrap();
|
|
assert!(!updated.read);
|
|
|
|
delete_feed_item(&mut connection, item_b.id);
|
|
delete_feed(&mut connection, feed_b.id);
|
|
delete_user(&mut connection, user_a.id);
|
|
delete_user(&mut connection, user_b.id);
|
|
}
|
|
}
|