Skip to content

Commit

Permalink
feat(newsletter): support public double opt-in (#187)
Browse files Browse the repository at this point in the history
* feature(newsletter): support public double opt-in

This das the route /api/v1/newsletter to allow public sign up to our
newsletter, once we made it non-private basket

* dynamic url

* add test
  • Loading branch information
fiji-flo authored May 16, 2023
1 parent 4860b43 commit e83d4ad
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 7 deletions.
5 changes: 4 additions & 1 deletion src/api/api_v1.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::api::newsletter::{is_subscribed, subscribe_handler, unsubscribe_handler};
use crate::api::newsletter::{
is_subscribed, subscribe_anonymous_handler, subscribe_handler, unsubscribe_handler,
};
use crate::api::ping::ping;
use crate::api::root::root_service;
use crate::api::search::search;
Expand All @@ -22,5 +24,6 @@ pub fn api_v1_service() -> impl HttpServiceFactory {
.service(web::resource("/search").route(web::get().to(search)))
.service(web::resource("/whoami").route(web::get().to(whoami)))
.service(web::resource("/ping").route(web::post().to(ping)))
.service(web::resource("/newsletter").route(web::post().to(subscribe_anonymous_handler)))
.service(root_service())
}
50 changes: 47 additions & 3 deletions src/api/newsletter.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use actix_identity::Identity;
use actix_web::{web::Data, HttpResponse};
use basket::{Basket, YesNo};
use actix_web::{
web::{self, Data},
HttpResponse,
};
use basket::{Basket, SubscribeOpts, YesNo};
use diesel::PgConnection;
use serde::{Deserialize, Serialize};

Expand All @@ -12,6 +15,7 @@ use crate::{
users::get_user,
Pool,
},
settings::SETTINGS,
};

const MDN_PLUS_LIST: &str = "mdnplus";
Expand All @@ -27,6 +31,11 @@ struct Subscribed {
pub subscribed: bool,
}

#[derive(Deserialize, Serialize)]
pub struct SubscriptionRequest {
pub email: String,
}

pub async fn subscribe_handler(
pool: Data<Pool>,
user_id: Identity,
Expand All @@ -40,13 +49,48 @@ pub async fn subscribe_handler(
Ok(HttpResponse::NotImplemented().finish())
}

pub async fn subscribe_anonymous_handler(
basket: Data<Option<Basket>>,
subscription_req: web::Json<SubscriptionRequest>,
) -> Result<HttpResponse, ApiError> {
if let Some(basket) = &**basket {
basket
.subscribe(
&subscription_req.email,
vec![MDN_PLUS_LIST.into()],
Some(SubscribeOpts {
source_url: Some(format!(
"{}/en-US/newsletter",
&SETTINGS.application.document_base_url
)),
..Default::default()
}),
)
.await?;

return Ok(HttpResponse::Created().json(Subscribed { subscribed: true }));
}
Ok(HttpResponse::NotImplemented().finish())
}

pub async fn subscribe(
conn: &mut PgConnection,
user: &UserQuery,
basket: &Basket,
) -> Result<HttpResponse, ApiError> {
basket
.subscribe_private(&user.email, vec![MDN_PLUS_LIST.into()], None)
.subscribe_private(
&user.email,
vec![MDN_PLUS_LIST.into()],
Some(SubscribeOpts {
optin: Some(YesNo::Y),
source_url: Some(format!(
"{}/en-US/settings",
&SETTINGS.application.document_base_url
)),
..Default::default()
}),
)
.await?;
db::settings::create_or_update_settings(
conn,
Expand Down
52 changes: 49 additions & 3 deletions tests/api/newsletter.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use crate::helpers::app::test_app_with_login;
use crate::helpers::app::{init_test, test_app_with_login};
use crate::helpers::db::reset;
use crate::helpers::http_client::TestHttpClient;
use crate::helpers::{read_json, wait_for_stubr};
use actix_web::test;
use anyhow::Error;
use serde_json::json;
use stubr::{Config, Stubr};

#[actix_rt::test]
#[stubr::mock(port = 4321)]
async fn whoami_settings_test() -> Result<(), Error> {
async fn settings_newsletter_test() -> Result<(), Error> {
let pool = reset()?;
wait_for_stubr().await?;
let app = test_app_with_login(&pool).await?;
Expand Down Expand Up @@ -41,7 +42,10 @@ async fn whoami_settings_test() -> Result<(), Error> {

drop(stubr);
let stubr = Stubr::start_blocking_with(
vec!["tests/stubs", "tests/test_specific_stubs/newsletter"],
vec![
"tests/stubs",
"tests/test_specific_stubs/newsletter/basket_lookup_user.json",
],
Config {
port: Some(4321),
latency: None,
Expand All @@ -67,3 +71,45 @@ async fn whoami_settings_test() -> Result<(), Error> {
drop(stubr);
Ok(())
}

#[actix_rt::test]
#[stubr::mock(port = 4321)]
async fn anonymous_newsletter_test() -> Result<(), Error> {
let pool = reset()?;
wait_for_stubr().await?;
let app = test_app_with_login(&pool).await.unwrap();
let service = test::init_service(app).await;
let request = test::TestRequest::post()
.set_json(json!({ "email": "[email protected]"}))
.uri("/api/v1/newsletter")
.to_request();
let newsletter_res = test::call_service(&service, request).await;

assert!(newsletter_res.status().is_success());

drop(stubr);
Ok(())
}

#[actix_rt::test]
async fn anonymous_newsletter_error_test() -> Result<(), Error> {
let (_, stubr) = init_test(vec![
"tests/stubs",
"tests/test_specific_stubs/newsletter/basket_subscribe_error.json",
])
.await?;

let pool = reset()?;
let app = test_app_with_login(&pool).await.unwrap();
let service = test::init_service(app).await;
let request = test::TestRequest::post()
.set_json(json!({ "email": "[email protected]"}))
.uri("/api/v1/newsletter")
.to_request();
let newsletter_res = test::call_service(&service, request).await;

assert!(newsletter_res.status().is_server_error());

drop(stubr);
Ok(())
}
17 changes: 17 additions & 0 deletions tests/test_specific_stubs/newsletter/basket_subscribe_error.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"uuid": "basket_subscribe",
"priority": 1,
"request": {
"method": "POST",
"url": "/news/subscribe/"
},
"response": {
"status": 500,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"status": "error"
}
}
}

0 comments on commit e83d4ad

Please sign in to comment.