From b8e52ff82a2c397d9f98c6bf415ec612c43fec8f Mon Sep 17 00:00:00 2001 From: Aravinth Manivannan Date: Tue, 14 Mar 2023 15:51:53 +0530 Subject: [PATCH] feat: add export page --- src/pages/mod.rs | 5 +- src/pages/panel/export.rs | 149 ++++++++++++++++++++++++++++++++++++ src/pages/panel/mod.rs | 10 ++- templates/panel/export.html | 43 +++++++++++ 4 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 src/pages/panel/export.rs create mode 100644 templates/panel/export.html diff --git a/src/pages/mod.rs b/src/pages/mod.rs index 88ad09f..7e62993 100644 --- a/src/pages/mod.rs +++ b/src/pages/mod.rs @@ -66,10 +66,13 @@ pub const FOOTER: TemplateFile = pub const PANEL_NAV: TemplateFile = TemplateFile::new("panel_nav", "panel/nav/index.html"); +pub const PUBLIC_NAV: TemplateFile = + TemplateFile::new("pub_nav", "panel/nav/public.html"); + lazy_static! { pub static ref TEMPLATES: Tera = { let mut tera = Tera::default(); - for t in [BASE, FOOTER, PANEL_NAV].iter() { + for t in [BASE, FOOTER, PANEL_NAV, PUBLIC_NAV].iter() { t.register(&mut tera).unwrap(); } errors::register_templates(&mut tera); diff --git a/src/pages/panel/export.rs b/src/pages/panel/export.rs new file mode 100644 index 0000000..b44f3f6 --- /dev/null +++ b/src/pages/panel/export.rs @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2023 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 std::cell::RefCell; + +use actix_web::http::header::ContentType; +use actix_web::{HttpResponse, Responder}; +use serde::{Deserialize, Serialize}; +use tera::Context; + +use crate::api::v1::admin::campaigns::{runners, ListCampaignResp}; +use crate::settings::Settings; +use crate::AppData; + +use super::*; +pub use crate::pages::errors::*; + +pub struct ExportPage { + ctx: RefCell, +} + +pub const EXPORT_CAMPAIGNS: TemplateFile = + TemplateFile::new("export_campaigns", "panel/export.html"); + +impl CtxError for ExportPage { + fn with_error(&self, e: &ReadableError) -> String { + self.ctx.borrow_mut().insert(ERROR_KEY, e); + self.render() + } +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] +pub struct ExportPagePayload { + pub campaigns: Vec, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] +pub struct ExportListCampaign { + pub uuid: String, + pub name: String, + pub export_link: String, + pub route: String, +} + +impl From for ExportListCampaign { + fn from(value: ListCampaignResp) -> Self { + use crate::DOWNLOAD_SCOPE; + + let route = PAGES.panel.campaigns.get_bench_route(&value.uuid); + let export_link = format!("{DOWNLOAD_SCOPE}/{}", value.uuid); + Self { + uuid: value.uuid, + name: value.name, + export_link, + route, + } + } +} + +impl ExportPage { + pub fn new(settings: &Settings, payload: Option) -> Self { + let ctx = RefCell::new(context(settings, "Results")); + if let Some(payload) = payload { + ctx.borrow_mut().insert(PAYLOAD_KEY, &payload); + } + Self { ctx } + } + + pub fn render(&self) -> String { + TEMPLATES + .render(EXPORT_CAMPAIGNS.name, &self.ctx.borrow()) + .unwrap() + } +} + +#[actix_web_codegen_const_routes::get(path = "PAGES.panel.export")] +pub async fn export_campaigns(data: AppData) -> PageResult { + let mut campaigns = runners::list_all_campaigns(&data) + .await + .map_err(|e| PageError::new(ExportPage::new(&data.settings, None), e))?; + let campaigns = campaigns.drain(0..).map(|c| c.into()).collect(); + let payload = ExportPagePayload { campaigns }; + + let results_page = ExportPage::new(&data.settings, Some(payload)).render(); + let html = ContentType::html(); + Ok(HttpResponse::Ok().content_type(html).body(results_page)) +} + +pub fn services(cfg: &mut actix_web::web::ServiceConfig) { + cfg.service(export_campaigns); +} + +#[cfg(test)] +mod tests { + use actix_web::test; + + use super::*; + + use crate::tests::*; + use crate::*; + use actix_web::http::StatusCode; + + #[actix_rt::test] + async fn export_campaigns_works() { + const NAME: &str = "pubexportcampaignuser"; + const EMAIL: &str = "pubexportcampaignuser@aaa.com"; + const PASSWORD: &str = "longpassword"; + const CAMPAIGN_NAME: &str = "pubexportcampaignusercampaign"; + + let data = get_test_data().await; + let app = get_app!(data).await; + delete_user(NAME, &data).await; + let (_, _, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await; + let cookies = get_cookie!(signin_resp); + + let uuid = + create_new_campaign(CAMPAIGN_NAME, data.clone(), cookies.clone()).await; + + let home_resp = get_request!( + &app, + &PAGES + .panel + .campaigns + .get_about_route(&data.settings.default_campaign) + ); + + assert_eq!(home_resp.status(), StatusCode::OK); + let body = String::from_utf8(test::read_body(home_resp).await.to_vec()).unwrap(); + assert!(body.contains(PAGES.panel.export)); + + let export_page_resp = get_request!(&app, PAGES.panel.export); + assert_eq!(export_page_resp.status(), StatusCode::OK); + let body = + String::from_utf8(test::read_body(export_page_resp).await.to_vec()).unwrap(); + assert!(body.contains(&uuid.campaign_id.to_string())); + } +} diff --git a/src/pages/panel/mod.rs b/src/pages/panel/mod.rs index 1ad711f..13d5700 100644 --- a/src/pages/panel/mod.rs +++ b/src/pages/panel/mod.rs @@ -19,12 +19,13 @@ pub use super::{context, Footer, TemplateFile, PAGES, PAYLOAD_KEY, TEMPLATES}; use crate::AppData; mod campaigns; +mod export; pub fn register_templates(t: &mut tera::Tera) { campaigns::register_templates(t); - // for template in [REGISTER].iter() { - // template.register(t).expect(template.name); - // } + for template in [export::EXPORT_CAMPAIGNS].iter() { + template.register(t).expect(template.name); + } } pub mod routes { @@ -34,6 +35,7 @@ pub mod routes { #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] pub struct Panel { pub home: &'static str, + pub export: &'static str, pub campaigns: Campaigns, } impl Panel { @@ -41,6 +43,7 @@ pub mod routes { let campaigns = Campaigns::new(); Panel { home: "/", + export: "/export", campaigns, } } @@ -55,6 +58,7 @@ pub mod routes { pub fn services(cfg: &mut actix_web::web::ServiceConfig) { cfg.service(home); campaigns::services(cfg); + export::services(cfg); } #[actix_web_codegen_const_routes::get(path = "PAGES.panel.home")] diff --git a/templates/panel/export.html b/templates/panel/export.html new file mode 100644 index 0000000..c4eeb8b --- /dev/null +++ b/templates/panel/export.html @@ -0,0 +1,43 @@ +{% extends 'base' %} +{% block body %} + +
+ {% if payload %} + + + + + + + + + + {% for campaign in payload.campaigns %} + + + + + + + {% endfor %} + +
IDNameExport Link
+ +

{{ campaign.name }}

+
+
+ +

{{ campaign.uuid }}

+
+ +

Click Here

+
+ + {% else %} +

Looks like there are no campaigns on this instance

+ {% endif %} +
+ +{% endblock body %}