fix: remove/resolve fixmes for services (#187)

This commit is contained in:
Nishant Joshi
2022-12-22 11:43:52 +05:30
committed by GitHub
parent 8f803ad507
commit 6fb19c7f4a
8 changed files with 36 additions and 92 deletions

View File

@ -384,7 +384,6 @@ impl From<errors::ApiErrorResponse> for StripeErrorCode {
errors::ApiErrorResponse::PreconditionFailed { message } => { errors::ApiErrorResponse::PreconditionFailed { message } => {
Self::PreconditionFailed { message } Self::PreconditionFailed { message }
} }
errors::ApiErrorResponse::BadCredentials => Self::Unauthorized,
errors::ApiErrorResponse::InvalidDataValue { field_name } => Self::ParameterMissing { errors::ApiErrorResponse::InvalidDataValue { field_name } => Self::ParameterMissing {
field_name: field_name.to_owned(), field_name: field_name.to_owned(),
param: field_name.to_owned(), param: field_name.to_owned(),

View File

@ -13,7 +13,7 @@ use router_env::opentelemetry::metrics::MetricsError;
use storage_models::errors as storage_errors; use storage_models::errors as storage_errors;
pub use self::api_error_response::ApiErrorResponse; pub use self::api_error_response::ApiErrorResponse;
pub(crate) use self::utils::{ApiClientErrorExt, ConnectorErrorExt, StorageErrorExt}; pub(crate) use self::utils::{ConnectorErrorExt, StorageErrorExt};
use crate::services; use crate::services;
pub type RouterResult<T> = CustomResult<T, ApiErrorResponse>; pub type RouterResult<T> = CustomResult<T, ApiErrorResponse>;
pub type RouterResponse<T> = CustomResult<services::BachResponse<T>, ApiErrorResponse>; pub type RouterResponse<T> = CustomResult<services::BachResponse<T>, ApiErrorResponse>;
@ -246,22 +246,8 @@ pub enum ApiClientError {
#[error("Failed to decode response")] #[error("Failed to decode response")]
ResponseDecodingFailed, ResponseDecodingFailed,
#[error("Server responded with Bad Request")]
BadRequestReceived(bytes::Bytes),
#[error("Server responded with Unauthorized")]
UnauthorizedReceived(bytes::Bytes),
#[error("Server responded with Forbidden")]
ForbiddenReceived,
#[error("Server responded with Not Found")]
NotFoundReceived(bytes::Bytes),
#[error("Server responded with Method Not Allowed")]
MethodNotAllowedReceived,
#[error("Server responded with Request Timeout")] #[error("Server responded with Request Timeout")]
RequestTimeoutReceived, RequestTimeoutReceived,
#[error("Server responded with Unprocessable Entity")]
UnprocessableEntityReceived(bytes::Bytes),
#[error("Server responded with Too Many Requests")]
TooManyRequestsReceived,
#[error("Server responded with Internal Server Error")] #[error("Server responded with Internal Server Error")]
InternalServerErrorReceived, InternalServerErrorReceived,

View File

@ -19,15 +19,9 @@ pub enum ErrorType {
pub enum ApiErrorResponse { pub enum ApiErrorResponse {
#[error( #[error(
error_type = ErrorType::InvalidRequestError, code = "IR_01", error_type = ErrorType::InvalidRequestError, code = "IR_01",
message = "API key not provided. Provide API key in the Authorization header, using api-key (e.g api-key: API_KEY)." message = "API key not provided or invalid API key used. Provide API key in the Authorization header or create new API key, using api-key (e.g api-key: API_KEY)."
)] )]
Unauthorized, Unauthorized,
#[error(
error_type = ErrorType::InvalidRequestError, code = "IR_02",
message = "Access forbidden, invalid API key was used. Please create your new API key from \
the Dashboard Settings section."
)]
BadCredentials,
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_03", message = "Unrecognized request URL.")] #[error(error_type = ErrorType::InvalidRequestError, code = "IR_03", message = "Unrecognized request URL.")]
InvalidRequestUrl, InvalidRequestUrl,
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_04", message = "The HTTP method is not applicable for this API.")] #[error(error_type = ErrorType::InvalidRequestError, code = "IR_04", message = "The HTTP method is not applicable for this API.")]
@ -148,9 +142,7 @@ impl actix_web::ResponseError for ApiErrorResponse {
use reqwest::StatusCode; use reqwest::StatusCode;
match self { match self {
Self::Unauthorized | Self::BadCredentials | Self::InvalidEphermeralKey => { Self::Unauthorized | Self::InvalidEphermeralKey => StatusCode::UNAUTHORIZED, // 401
StatusCode::UNAUTHORIZED
} // 401
Self::InvalidRequestUrl => StatusCode::NOT_FOUND, // 404 Self::InvalidRequestUrl => StatusCode::NOT_FOUND, // 404
Self::InvalidHttpMethod => StatusCode::METHOD_NOT_ALLOWED, // 405 Self::InvalidHttpMethod => StatusCode::METHOD_NOT_ALLOWED, // 405
Self::MissingRequiredField { .. } | Self::InvalidDataValue { .. } => { Self::MissingRequiredField { .. } | Self::InvalidDataValue { .. } => {

View File

@ -36,27 +36,6 @@ impl StorageErrorExt for error_stack::Report<errors::StorageError> {
} }
} }
pub(crate) trait ApiClientErrorExt {
fn to_unsuccessful_processing_step_response(
self,
) -> error_stack::Report<errors::ConnectorError>;
}
impl ApiClientErrorExt for error_stack::Report<errors::ApiClientError> {
fn to_unsuccessful_processing_step_response(
self,
) -> error_stack::Report<errors::ConnectorError> {
let data = match self.current_context() {
errors::ApiClientError::BadRequestReceived(bytes)
| errors::ApiClientError::UnauthorizedReceived(bytes)
| errors::ApiClientError::NotFoundReceived(bytes)
| errors::ApiClientError::UnprocessableEntityReceived(bytes) => Some(bytes.clone()),
_ => None,
};
self.change_context(errors::ConnectorError::ProcessingStepFailed(data))
}
}
pub(crate) trait ConnectorErrorExt { pub(crate) trait ConnectorErrorExt {
fn to_refund_failed_response(self) -> error_stack::Report<errors::ApiErrorResponse>; fn to_refund_failed_response(self) -> error_stack::Report<errors::ApiErrorResponse>;
fn to_payment_failed_response(self) -> error_stack::Report<errors::ApiErrorResponse>; fn to_payment_failed_response(self) -> error_stack::Report<errors::ApiErrorResponse>;

View File

@ -18,9 +18,7 @@ pub use self::request::{Method, Request, RequestBuilder};
use crate::{ use crate::{
configs::settings::Connectors, configs::settings::Connectors,
core::{ core::{
errors::{ errors::{self, CustomResult, RouterResponse, RouterResult, StorageErrorExt},
self, ApiClientErrorExt, CustomResult, RouterResponse, RouterResult, StorageErrorExt,
},
payments, payments,
}, },
db::StorageInterface, db::StorageInterface,
@ -173,7 +171,8 @@ where
logger::debug!(?response); logger::debug!(?response);
Ok(response) Ok(response)
} }
Err(error) => Err(error.to_unsuccessful_processing_step_response()), Err(error) => Err(error
.change_context(errors::ConnectorError::ProcessingStepFailed(None))),
} }
} }
None => Ok(router_data), None => Ok(router_data),
@ -205,7 +204,6 @@ async fn send_request(
logger::debug!(method=?request.method, headers=?request.headers, payload=?request.payload, ?request); logger::debug!(method=?request.method, headers=?request.headers, payload=?request.payload, ?request);
let url = &request.url; let url = &request.url;
let should_bypass_proxy = client::proxy_bypass_urls(&state.conf.locker).contains(url); let should_bypass_proxy = client::proxy_bypass_urls(&state.conf.locker).contains(url);
// TODO propogate error for request timeout
let client = client::create_client( let client = client::create_client(
&state.conf.proxy, &state.conf.proxy,
should_bypass_proxy, should_bypass_proxy,
@ -247,8 +245,11 @@ async fn send_request(
Method::Put => client.put(url).add_headers(headers).send().await, Method::Put => client.put(url).add_headers(headers).send().await,
Method::Delete => client.delete(url).add_headers(headers).send().await, Method::Delete => client.delete(url).add_headers(headers).send().await,
} }
.map_err(|error| match error {
error if error.is_timeout() => errors::ApiClientError::RequestTimeoutReceived,
_ => errors::ApiClientError::RequestNotSent,
})
.into_report() .into_report()
.change_context(errors::ApiClientError::RequestNotSent)
.attach_printable("Unable to send request to connector") .attach_printable("Unable to send request to connector")
} }
@ -313,11 +314,8 @@ async fn handle_response(
})) }))
} }
_ => { _ => Err(report!(errors::ApiClientError::UnexpectedServerResponse)
// FIXME: may need to understand redirects .attach_printable("Unexpected response from server")),
Err(report!(errors::ApiClientError::UnexpectedServerResponse)
.attach_printable("Unexpected response from server"))
}
} }
})? })?
.await .await
@ -541,24 +539,16 @@ pub async fn authenticate_merchant<'a>(
authenticate_by_api_key(store, api_key).await authenticate_by_api_key(store, api_key).await
} }
MerchantAuthentication::MerchantId(merchant_id) => { MerchantAuthentication::MerchantId(merchant_id) => store
store
.find_merchant_account_by_merchant_id(&merchant_id) .find_merchant_account_by_merchant_id(&merchant_id)
.await .await
.map_err(|error| { .map_err(|error| error.to_not_found_response(errors::ApiErrorResponse::Unauthorized)),
// TODO: The BadCredentials error is too specific for api keys, and inappropriate for AdminApiKey/MerchantID
// https://juspay.atlassian.net/browse/ORCA-366
error.to_not_found_response(errors::ApiErrorResponse::BadCredentials)
})
}
MerchantAuthentication::AdminApiKey => { MerchantAuthentication::AdminApiKey => {
let admin_api_key = let admin_api_key =
get_api_key(request).change_context(errors::ApiErrorResponse::Unauthorized)?; get_api_key(request).change_context(errors::ApiErrorResponse::Unauthorized)?;
if admin_api_key != "test_admin" { if admin_api_key != "test_admin" {
// TODO: The BadCredentials error is too specific for api keys, and inappropriate Err(report!(errors::ApiErrorResponse::Unauthorized)
// for AdminApiKey/MerchantID
Err(report!(errors::ApiErrorResponse::BadCredentials)
.attach_printable("Admin Authentication Failure"))?; .attach_printable("Admin Authentication Failure"))?;
} }
@ -599,7 +589,7 @@ pub async fn authenticate_connector<'a>(
ConnectorAuthentication::MerchantId(merchant_id) => store ConnectorAuthentication::MerchantId(merchant_id) => store
.find_merchant_account_by_merchant_id(merchant_id) .find_merchant_account_by_merchant_id(merchant_id)
.await .await
.map_err(|error| error.to_not_found_response(errors::ApiErrorResponse::BadCredentials)), .map_err(|error| error.to_not_found_response(errors::ApiErrorResponse::Unauthorized)),
} }
} }
@ -627,7 +617,7 @@ pub(crate) async fn authenticate_eph_key<'a>(
let ek = store let ek = store
.get_ephemeral_key(api_key) .get_ephemeral_key(api_key)
.await .await
.change_context(errors::ApiErrorResponse::BadCredentials)?; .change_context(errors::ApiErrorResponse::Unauthorized)?;
utils::when( utils::when(
ek.customer_id.ne(&customer_id), ek.customer_id.ne(&customer_id),
Err(report!(errors::ApiErrorResponse::InvalidEphermeralKey)), Err(report!(errors::ApiErrorResponse::InvalidEphermeralKey)),
@ -657,7 +647,7 @@ pub async fn authenticate_by_api_key(
store store
.find_merchant_account_by_api_key(api_key) .find_merchant_account_by_api_key(api_key)
.await .await
.change_context(errors::ApiErrorResponse::BadCredentials) .change_context(errors::ApiErrorResponse::Unauthorized)
.attach_printable("Merchant not authenticated") .attach_printable("Merchant not authenticated")
} }
@ -668,7 +658,7 @@ async fn authenticate_by_publishable_key(
store store
.find_merchant_account_by_publishable_key(publishable_key) .find_merchant_account_by_publishable_key(publishable_key)
.await .await
.change_context(errors::ApiErrorResponse::BadCredentials) .change_context(errors::ApiErrorResponse::Unauthorized)
.attach_printable("Merchant not authenticated") .attach_printable("Merchant not authenticated")
} }

View File

@ -100,7 +100,6 @@ pub(super) fn create_client(
.attach_printable_lazy(|| "Error with client library") .attach_printable_lazy(|| "Error with client library")
} }
// TODO: Move to env variable
pub(super) fn proxy_bypass_urls(locker: &Locker) -> Vec<String> { pub(super) fn proxy_bypass_urls(locker: &Locker) -> Vec<String> {
let locker_host = locker.host.to_owned(); let locker_host = locker.host.to_owned();
let basilisk_host = locker.basilisk_host.to_owned(); let basilisk_host = locker.basilisk_host.to_owned();

View File

@ -1,4 +1,4 @@
use std::str::FromStr; use std::{collections, str::FromStr};
use error_stack::{IntoReport, ResultExt}; use error_stack::{IntoReport, ResultExt};
use masking::Secret; use masking::Secret;
@ -10,7 +10,7 @@ use crate::{
logger, logger,
}; };
pub(crate) type Headers = Vec<(String, String)>; pub(crate) type Headers = collections::HashSet<(String, String)>;
#[derive( #[derive(
Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize, strum::Display, strum::EnumString, Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize, strum::Display, strum::EnumString,
@ -46,7 +46,7 @@ impl Request {
Self { Self {
method, method,
url: String::from(url), url: String::from(url),
headers: Vec::new(), headers: collections::HashSet::new(),
payload: None, payload: None,
content_type: None, content_type: None,
certificate: None, certificate: None,
@ -60,7 +60,7 @@ impl Request {
pub fn add_header(&mut self, header: &str, value: &str) { pub fn add_header(&mut self, header: &str, value: &str) {
self.headers self.headers
.push((String::from(header), String::from(value))); .insert((String::from(header), String::from(value)));
} }
pub fn add_content_type(&mut self, content_type: ContentType) { pub fn add_content_type(&mut self, content_type: ContentType) {
@ -91,7 +91,7 @@ impl RequestBuilder {
Self { Self {
method: Method::Get, method: Method::Get,
url: String::with_capacity(1024), url: String::with_capacity(1024),
headers: Vec::new(), headers: std::collections::HashSet::new(),
payload: None, payload: None,
content_type: None, content_type: None,
certificate: None, certificate: None,
@ -110,14 +110,13 @@ impl RequestBuilder {
} }
pub fn header(mut self, header: &str, value: &str) -> Self { pub fn header(mut self, header: &str, value: &str) -> Self {
self.headers.push((header.into(), value.into())); self.headers.insert((header.into(), value.into()));
self self
} }
pub fn headers(mut self, headers: Vec<(String, String)>) -> Self { pub fn headers(mut self, headers: Vec<(String, String)>) -> Self {
// Fixme add union property let mut h = headers.into_iter().map(|(h, v)| (h, v));
let mut h = headers.into_iter().map(|(h, v)| (h, v)).collect(); self.headers.extend(&mut h);
self.headers.append(&mut h);
self self
} }

View File

@ -234,7 +234,7 @@ pub fn decrypt(mut data: Vec<u8>, key: &[u8]) -> CustomResult<String, errors::En
} }
pub fn get_key_id(keys: &Jwekey) -> &str { pub fn get_key_id(keys: &Jwekey) -> &str {
let key_identifier = "1"; //TODO https://github.com/juspay/orca/issues/46 let key_identifier = "1"; // [#46]: Fetch this value from redis or external sources
if key_identifier == "1" { if key_identifier == "1" {
&keys.locker_key_identifier1 &keys.locker_key_identifier1
} else { } else {