From f79343e9e9759f8faa2725e5ae871eec3bef82d0 Mon Sep 17 00:00:00 2001 From: realaravinth Date: Thu, 8 Sep 2022 17:49:51 +0530 Subject: [PATCH] feat: process url encoded and json generic forms --- src/api/v1/forms.rs | 159 ++++++++++++++++++++++++++++++++++++++++++++ src/api/v1/mod.rs | 9 ++- 2 files changed, 167 insertions(+), 1 deletion(-) 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..f25ac8b --- /dev/null +++ b/src/api/v1/forms.rs @@ -0,0 +1,159 @@ +/* + * 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 std::collections::HashMap; + +use actix_web::{web, Error, HttpRequest, HttpResponse, Responder}; +use serde::{Deserialize, Serialize}; + +use super::API_V1_ROUTES; +use crate::AppCtx; + +pub mod routes { + use super::*; + #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] + pub struct Forms { + pub submit: &'static str, + } + impl Forms { + pub const fn new() -> Self { + Self { + submit: "/api/v1/forms/submit", + } + } + } +} + +pub fn services(cfg: &mut web::ServiceConfig) { + cfg.service(upload); +} + +#[derive(Deserialize, Serialize, Debug)] +#[serde(untagged)] +enum FormDType { + Num(f64), + Str(String), +} + +impl FormDType { + fn apply_types(&mut self) { + if let Self::Str(data) = self { + if let Ok(num) = data.parse::() { + *self = Self::Num(num); + } + } + } +} + +pub type FormValue = HashMap; + +#[actix_web_codegen_const_routes::post(path = "API_V1_ROUTES.forms.submit")] +async fn upload( + req: HttpRequest, + ctx: AppCtx, + payload: web::Either, web::Form>, +) -> Result { + let c = req.connection_info(); + let host = c.host(); + let path = req.uri(); + + let data = match payload { + web::Either::Left(json) => json.into_inner(), + web::Either::Right(form) => { + let mut form = form.into_inner(); + for (_, v) in form.iter_mut() { + v.apply_types(); + } + serde_json::to_value(&form).unwrap() + } + }; + + println!("{:?}", data); + //ctx.gitea.report(&payload).await; + Ok(HttpResponse::Ok().json(data)) +} + +#[cfg(test)] +pub mod tests { + use actix_web::{ + http::{header, StatusCode}, + test, App, + }; + + use super::*; + use crate::*; + + #[derive(Serialize, Clone, Deserialize, PartialEq)] + struct Foo { + foo: String, + num: f64, + } + + #[actix_rt::test] + async fn submit_works() { + // const USERNAME: &str = "index_works"; + // const PASSWORD: &str = "23k4j;123k4j1;l23kj4"; + let settings = Settings::new().unwrap(); + // let settings = Settings::new().unwrap(); + let ctx = AppCtx::new(crate::ctx::Ctx::new(&settings).await); + let app = test::init_service( + App::new() + .app_data(ctx.clone()) + .configure(crate::routes::services), + ) + .await; + + let foo = Foo { + foo: "Foo".into(), + num: 2.33, + }; + + // upload json + let upload_json = test::call_service( + &app, + test::TestRequest::post() + .uri(API_V1_ROUTES.forms.submit) + .set_json(&foo) + .to_request(), + ) + .await; + if upload_json.status() != StatusCode::OK { + let resp_err: crate::errors::ErrorToResponse = test::read_body_json(upload_json).await; + panic!("{:?}", resp_err.error); + } + + assert_eq!(upload_json.status(), StatusCode::OK); + let json: serde_json::Value = test::read_body_json(upload_json).await; + + // upload url encoded + let upload_form = test::call_service( + &app, + test::TestRequest::post() + .uri(API_V1_ROUTES.forms.submit) + .set_form(&foo) + .to_request(), + ) + .await; + if upload_form.status() != StatusCode::OK { + let resp_err: crate::errors::ErrorToResponse = test::read_body_json(upload_form).await; + panic!("{:?}", resp_err.error); + } + + assert_eq!(upload_form.status(), StatusCode::OK); + let form: serde_json::Value = test::read_body_json(upload_form).await; + assert_eq!(form, json); + } +} diff --git a/src/api/v1/mod.rs b/src/api/v1/mod.rs index d8cb4c6..c01a405 100644 --- a/src/api/v1/mod.rs +++ b/src/api/v1/mod.rs @@ -16,6 +16,7 @@ */ use actix_web::web; +pub mod forms; pub mod meta; pub const API_V1_ROUTES: routes::Routes = routes::Routes::new(); @@ -25,18 +26,24 @@ pub struct SignedInUser(String); pub fn services(cfg: &mut web::ServiceConfig) { meta::services(cfg); + forms::services(cfg); } pub mod routes { + use crate::api::v1::forms::routes::Forms; use crate::api::v1::meta::routes::Meta; pub struct Routes { pub meta: Meta, + pub forms: Forms, } impl Routes { pub const fn new() -> Self { - Self { meta: Meta::new() } + Self { + meta: Meta::new(), + forms: Forms::new(), + } } } }