diff --git a/src/api/v1/notifications/mark_read.rs b/src/api/v1/notifications/mark_read.rs new file mode 100644 index 00000000..5a5287af --- /dev/null +++ b/src/api/v1/notifications/mark_read.rs @@ -0,0 +1,156 @@ +/* +* Copyright (C) 2021 Aravinth Manivannan +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +*/ + +use actix_identity::Identity; +use actix_web::{web, HttpResponse, Responder}; +use serde::{Deserialize, Serialize}; + +use crate::errors::*; +use crate::Data; + +#[derive(Deserialize, Serialize)] +pub struct MarkReadReq { + pub id: i32, +} + +#[derive(Deserialize, Serialize)] +pub struct NotificationResp { + pub name: String, + pub heading: String, + pub message: String, + pub received: i64, + pub id: i32, +} + +/// route handler that marks a notification read +#[my_codegen::post(path="crate::V1_API_ROUTES.notifications.mark_read", wrap="crate::CheckLogin")] +pub async fn mark_read( + data: web::Data, + payload: web::Json, + id: Identity, +) -> ServiceResult { + let receiver = id.identity().unwrap(); + // TODO handle error where payload.to doesnt exist + + sqlx::query_file_as!( + Notification, + "src/api/v1/notifications/mark_read.sql", + payload.id, + &receiver + ) + .execute(&data.db) + .await?; + + Ok(HttpResponse::Ok()) +} + +#[cfg(test)] +mod tests { + use actix_web::http::{header, StatusCode}; + use actix_web::test; + + use super::*; + use crate::api::v1::notifications::add::AddNotification; + use crate::tests::*; + use crate::*; + + + #[actix_rt::test] + async fn notification_mark_read_works() { + const NAME1: &str = "notifuser122"; + const NAME2: &str = "notiuser222"; + const PASSWORD: &str = "longpassworddomain"; + const EMAIL1: &str = "testnotification122@a.com"; + const EMAIL2: &str = "testnotification222@a.com"; + const HEADING: &str = "testing notifications get"; + const MESSAGE: &str = "testing notifications get message"; + + { + let data = Data::new().await; + delete_user(NAME1, &data).await; + delete_user(NAME2, &data).await; + } + + register_and_signin(NAME1, EMAIL1, PASSWORD).await; + register_and_signin(NAME2, EMAIL2, PASSWORD).await; + let (data, _creds, signin_resp) = signin(NAME1, PASSWORD).await; + let (_data, _creds2, signin_resp2) = signin(NAME2, PASSWORD).await; + let cookies = get_cookie!(signin_resp); + let cookies2 = get_cookie!(signin_resp2); + let mut app = get_app!(data).await; + + let msg = AddNotification { + to: NAME2.into(), + heading: HEADING.into(), + message: MESSAGE.into(), + }; + + let send_notification_resp = test::call_service( + &mut app, + post_request!(&msg, V1_API_ROUTES.notifications.add) + .cookie(cookies.clone()) + .to_request(), + ) + .await; + assert_eq!(send_notification_resp.status(), StatusCode::OK); + + let get_notifications_resp = test::call_service( + &mut app, + test::TestRequest::get() + .uri(V1_API_ROUTES.notifications.get) + .cookie(cookies2.clone()) + .to_request(), + ) + .await; + assert_eq!(get_notifications_resp.status(), StatusCode::OK); + + let mut notifications: Vec = + test::read_body_json(get_notifications_resp).await; + let notification = notifications.pop().unwrap(); + assert_eq!(notification.name, NAME1); + assert_eq!(notification.message, MESSAGE); + assert_eq!(notification.heading, HEADING); + + + let mark_read_payload = MarkReadReq { + id: notification.id.clone(), + }; + let mark_read_resp = test::call_service( + &mut app, + post_request!(&mark_read_payload, V1_API_ROUTES.notifications.mark_read) + .cookie(cookies2.clone()) + .to_request(), + ) + .await; + assert_eq!(mark_read_resp.status(), StatusCode::OK); + + + let get_notifications_resp = test::call_service( + &mut app, + test::TestRequest::get() + .uri(V1_API_ROUTES.notifications.get) + .cookie(cookies2.clone()) + .to_request(), + ) + .await; + assert_eq!(get_notifications_resp.status(), StatusCode::OK); + let mut notifications: Vec = + test::read_body_json(get_notifications_resp).await; + assert!(notifications.pop().is_none()); + + } +} diff --git a/src/api/v1/notifications/mark_read.sql b/src/api/v1/notifications/mark_read.sql new file mode 100644 index 00000000..97ff3267 --- /dev/null +++ b/src/api/v1/notifications/mark_read.sql @@ -0,0 +1,14 @@ +-- mark a notification as read +UPDATE mcaptcha_notifications + SET read = TRUE +WHERE + mcaptcha_notifications.id = $1 +AND + mcaptcha_notifications.rx = ( + SELECT + id + FROM + mcaptcha_users + WHERE + name = $2 + ); diff --git a/src/api/v1/notifications/mod.rs b/src/api/v1/notifications/mod.rs index 1e8527ea..0d09ec48 100644 --- a/src/api/v1/notifications/mod.rs +++ b/src/api/v1/notifications/mod.rs @@ -17,6 +17,7 @@ mod add; mod get; +mod mark_read; pub mod routes { @@ -30,7 +31,7 @@ pub mod routes { pub const fn new() -> Notifications { Notifications { add: "/api/v1/notifications/add", - mark_read: "/api/v1/notifications/read/", + mark_read: "/api/v1/notifications/read", get: "/api/v1/notifications/get", } } @@ -40,4 +41,5 @@ pub mod routes { pub fn services(cfg: &mut actix_web::web::ServiceConfig) { cfg.service(add::add_notification); cfg.service(get::get_notification); + cfg.service(mark_read::mark_read); }