Skip to content

Commit

Permalink
feat(pm_auth): pm_auth service migration
Browse files Browse the repository at this point in the history
  • Loading branch information
Chethan-rao committed Dec 4, 2023
1 parent 9d93533 commit 643da1e
Show file tree
Hide file tree
Showing 14 changed files with 861 additions and 0 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/CI-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ jobs:
else
echo "test_utils_changes_exist=true" >> $GITHUB_ENV
fi
if git diff --submodule=diff --exit-code --quiet origin/$GITHUB_BASE_REF -- crates/pm_auth/; then
echo "pm_auth_changes_exist=false" >> $GITHUB_ENV
else
echo "pm_auth_changes_exist=true" >> $GITHUB_ENV
fi
- name: Cargo hack api_models
if: env.api_models_changes_exist == 'true'
Expand Down Expand Up @@ -249,6 +254,11 @@ jobs:
shell: bash
run: cargo hack check --each-feature --no-dev-deps -p redis_interface

- name: Cargo hack pm_auth
if: env.pm_auth_changes_exist == 'true'
shell: bash
run: cargo hack check --each-feature --no-dev-deps -p pm_auth

- name: Cargo hack router
if: env.router_changes_exist == 'true'
shell: bash
Expand Down Expand Up @@ -456,6 +466,11 @@ jobs:
else
echo "test_utils_changes_exist=true" >> $GITHUB_ENV
fi
if git diff --submodule=diff --exit-code --quiet origin/$GITHUB_BASE_REF -- crates/pm_auth/; then
echo "pm_auth_changes_exist=false" >> $GITHUB_ENV
else
echo "pm_auth_changes_exist=true" >> $GITHUB_ENV
fi
- name: Cargo hack api_models
if: env.api_models_changes_exist == 'true'
Expand Down Expand Up @@ -502,6 +517,11 @@ jobs:
shell: bash
run: cargo hack check --each-feature --no-dev-deps -p redis_interface

- name: Cargo hack pm_auth
if: env.pm_auth_changes_exist == 'true'
shell: bash
run: cargo hack check --each-feature --no-dev-deps -p pm_auth

- name: Cargo hack router
if: env.router_changes_exist == 'true'
shell: bash
Expand Down
21 changes: 21 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions crates/pm_auth/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "pm_auth"
description = "Open banking services"
version = "0.1.0"
edition.workspace = true
rust-version.workspace = true
readme = "README.md"

[dependencies]
# First party crates
api_models = { version = "0.1.0", path = "../api_models" }
common_enums = { version = "0.1.0", path = "../common_enums" }
common_utils = { version = "0.1.0", path = "../common_utils" }
masking = { version = "0.1.0", path = "../masking" }
router_derive = { version = "0.1.0", path = "../router_derive" }
router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] }

# Third party crates
async-trait = "0.1.66"
bytes = "1.4.0"
error-stack = "0.3.1"
http = "0.2.9"
mime = "0.3.17"
serde = "1.0.159"
serde_json = "1.0.91"
strum = { version = "0.24.1", features = ["derive"] }
thiserror = "1.0.43"
3 changes: 3 additions & 0 deletions crates/pm_auth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Payment Method Auth Services

An open banking services for payment method auth validation
3 changes: 3 additions & 0 deletions crates/pm_auth/src/connector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod plaid;

pub use self::plaid::Plaid;
268 changes: 268 additions & 0 deletions crates/pm_auth/src/connector/plaid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
pub mod transformers;

use std::fmt::Debug;

use common_utils::{
ext_traits::{BytesExt, Encode},
request::{Method, Request, RequestBody, RequestBuilder},
};
use error_stack::ResultExt;
use masking::{Mask, Maskable};
use transformers as plaid;

use crate::{
core::errors,
types::{
self as auth_types,
api::{
auth_service::{self, ExchangeToken, LinkToken},
ConnectorCommon, ConnectorCommonExt, ConnectorIntegration,
},
},
};

#[derive(Debug, Clone)]
pub struct Plaid;

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Plaid
where
Self: ConnectorIntegration<Flow, Request, Response>,
{
fn build_headers(
&self,
req: &auth_types::PaymentAuthRouterData<Flow, Request, Response>,
_connectors: &auth_types::PaymentMethodAuthConnectors,
) -> errors::CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
let mut header = vec![(
"Content-Type".to_string(),
self.get_content_type().to_string().into(),
)];

let mut auth = self.get_auth_header(&req.connector_auth_type)?;
header.append(&mut auth);
Ok(header)
}
}

impl ConnectorCommon for Plaid {
fn id(&self) -> &'static str {
"plaid"
}

fn common_get_content_type(&self) -> &'static str {
"application/json"
}
fn base_url<'a>(&self, _connectors: &'a auth_types::PaymentMethodAuthConnectors) -> &'a str {
"https://sandbox.plaid.com"
}

fn get_auth_header(
&self,
auth_type: &auth_types::ConnectorAuthType,
) -> errors::CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
let auth = plaid::PlaidAuthType::try_from(auth_type)
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
let client_id = auth.client_id.into_masked();
let secret = auth.secret.into_masked();

Ok(vec![
("PLAID-CLIENT-ID".to_string(), client_id),
("PLAID-SECRET".to_string(), secret),
])
}

fn build_error_response(
&self,
res: auth_types::Response,
) -> errors::CustomResult<auth_types::ErrorResponse, errors::ConnectorError> {
let response: plaid::PlaidErrorResponse =
res.response
.parse_struct("PlaidErrorResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
Ok(auth_types::ErrorResponse {
status_code: res.status_code,
code: crate::consts::NO_ERROR_CODE.to_string(),
message: response.error_message,
reason: response.display_message,
})
}
}

impl auth_service::AuthService for Plaid {}
impl auth_service::AuthServiceLinkToken for Plaid {}

impl ConnectorIntegration<LinkToken, auth_types::LinkTokenRequest, auth_types::LinkTokenResponse>
for Plaid
{
fn get_headers(
&self,
req: &auth_types::LinkTokenRouterData,
connectors: &auth_types::PaymentMethodAuthConnectors,
) -> errors::CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
self.build_headers(req, connectors)
}

fn get_content_type(&self) -> &'static str {
self.common_get_content_type()
}

fn get_url(
&self,
_req: &auth_types::LinkTokenRouterData,
connectors: &auth_types::PaymentMethodAuthConnectors,
) -> errors::CustomResult<String, errors::ConnectorError> {
Ok(format!(
"{}{}",
self.base_url(connectors),
"/link/token/create"
))
}

fn get_request_body(
&self,
req: &auth_types::LinkTokenRouterData,
) -> errors::CustomResult<Option<RequestBody>, errors::ConnectorError> {
let req_obj = plaid::PlaidLinkTokenRequest::try_from(req)?;
let plaid_req = RequestBody::log_and_get_request_body(
&req_obj,
Encode::<plaid::PlaidLinkTokenRequest>::encode_to_string_of_json,
)
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(Some(plaid_req))
}

fn build_request(
&self,
req: &auth_types::LinkTokenRouterData,
connectors: &auth_types::PaymentMethodAuthConnectors,
) -> errors::CustomResult<Option<Request>, errors::ConnectorError> {
Ok(Some(
RequestBuilder::new()
.method(Method::Post)
.url(&auth_types::PaymentAuthLinkTokenType::get_url(
self, req, connectors,
)?)
.attach_default_headers()
.headers(auth_types::PaymentAuthLinkTokenType::get_headers(
self, req, connectors,
)?)
.body(auth_types::PaymentAuthLinkTokenType::get_request_body(
self, req,
)?)
.build(),
))
}

fn handle_response(
&self,
data: &auth_types::LinkTokenRouterData,
res: auth_types::Response,
) -> errors::CustomResult<auth_types::LinkTokenRouterData, errors::ConnectorError> {
let response: plaid::PlaidLinkTokenResponse = res
.response
.parse_struct("PlaidLinkTokenResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
<auth_types::LinkTokenRouterData>::try_from(auth_types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
}
fn get_error_response(
&self,
res: auth_types::Response,
) -> errors::CustomResult<auth_types::ErrorResponse, errors::ConnectorError> {
self.build_error_response(res)
}
}

impl auth_service::AuthServiceExchangeToken for Plaid {}

impl
ConnectorIntegration<
ExchangeToken,
auth_types::ExchangeTokenRequest,
auth_types::ExchangeTokenResponse,
> for Plaid
{
fn get_headers(
&self,
req: &auth_types::ExchangeTokenRouterData,
connectors: &auth_types::PaymentMethodAuthConnectors,
) -> errors::CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
self.build_headers(req, connectors)
}

fn get_content_type(&self) -> &'static str {
self.common_get_content_type()
}

fn get_url(
&self,
_req: &auth_types::ExchangeTokenRouterData,
connectors: &auth_types::PaymentMethodAuthConnectors,
) -> errors::CustomResult<String, errors::ConnectorError> {
Ok(format!(
"{}{}",
self.base_url(connectors),
"/item/public_token/exchange"
))
}

fn get_request_body(
&self,
req: &auth_types::ExchangeTokenRouterData,
) -> errors::CustomResult<Option<RequestBody>, errors::ConnectorError> {
let req_obj = plaid::PlaidExchangeTokenRequest::try_from(req)?;
let plaid_req = RequestBody::log_and_get_request_body(
&req_obj,
Encode::<plaid::PlaidExchangeTokenRequest>::encode_to_string_of_json,
)
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(Some(plaid_req))
}

fn build_request(
&self,
req: &auth_types::ExchangeTokenRouterData,
connectors: &auth_types::PaymentMethodAuthConnectors,
) -> errors::CustomResult<Option<Request>, errors::ConnectorError> {
Ok(Some(
RequestBuilder::new()
.method(Method::Post)
.url(&auth_types::PaymentAuthExchangeTokenType::get_url(
self, req, connectors,
)?)
.attach_default_headers()
.headers(auth_types::PaymentAuthExchangeTokenType::get_headers(
self, req, connectors,
)?)
.body(auth_types::PaymentAuthExchangeTokenType::get_request_body(
self, req,
)?)
.build(),
))
}

fn handle_response(
&self,
data: &auth_types::ExchangeTokenRouterData,
res: auth_types::Response,
) -> errors::CustomResult<auth_types::ExchangeTokenRouterData, errors::ConnectorError> {
let response: plaid::PlaidExchangeTokenResponse = res
.response
.parse_struct("PlaidExchangeTokenResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
<auth_types::ExchangeTokenRouterData>::try_from(auth_types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
}
fn get_error_response(
&self,
res: auth_types::Response,
) -> errors::CustomResult<auth_types::ErrorResponse, errors::ConnectorError> {
self.build_error_response(res)
}
}
Loading

0 comments on commit 643da1e

Please sign in to comment.