Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(router): Move organization_id to request header from request body for v2 #6277

Merged
merged 11 commits into from
Oct 25, 2024
17 changes: 4 additions & 13 deletions api-reference-v2/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MerchantAccountCreate"
"$ref": "#/components/schemas/MerchantAccountCreateWithoutOrgId"
},
"examples": {
"Create a merchant account with merchant details": {
Expand All @@ -482,8 +482,7 @@
},
"Create a merchant account with minimal fields": {
"value": {
"merchant_name": "Cloth Store",
"organization_id": "org_abcdefghijklmnop"
"merchant_name": "Cloth Store"
}
}
}
Expand Down Expand Up @@ -9029,11 +9028,10 @@
}
}
},
"MerchantAccountCreate": {
"MerchantAccountCreateWithoutOrgId": {
"type": "object",
"required": [
"merchant_name",
"organization_id"
"merchant_name"
],
"properties": {
"merchant_name": {
Expand All @@ -9054,13 +9052,6 @@
"type": "object",
"description": "Metadata is useful for storing additional, unstructured information about the merchant account.",
"nullable": true
},
"organization_id": {
"type": "string",
"description": "The id of the organization to which the merchant belongs to. Please use the organization endpoint to create an organization",
"example": "org_q98uSGAYbjEwqs0mJwnz",
"maxLength": 64,
"minLength": 1
}
},
"additionalProperties": false
Expand Down
16 changes: 13 additions & 3 deletions crates/api_models/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ impl MerchantAccountCreate {
#[cfg(feature = "v2")]
#[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
#[serde(deny_unknown_fields)]
pub struct MerchantAccountCreate {
pub struct MerchantAccountCreateWithoutOrgId {
/// Name of the Merchant Account, This will be used as a prefix to generate the id
#[schema(value_type= String, max_length = 64, example = "NewAge Retailer")]
pub merchant_name: Secret<common_utils::new_type::MerchantName>,
Expand All @@ -189,9 +189,19 @@ pub struct MerchantAccountCreate {
/// Metadata is useful for storing additional, unstructured information about the merchant account.
#[schema(value_type = Option<Object>, example = r#"{ "city": "NY", "unit": "245" }"#)]
pub metadata: Option<pii::SecretSerdeValue>,
}

/// The id of the organization to which the merchant belongs to. Please use the organization endpoint to create an organization
#[schema(value_type = String, max_length = 64, min_length = 1, example = "org_q98uSGAYbjEwqs0mJwnz")]
// In v2 the struct used in the API is MerchantAccountCreateWithoutOrgId
// The following struct is only used internally, so we can reuse the common
// part of `create_merchant_account` without duplicating its code for v2
#[cfg(feature = "v2")]
#[derive(Clone, Debug, Serialize)]
pub struct MerchantAccountCreate {
pub merchant_name: Secret<common_utils::new_type::MerchantName>,
pub merchant_details: Option<MerchantDetails>,
pub metadata: Option<pii::SecretSerdeValue>,

// #[schema(value_type = String, max_length = 64, min_length = 1, example = "org_q98uSGAYbjEwqs0mJwnz")]
AnuthaDev marked this conversation as resolved.
Show resolved Hide resolved
pub organization_id: id_type::OrganizationId,
}

Expand Down
9 changes: 9 additions & 0 deletions crates/common_utils/src/id_type/organization.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::errors::{CustomResult, ValidationError};

crate::id_type!(
OrganizationId,
"A type for organization_id that can be used for organization ids"
Expand All @@ -13,3 +15,10 @@ crate::impl_generate_id_id_type!(OrganizationId, "org");
crate::impl_serializable_secret_id_type!(OrganizationId);
crate::impl_queryable_id_type!(OrganizationId);
crate::impl_to_sql_from_sql_id_type!(OrganizationId);

impl OrganizationId {
/// Get an organization id from String
pub fn wrap(org_id: String) -> CustomResult<Self, ValidationError> {
Self::try_from(std::borrow::Cow::from(org_id))
}
}
2 changes: 1 addition & 1 deletion crates/openapi/src/openapi_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ Never share your secret api keys. Keep them guarded and secure.
api_models::organization::OrganizationCreateRequest,
api_models::organization::OrganizationUpdateRequest,
api_models::organization::OrganizationResponse,
api_models::admin::MerchantAccountCreate,
api_models::admin::MerchantAccountCreateWithoutOrgId,
api_models::admin::MerchantAccountUpdate,
api_models::admin::MerchantAccountDeleteResponse,
api_models::admin::MerchantConnectorDeleteResponse,
Expand Down
3 changes: 1 addition & 2 deletions crates/openapi/src/routes/merchant_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,12 @@ pub async fn merchant_account_create() {}
post,
path = "/v2/merchant_accounts",
request_body(
content = MerchantAccountCreate,
content = MerchantAccountCreateWithoutOrgId,
AnuthaDev marked this conversation as resolved.
Show resolved Hide resolved
examples(
(
"Create a merchant account with minimal fields" = (
value = json!({
"merchant_name": "Cloth Store",
"organization_id": "org_abcdefghijklmnop"
})
)
),
Expand Down
1 change: 1 addition & 0 deletions crates/router/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub mod headers {
pub const X_API_VERSION: &str = "X-ApiVersion";
pub const X_FORWARDED_FOR: &str = "X-Forwarded-For";
pub const X_MERCHANT_ID: &str = "X-Merchant-Id";
pub const X_ORGANIZATION_ID: &str = "X-Organization-Id";
pub const X_LOGIN: &str = "X-Login";
pub const X_TRANS_KEY: &str = "X-Trans-Key";
pub const X_VERSION: &str = "X-Version";
Expand Down
38 changes: 37 additions & 1 deletion crates/router/src/routes/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ pub async fn organization_retrieve(
.await
}

#[cfg(feature = "olap")]
#[cfg(all(feature = "olap", feature = "v1"))]
#[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountCreate))]
pub async fn merchant_account_create(
state: web::Data<AppState>,
Expand All @@ -115,6 +115,42 @@ pub async fn merchant_account_create(
.await
}

#[cfg(all(feature = "olap", feature = "v2"))]
#[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountCreate))]
pub async fn merchant_account_create(
state: web::Data<AppState>,
req: HttpRequest,
json_payload: web::Json<api_models::admin::MerchantAccountCreateWithoutOrgId>,
) -> HttpResponse {
let flow = Flow::MerchantsAccountCreate;
let headers = req.headers();

let org_id = match auth::HeaderMapStruct::new(headers).get_organization_id_from_header() {
Ok(org_id) => org_id,
Err(e) => return api::log_and_return_error_response(e),
};

// Converting from MerchantAccountCreateWithoutOrgId to MerchantAccountCreate so we can use the existing
// `create_merchant_account` function for v2 as well
let new_request_payload_with_org_id = api_models::admin::MerchantAccountCreate {
merchant_name: json_payload.merchant_name.clone(),
merchant_details: json_payload.merchant_details.clone(),
metadata: json_payload.metadata.clone(),
organization_id: org_id,
};

Box::pin(api::server_wrap(
flow,
state,
&req,
new_request_payload_with_org_id,
|state, _, req, _| create_merchant_account(state, req),
&auth::AdminApiAuth,
api_locking::LockAction::NotApplicable,
))
.await
}

/// Merchant Account - Retrieve
///
/// Retrieve a merchant account details.
Expand Down
14 changes: 13 additions & 1 deletion crates/router/src/services/authentication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,7 @@ where
}

/// A helper struct to extract headers from the request
struct HeaderMapStruct<'a> {
pub(crate) struct HeaderMapStruct<'a> {
headers: &'a HeaderMap,
}

Expand Down Expand Up @@ -819,6 +819,18 @@ impl<'a> HeaderMapStruct<'a> {
)
})
}
#[cfg(feature = "v2")]
pub fn get_organization_id_from_header(&self) -> RouterResult<id_type::OrganizationId> {
self.get_mandatory_header_value_by_key(headers::X_ORGANIZATION_ID.into())
.map(|val| val.to_owned())
.and_then(|merchant_id| {
id_type::OrganizationId::wrap(merchant_id).change_context(
AnuthaDev marked this conversation as resolved.
Show resolved Hide resolved
errors::ApiErrorResponse::InvalidRequestData {
message: format!("`{}` header is invalid", headers::X_ORGANIZATION_ID),
},
)
})
}
}

/// Get the merchant-id from `x-merchant-id` header
Expand Down
Loading