add conditional put

This commit is contained in:
Sebastian Hugentobler 2022-03-30 09:36:59 +02:00
parent b4fcbdfef8
commit 547b7b375a
Signed by: shu
GPG key ID: BB32CF3CA052C2F0
7 changed files with 87 additions and 21 deletions

View file

@ -1,9 +1,7 @@
use actix_web::{put, web, HttpResponse, Responder, Result};
use actix_web::{put, web, HttpRequest, HttpResponse, Responder, Result};
use bank::account::AccountError;
use serde::Serialize;
use bank::bank::Bank;
use crate::handlers::error::HttpAccountError;
use crate::handlers::AmountData;
use crate::AppState;
@ -18,6 +16,7 @@ pub async fn route(
info: web::Path<String>,
form: web::Form<AmountData>,
data: web::Data<AppState>,
req: HttpRequest,
) -> Result<impl Responder> {
let nr = info.into_inner();
let amount = form.amount;
@ -29,11 +28,22 @@ pub async fn route(
info!("updating account {}...", nr);
let bank = data.bank.read().unwrap();
match Bank::account_action(bank, &nr, |account| {
account.balance = amount;
Ok(amount)
}) {
Err(e) => Err(HttpAccountError(e).into()),
Ok(_) => Ok(HttpResponse::Ok().finish()),
match bank.accounts.get(&nr) {
None => Err(HttpAccountError(AccountError::NotFound).into()),
Some(account) => {
let mut account = account.write().unwrap();
let hash = account.hash_string();
if let Some(etag) = req.headers().get("If-Match") {
let etag = etag.to_str().unwrap_or("");
if etag != hash {
Ok(HttpResponse::PreconditionFailed().finish())
} else {
account.balance = amount;
Ok(HttpResponse::Ok().finish())
}
} else {
Ok(HttpResponse::PreconditionRequired().finish())
}
}
}
}

View file

@ -155,6 +155,30 @@ mod tests {
.set_form(&payload)
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::PRECONDITION_REQUIRED);
let payload = AmountData { amount: 10_f64 };
let req = test::TestRequest::put()
.uri(&format!("/accounts/{}", nr))
.append_header((actix_web::http::header::IF_MATCH, "I aspire to be an etag"))
.set_form(&payload)
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::PRECONDITION_FAILED);
let acc = bank::account::Account {
number: nr.to_string(),
owner: "aaa".to_string(),
balance: 5_f64,
is_active: true
};
let payload = AmountData { amount: 10_f64 };
let req = test::TestRequest::put()
.uri(&format!("/accounts/{}", nr))
.append_header((actix_web::http::header::IF_MATCH, acc.hash_string()))
.set_form(&payload)
.to_request();
let resp = test::call_service(&app, req).await;
assert!(resp.status().is_success());
let req = test::TestRequest::put()