Skip to content

Commit

Permalink
payment: use application/problem+json Content-Type for structured errors
Browse files Browse the repository at this point in the history
  • Loading branch information
kamirr committed Jul 9, 2024
1 parent aab950b commit 3cf0f39
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 67 deletions.
49 changes: 35 additions & 14 deletions core/payment/src/api/allocations/api_error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::collections::BTreeMap;

use actix_web::HttpResponse;
use http::Uri;
use problem_details::ProblemDetails;
use serde::Serialize;
Expand All @@ -8,12 +9,14 @@ use ya_core_model::driver::ValidateAllocationResult;

pub type PaymentProblemDetails = ProblemDetails<BTreeMap<String, Value>>;

const CONTENT_TYPE_PROBLEM_JSON: (&str, &str) = ("Content-Type", "application/problem+json");

pub fn try_from_validation(
result: ValidateAllocationResult,
request_body: &impl Serialize,
payment_triple: String,
address: String,
) -> Option<PaymentProblemDetails> {
) -> Option<HttpResponse> {
let mut extensions = BTreeMap::new();

extensions.insert(
Expand Down Expand Up @@ -203,14 +206,23 @@ pub fn try_from_validation(
}
};

Some(details.with_extensions(extensions))
log::error!(
"{}",
details.detail.as_deref().unwrap_or("missing error detail")
);

Some(
HttpResponse::BadRequest()
.insert_header(CONTENT_TYPE_PROBLEM_JSON)
.json(details.with_extensions(extensions)),
)
}

pub fn account_not_registered(
request_body: &impl Serialize,
payment_triple: String,
address: String,
) -> PaymentProblemDetails {
) -> HttpResponse {
let mut extensions = BTreeMap::new();

extensions.insert(
Expand All @@ -227,21 +239,25 @@ pub fn account_not_registered(

extensions.insert("address".to_string(), Value::String(address.clone()));

ProblemDetails::new()
let details = ProblemDetails::new()
.with_type(Uri::from_static(
"/payment-api/v1/allocations/account-not-registered",
))
.with_detail(format!(
"Account {address} not registered for platform {payment_triple}"
))
.with_extensions(extensions)
.with_extensions(extensions);

HttpResponse::InternalServerError()
.insert_header(CONTENT_TYPE_PROBLEM_JSON)
.json(details)
}

pub fn bad_platform_parameter(
request_body: &impl Serialize,
error: &impl Serialize,
requested_payment_platform: &impl Serialize,
) -> PaymentProblemDetails {
) -> HttpResponse {
let mut extensions = BTreeMap::new();

extensions.insert(
Expand All @@ -265,18 +281,19 @@ pub fn bad_platform_parameter(
)),
);

ProblemDetails::new()
let details = ProblemDetails::new()
.with_type(Uri::from_static(
"/payment-api/v1/allocations/bad-payment-platform",
))
.with_detail(format!("Payment platform doesn't parse"))
.with_extensions(extensions)
.with_extensions(extensions);

HttpResponse::BadRequest()
.insert_header(CONTENT_TYPE_PROBLEM_JSON)
.json(details)
}

pub fn server_error(
request_body: &impl Serialize,
error: &impl Serialize,
) -> PaymentProblemDetails {
pub fn server_error(request_body: &impl Serialize, error: &impl Serialize) -> HttpResponse {
let mut extensions = BTreeMap::new();

extensions.insert(
Expand All @@ -293,10 +310,14 @@ pub fn server_error(
)),
);

ProblemDetails::new()
let details = ProblemDetails::new()
.with_type(Uri::from_static(
"/payment-api/v1/allocations/internal-error",
))
.with_detail(format!("Unhandled internal error"))
.with_extensions(extensions)
.with_extensions(extensions);

HttpResponse::InternalServerError()
.insert_header(CONTENT_TYPE_PROBLEM_JSON)
.json(details)
}
75 changes: 22 additions & 53 deletions core/payment/src/api/allocations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,7 @@ async fn create_allocation(
Ok(p) => p,
Err(err) => {
log::error!("Payment platform string parse failed: {err}");
return HttpResponse::BadRequest().json(api_error::bad_platform_parameter(
&allocation,
&err.to_string(),
&name,
));
return api_error::bad_platform_parameter(&allocation, &err.to_string(), &name);
}
};
log::debug!(
Expand All @@ -82,11 +78,11 @@ async fn create_allocation(
Ok(platform_str) => platform_str,
Err(err) => {
log::error!("Payment platform object parse failed: {err}");
return HttpResponse::BadRequest().json(api_error::bad_platform_parameter(
return api_error::bad_platform_parameter(
&allocation,
&err.to_string(),
&payment_platform,
));
);
}
}
}
Expand Down Expand Up @@ -117,8 +113,7 @@ async fn create_allocation(
};

if let Err(err) = init_account(acc).await {
return HttpResponse::InternalServerError()
.json(api_error::server_error(&allocation, &err.to_string()));
return api_error::server_error(&allocation, &err.to_string());
}

let validate_msg = ValidateAllocation {
Expand All @@ -132,21 +127,13 @@ async fn create_allocation(

match async move { Ok(bus::service(LOCAL_SERVICE).send(validate_msg).await??) }.await {
Ok(result) => {
if let Some(problem_details) = api_error::try_from_validation(
if let Some(error_response) = api_error::try_from_validation(
result,
&allocation,
payment_triple.to_string(),
address.clone(),
) {
log::error!(
"{}",
problem_details
.detail
.as_deref()
.unwrap_or("[allocation validation error with no detail]")
);

return HttpResponse::BadRequest().json(problem_details);
return error_response;
}
}
Err(Error::Rpc(RpcMessageError::ValidateAllocation(
Expand All @@ -158,16 +145,13 @@ async fn create_allocation(
payment_triple
);

return HttpResponse::BadRequest().json(api_error::account_not_registered(
return api_error::account_not_registered(
&allocation,
payment_triple.to_string(),
address.clone(),
));
}
Err(e) => {
return HttpResponse::InternalServerError()
.json(api_error::server_error(&allocation, &e.to_string()))
);
}
Err(e) => return api_error::server_error(&allocation, &e.to_string()),
}

let dao = db.as_dao::<AllocationDao>();
Expand Down Expand Up @@ -195,12 +179,11 @@ async fn create_allocation(

response::created(allocation)
}
Ok(AllocationStatus::NotFound) => HttpResponse::InternalServerError()
.json(api_error::server_error(&allocation, &"Database Error")),
Ok(AllocationStatus::Gone) => HttpResponse::InternalServerError()
.json(api_error::server_error(&allocation, &"Database Error")),
Err(e) => HttpResponse::InternalServerError()
.json(api_error::server_error(&allocation, &e.to_string())),
Ok(AllocationStatus::NotFound) => {
api_error::server_error(&allocation, &"Database Error")
}
Ok(AllocationStatus::Gone) => api_error::server_error(&allocation, &"Database Error"),
Err(e) => api_error::server_error(&allocation, &e.to_string()),
},
Err(e) => response::server_error(&e),
}
Expand Down Expand Up @@ -319,21 +302,13 @@ async fn amend_allocation(
};
match async move { Ok(bus::service(LOCAL_SERVICE).send(validate_msg).await??) }.await {
Ok(result) => {
if let Some(problem_details) = api_error::try_from_validation(
if let Some(error_response) = api_error::try_from_validation(
result,
&allocation_update,
payment_triple.to_string(),
amended_allocation.address.clone(),
) {
log::error!(
"{}",
problem_details
.detail
.as_deref()
.unwrap_or("[allocation validation error with no detail]")
);

return HttpResponse::BadRequest().json(problem_details);
return error_response;
}
}
Err(Error::Rpc(RpcMessageError::ValidateAllocation(
Expand All @@ -345,30 +320,24 @@ async fn amend_allocation(
payment_triple
);

return HttpResponse::BadRequest().json(api_error::account_not_registered(
return api_error::account_not_registered(
&allocation_update,
payment_triple.to_string(),
amended_allocation.address.clone(),
));
}
Err(e) => {
return HttpResponse::InternalServerError()
.json(api_error::server_error(&allocation_update, &e.to_string()))
);
}
Err(e) => return api_error::server_error(&allocation_update, &e.to_string()),
}

match dao.replace(amended_allocation, node_id).await {
Ok(true) => {}
Ok(false) => {
return HttpResponse::InternalServerError().json(api_error::server_error(
return api_error::server_error(
&allocation_update,
&"Allocation not present despite preconditions being already ensured",
));
}
Err(e) => {
return HttpResponse::InternalServerError()
.json(api_error::server_error(&allocation_update, &e.to_string()))
);
}
Err(e) => return api_error::server_error(&allocation_update, &e.to_string()),
}

get_allocation(db, path, id).await
Expand Down

0 comments on commit 3cf0f39

Please sign in to comment.