From 4272ea103a8b7f92f40c48c5c056fc3c5dc4bbe7 Mon Sep 17 00:00:00 2001 From: Aravinth Manivannan Date: Thu, 29 Dec 2022 17:00:19 +0530 Subject: [PATCH] feat: REST endpoints to list submissions and delete form submission with auth --- src/api/v1/forms.rs | 175 ++++++++++++++++++++++++++++++++++++++++ src/api/v1/mod.rs | 2 + src/api/v1/routes.rs | 3 + src/ctx/api/v1/forms.rs | 9 +-- 4 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 src/api/v1/forms.rs diff --git a/src/api/v1/forms.rs b/src/api/v1/forms.rs new file mode 100644 index 0000000..7a5cda3 --- /dev/null +++ b/src/api/v1/forms.rs @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2022 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 libforms::Table; +use serde::{Deserialize, Serialize}; + +use super::get_auth_middleware; +use crate::{errors::*, AppCtx}; + +pub mod routes { + + pub struct Forms { + pub list_submissions: &'static str, + pub delete_submission: &'static str, + } + + impl Forms { + pub const fn new() -> Self { + Self { + list_submissions: "/api/v1/forms/list", + delete_submission: "/api/v1/forms/delete/{id}", + } + } + + pub fn get_list(&self, page: usize) -> String { + format!("{}?page={}", self.list_submissions, page) + } + + pub fn get_delete(&self, id: usize, host: &str, path: &str) -> String { + let del = self.delete_submission.replace("{id}", &id.to_string()); + format!("{}?host={}&path={}", del, host, path) + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct Page { + page: usize, +} + +#[actix_web_codegen_const_routes::post( + path = "crate::V1_API_ROUTES.forms.list_submissions", + wrap = "get_auth_middleware()" +)] +#[tracing::instrument(name = "List form submission" skip(id, ctx, payload))] +async fn list_submission( + ctx: AppCtx, + id: Identity, + page: web::Query, + payload: web::Json, +) -> ServiceResult { + let owner = id.identity().unwrap(); + let resp = ctx + .get_all_form_submission(&owner, page.page, &payload) + .await?; + Ok(HttpResponse::Ok().json(resp)) +} + +#[actix_web_codegen_const_routes::post( + path = "crate::V1_API_ROUTES.forms.delete_submission", + wrap = "get_auth_middleware()" +)] +#[tracing::instrument(name = "Delete form submission" skip(id, ctx))] +async fn delete_form_submission( + ctx: AppCtx, + id: Identity, + sub_id: web::Path, + payload: web::Json
, +) -> ServiceResult { + let owner = id.identity().unwrap(); + ctx.delete_form_submission(&owner, *sub_id, &payload) + .await?; + Ok(HttpResponse::Ok()) +} + +pub fn services(cfg: &mut web::ServiceConfig) { + cfg.service(list_submission); + cfg.service(delete_form_submission); +} + +#[cfg(test)] +mod tests { + use actix_web::{http::StatusCode, test}; + + use crate::tests; + use crate::*; + + use libforms::*; + + #[actix_rt::test] + async fn test_api_forms() { + const NAME: &str = "testapiformuser"; + const PASSWORD: &str = "longpasswordasdfa2"; + const EMAIL: &str = "testapiformuser@a.com"; + + let (_dir, ctx) = tests::get_ctx().await; + let _ = ctx.delete_user(NAME, PASSWORD).await; + let (_, signin_resp) = ctx.register_and_signin(NAME, EMAIL, PASSWORD).await; + let page = ctx.add_test_site(NAME.into()).await; + let cookies = get_cookie!(signin_resp); + let app = get_app!(ctx).await; + + let site_info = Table { + host: page.domain.clone(), + path: format!("/foo/{NAME}"), + }; + + if let Ok(subs) = ctx.get_all_form_submission(NAME, 0, &site_info).await { + for s in subs.iter() { + let _ = ctx.delete_form_submission(NAME, s.id, &site_info).await; + } + } + ctx.add_form_submission(NAME, &site_info, &serde_json::to_value(&site_info).unwrap()) + .await + .unwrap(); + + // list subs using REST API + let list_form_submissions = test::call_service( + &app, + post_request!(&site_info, &V1_API_ROUTES.forms.get_list(0)) + .cookie(cookies.clone()) + .to_request(), + ) + .await; + assert_eq!(list_form_submissions.status(), StatusCode::OK); + let subs: Vec = test::read_body_json(list_form_submissions).await; + assert_eq!(subs.len(), 1); + assert_eq!( + subs[0].value, + Some(serde_json::to_value(&site_info).unwrap()) + ); + + // delete form submission + let delete_form_submission_resp = test::call_service( + &app, + post_request!( + &site_info, + &V1_API_ROUTES + .forms + .get_delete(subs[0].id, &site_info.host, &site_info.path) + ) + .cookie(cookies.clone()) + .to_request(), + ) + .await; + assert_eq!(delete_form_submission_resp.status(), StatusCode::OK); + + // list subs using REST API post deletion. Len = 0 + let list_form_submissions = test::call_service( + &app, + post_request!(&site_info, &V1_API_ROUTES.forms.get_list(0)) + .cookie(cookies.clone()) + .to_request(), + ) + .await; + assert_eq!(list_form_submissions.status(), StatusCode::OK); + let subs: Vec = test::read_body_json(list_form_submissions).await; + assert!(subs.is_empty()); + } +} diff --git a/src/api/v1/mod.rs b/src/api/v1/mod.rs index 19b5047..fce2047 100644 --- a/src/api/v1/mod.rs +++ b/src/api/v1/mod.rs @@ -21,6 +21,7 @@ use serde::Deserialize; pub mod account; pub mod auth; pub mod forgejo; +pub mod forms; pub mod meta; pub mod pages; pub mod routes; @@ -32,6 +33,7 @@ pub fn services(cfg: &mut ServiceConfig) { account::services(cfg); meta::services(cfg); forgejo::services(cfg); + forms::services(cfg); pages::services(cfg); } diff --git a/src/api/v1/routes.rs b/src/api/v1/routes.rs index d7acc75..175280d 100644 --- a/src/api/v1/routes.rs +++ b/src/api/v1/routes.rs @@ -20,6 +20,7 @@ use actix_auth_middleware::GetLoginRoute; use crate::serve::routes::Serve; use super::forgejo::routes::Forgejo; +use super::forms::routes::Forms; use super::meta::routes::Meta; use super::pages::routes::Deploy; @@ -94,6 +95,7 @@ pub struct Routes { /// Meta routes pub meta: Meta, pub forgejo: Forgejo, + pub forms: Forms, pub deploy: Deploy, pub serve: Serve, } @@ -106,6 +108,7 @@ impl Routes { account: Account::new(), meta: Meta::new(), forgejo: Forgejo::new(), + forms: Forms::new(), deploy: Deploy::new(), serve: Serve::new(), } diff --git a/src/ctx/api/v1/forms.rs b/src/ctx/api/v1/forms.rs index 60302ce..3ca5a55 100644 --- a/src/ctx/api/v1/forms.rs +++ b/src/ctx/api/v1/forms.rs @@ -61,20 +61,17 @@ impl Ctx { .json(payload) .send() .await + .unwrap() + .json() + .await .unwrap(); - - println!("{:?}", res); - let res = res.json().await.unwrap(); Ok(res) } } #[cfg(test)] mod tests { - use actix_web::{http::StatusCode, test}; - use crate::tests; - use crate::*; use super::*;