fix: upload difficulty via web form

This commit is contained in:
Aravinth Manivannan 2023-01-26 18:25:01 +05:30
parent 7b4fa1e54c
commit f6030577a0
Signed by: realaravinth
GPG key ID: AD9F0F08E855ED88
5 changed files with 57 additions and 8 deletions

View file

@ -79,6 +79,9 @@ pub enum ServiceError {
#[display(fmt = "Campaign doesn't exist")] #[display(fmt = "Campaign doesn't exist")]
CampaignDoesntExist, CampaignDoesntExist,
#[display(fmt = "Not a number: only numeral data is accepted")]
NotANumber,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -123,6 +126,7 @@ impl ResponseError for ServiceError {
ServiceError::PasswordsDontMatch => StatusCode::BAD_REQUEST, ServiceError::PasswordsDontMatch => StatusCode::BAD_REQUEST,
ServiceError::CampaignDoesntExist => StatusCode::NOT_FOUND, ServiceError::CampaignDoesntExist => StatusCode::NOT_FOUND,
ServiceError::NotANumber => StatusCode::BAD_REQUEST,
} }
} }
} }

View file

@ -30,7 +30,7 @@ pub struct Bench {
ctx: RefCell<Context>, ctx: RefCell<Context>,
} }
pub const BENCH: TemplateFile = TemplateFile::new("new_campaign", "bench/index.html"); pub const BENCH: TemplateFile = TemplateFile::new("new_bench", "bench/index.html");
impl CtxError for Bench { impl CtxError for Bench {
fn with_error(&self, e: &ReadableError) -> String { fn with_error(&self, e: &ReadableError) -> String {

View file

@ -23,6 +23,7 @@ use actix_web::{web, HttpResponse, Responder};
use tera::Context; use tera::Context;
use crate::api::v1::admin::campaigns::{runners, AddCapmaign}; use crate::api::v1::admin::campaigns::{runners, AddCapmaign};
use crate::errors::*;
use crate::AppData; use crate::AppData;
pub use super::*; pub use super::*;
@ -60,23 +61,46 @@ impl NewCampaign {
path = "PAGES.panel.campaigns.new", path = "PAGES.panel.campaigns.new",
wrap = "crate::pages::get_page_check_login()" wrap = "crate::pages::get_page_check_login()"
)] )]
#[tracing::instrument(name = "New campaign form", skip(data))]
pub async fn new_campaign(data: AppData) -> PageResult<impl Responder, NewCampaign> { pub async fn new_campaign(data: AppData) -> PageResult<impl Responder, NewCampaign> {
let new_campaign = NewCampaign::new(&data.settings).render(); let new_campaign = NewCampaign::new(&data.settings).render();
let html = ContentType::html(); let html = ContentType::html();
Ok(HttpResponse::Ok().content_type(html).body(new_campaign)) Ok(HttpResponse::Ok().content_type(html).body(new_campaign))
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FormAddCampaign {
pub name: String,
pub difficulties: String,
}
impl FormAddCampaign {
fn parse(self) -> ServiceResult<AddCapmaign> {
let name = self.name;
let mut difficulties = Vec::new();
for d in self.difficulties.split(',') {
let d = d.parse::<i32>().map_err(|_| ServiceError::NotANumber)?;
difficulties.push(d);
}
Ok(AddCapmaign { name, difficulties })
}
}
#[actix_web_codegen_const_routes::post( #[actix_web_codegen_const_routes::post(
path = "PAGES.panel.campaigns.new", path = "PAGES.panel.campaigns.new",
wrap = "crate::pages::get_page_check_login()" wrap = "crate::pages::get_page_check_login()"
)] )]
#[tracing::instrument(name = "New campaign form submit", skip(data, id))]
pub async fn new_campaign_submit( pub async fn new_campaign_submit(
id: Identity, id: Identity,
payload: web::Json<AddCapmaign>, payload: web::Form<FormAddCampaign>,
data: AppData, data: AppData,
) -> PageResult<impl Responder, NewCampaign> { ) -> PageResult<impl Responder, NewCampaign> {
let username = id.identity().unwrap(); let username = id.identity().unwrap();
let mut payload = payload.into_inner(); let mut payload = payload
.into_inner()
.parse()
.map_err(|e| PageError::new(NewCampaign::new(&data.settings), e))?;
runners::add_runner(&username, &mut payload, &data) runners::add_runner(&username, &mut payload, &data)
.await .await
@ -117,14 +141,23 @@ mod tests {
let (_, _, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await; let (_, _, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await;
let cookies = get_cookie!(signin_resp); let cookies = get_cookie!(signin_resp);
let new = AddCapmaign { let mut difficulties = String::new();
for d in DIFFICULTIES.iter() {
if difficulties.is_empty() {
difficulties = format!("{d}");
} else {
difficulties = format!("{difficulties},{d}");
}
}
println!("{difficulties}");
let new = super::FormAddCampaign {
name: CAMPAIGN_NAME.into(), name: CAMPAIGN_NAME.into(),
difficulties: DIFFICULTIES.into(), difficulties,
}; };
let new_resp = test::call_service( let new_resp = test::call_service(
&app, &app,
post_request!(&new, crate::PAGES.panel.campaigns.new) post_request!(&new, crate::PAGES.panel.campaigns.new, FORM)
.cookie(cookies) .cookie(cookies)
.to_request(), .to_request(),
) )

View file

@ -1,6 +1,6 @@
<h1>Create new campaigns</h1> <h1>Create new campaigns</h1>
<form <form
action="<.= page.panel.campaigns.new .>" action="{{ page.panel.campaigns.new }}"
method="POST" method="POST"
class="new-campaign__form" class="new-campaign__form"
accept-charset="utf-8" accept-charset="utf-8"
@ -17,5 +17,17 @@
type="text" type="text"
/> />
</label> </label>
<label class="form__label" for="difficulties">
Difficulties (comma separated, for example: 555,1111,2222)
<input
class="form__input"
name="difficulties"
placeholder="Comma separated difficulty list, for example: 5000,6000,7000"
required
id="difficulties"
type="text"
/>
</label>
<button class="form__submit" type="submit">Create Campaign</button> <button class="form__submit" type="submit">Create Campaign</button>
</form> </form>

View file

@ -4,7 +4,7 @@
<div class="nav__header"> <div class="nav__header">
<a class="nav__logo-container" href="{{ page.home }}"> <a class="nav__logo-container" href="{{ page.home }}">
<img src="{{ assets.logo.path }}" alt="{{ assets.logo.name }}" class="nav__logo" /> <img src="{{ assets.logo.path }}" alt="{{ assets.logo.name }}" class="nav__logo" />
<p class="nav__logo-text">{{ assets.logo.name }}</p> <p class="nav__logo-text">mCaptcha Survey</p>
</a> </a>
<label class="nav__hamburger-menu" for="nav__toggle"> <label class="nav__hamburger-menu" for="nav__toggle">
<span class="nav__hamburger-inner"></span> <span class="nav__hamburger-inner"></span>