mirror of
https://github.com/LukeMathWalker/zero-to-production.git
synced 2024-05-18 16:28:08 +00:00
118 lines
3.2 KiB
Rust
118 lines
3.2 KiB
Rust
use crate::authentication::UserId;
|
|
use crate::idempotency::{save_response, try_processing, IdempotencyKey, NextAction};
|
|
use crate::utils::e400;
|
|
use crate::utils::{e500, see_other};
|
|
use actix_web::{web, HttpResponse};
|
|
use actix_web_flash_messages::FlashMessage;
|
|
use anyhow::Context;
|
|
use sqlx::{Executor, PgPool, Postgres, Transaction};
|
|
use uuid::Uuid;
|
|
|
|
#[derive(serde::Deserialize)]
|
|
pub struct FormData {
|
|
title: String,
|
|
text_content: String,
|
|
html_content: String,
|
|
idempotency_key: String,
|
|
}
|
|
|
|
fn success_message() -> FlashMessage {
|
|
FlashMessage::info(
|
|
"The newsletter issue has been accepted - \
|
|
emails will go out shortly.",
|
|
)
|
|
}
|
|
|
|
#[tracing::instrument(
|
|
name = "Publish a newsletter issue",
|
|
skip_all,
|
|
fields(user_id=%&*user_id)
|
|
)]
|
|
pub async fn publish_newsletter(
|
|
form: web::Form<FormData>,
|
|
pool: web::Data<PgPool>,
|
|
user_id: web::ReqData<UserId>,
|
|
) -> Result<HttpResponse, actix_web::Error> {
|
|
let user_id = user_id.into_inner();
|
|
let FormData {
|
|
title,
|
|
text_content,
|
|
html_content,
|
|
idempotency_key,
|
|
} = form.0;
|
|
let idempotency_key: IdempotencyKey = idempotency_key.try_into().map_err(e400)?;
|
|
let mut transaction = match try_processing(&pool, &idempotency_key, *user_id)
|
|
.await
|
|
.map_err(e500)?
|
|
{
|
|
NextAction::StartProcessing(t) => t,
|
|
NextAction::ReturnSavedResponse(saved_response) => {
|
|
success_message().send();
|
|
return Ok(saved_response);
|
|
}
|
|
};
|
|
let issue_id = insert_newsletter_issue(&mut transaction, &title, &text_content, &html_content)
|
|
.await
|
|
.context("Failed to store newsletter issue details")
|
|
.map_err(e500)?;
|
|
enqueue_delivery_tasks(&mut transaction, issue_id)
|
|
.await
|
|
.context("Failed to enqueue delivery tasks")
|
|
.map_err(e500)?;
|
|
let response = see_other("/admin/newsletters");
|
|
let response = save_response(transaction, &idempotency_key, *user_id, response)
|
|
.await
|
|
.map_err(e500)?;
|
|
success_message().send();
|
|
Ok(response)
|
|
}
|
|
|
|
#[tracing::instrument(skip_all)]
|
|
async fn insert_newsletter_issue(
|
|
transaction: &mut Transaction<'_, Postgres>,
|
|
title: &str,
|
|
text_content: &str,
|
|
html_content: &str,
|
|
) -> Result<Uuid, sqlx::Error> {
|
|
let newsletter_issue_id = Uuid::new_v4();
|
|
let query = sqlx::query!(
|
|
r#"
|
|
INSERT INTO newsletter_issues (
|
|
newsletter_issue_id,
|
|
title,
|
|
text_content,
|
|
html_content,
|
|
published_at
|
|
)
|
|
VALUES ($1, $2, $3, $4, now())
|
|
"#,
|
|
newsletter_issue_id,
|
|
title,
|
|
text_content,
|
|
html_content
|
|
);
|
|
transaction.execute(query).await?;
|
|
Ok(newsletter_issue_id)
|
|
}
|
|
|
|
#[tracing::instrument(skip_all)]
|
|
async fn enqueue_delivery_tasks(
|
|
transaction: &mut Transaction<'_, Postgres>,
|
|
newsletter_issue_id: Uuid,
|
|
) -> Result<(), sqlx::Error> {
|
|
let query = sqlx::query!(
|
|
r#"
|
|
INSERT INTO issue_delivery_queue (
|
|
newsletter_issue_id,
|
|
subscriber_email
|
|
)
|
|
SELECT $1, email
|
|
FROM subscriptions
|
|
WHERE status = 'confirmed'
|
|
"#,
|
|
newsletter_issue_id,
|
|
);
|
|
transaction.execute(query).await?;
|
|
Ok(())
|
|
}
|