mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat(documentation): add polymorphic generate_schema macro (#1183)
Co-authored-by: pixincreate@work <69745008+pixincreate@users.noreply.github.com>
This commit is contained in:
@ -321,7 +321,9 @@ impl From<StraightThroughAlgorithm> for StraightThroughAlgorithmSerde {
|
||||
#[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct PrimaryBusinessDetails {
|
||||
#[schema(value_type = CountryAlpha2)]
|
||||
pub country: api_enums::CountryAlpha2,
|
||||
#[schema(example = "food")]
|
||||
pub business: String,
|
||||
}
|
||||
|
||||
@ -452,10 +454,12 @@ pub struct MerchantConnectorCreate {
|
||||
pub frm_configs: Option<FrmConfigs>,
|
||||
|
||||
/// Business Country of the connector
|
||||
#[schema(value_type = CountryAlpha2, example = "US")]
|
||||
#[cfg(feature = "multiple_mca")]
|
||||
#[schema(value_type = CountryAlpha2, example = "US")]
|
||||
pub business_country: api_enums::CountryAlpha2,
|
||||
|
||||
#[cfg(not(feature = "multiple_mca"))]
|
||||
#[schema(value_type = Option<CountryAlpha2>, example = "US")]
|
||||
pub business_country: Option<api_enums::CountryAlpha2>,
|
||||
|
||||
///Business Type of the merchant
|
||||
@ -633,7 +637,11 @@ pub struct FrmConfigs {
|
||||
pub frm_enabled_pms: Option<Vec<String>>,
|
||||
pub frm_enabled_pm_types: Option<Vec<String>>,
|
||||
pub frm_enabled_gateways: Option<Vec<String>>,
|
||||
pub frm_action: api_enums::FrmAction, //What should be the action if FRM declines the txn (autorefund/cancel txn/manual review)
|
||||
/// What should be the action if FRM declines the txn (autorefund/cancel txn/manual review)
|
||||
#[schema(value_type = FrmAction)]
|
||||
pub frm_action: api_enums::FrmAction,
|
||||
/// Whether to make a call to the FRM before or after the payment
|
||||
#[schema(value_type = FrmPreferredFlowTypes)]
|
||||
pub frm_preferred_flow_type: api_enums::FrmPreferredFlowTypes,
|
||||
}
|
||||
/// Details of all the payment methods enabled for the connector for the given merchant account
|
||||
@ -657,7 +665,9 @@ pub struct PaymentMethodsEnabled {
|
||||
rename_all = "snake_case"
|
||||
)]
|
||||
pub enum AcceptedCurrencies {
|
||||
#[schema(value_type = Vec<Currency>)]
|
||||
EnableOnly(Vec<api_enums::Currency>),
|
||||
#[schema(value_type = Vec<Currency>)]
|
||||
DisableOnly(Vec<api_enums::Currency>),
|
||||
AllAccepted,
|
||||
}
|
||||
@ -670,7 +680,9 @@ pub enum AcceptedCurrencies {
|
||||
rename_all = "snake_case"
|
||||
)]
|
||||
pub enum AcceptedCountries {
|
||||
#[schema(value_type = Vec<CountryAlpha2>)]
|
||||
EnableOnly(Vec<api_enums::CountryAlpha2>),
|
||||
#[schema(value_type = Vec<CountryAlpha2>)]
|
||||
DisableOnly(Vec<api_enums::CountryAlpha2>),
|
||||
AllAccepted,
|
||||
}
|
||||
|
||||
@ -42,7 +42,16 @@ pub struct BankCodeResponse {
|
||||
pub eligible_connectors: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
|
||||
#[derive(
|
||||
Default,
|
||||
Debug,
|
||||
serde::Deserialize,
|
||||
serde::Serialize,
|
||||
Clone,
|
||||
ToSchema,
|
||||
router_derive::PolymorphicSchema,
|
||||
)]
|
||||
#[generate_schemas(PaymentsCreateRequest)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct PaymentsRequest {
|
||||
/// Unique identifier for the payment. This ensures idempotency for multiple payments
|
||||
@ -64,6 +73,8 @@ pub struct PaymentsRequest {
|
||||
/// The payment amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,
|
||||
#[schema(value_type = Option<u64>, example = 6540)]
|
||||
#[serde(default, deserialize_with = "amount::deserialize_option")]
|
||||
#[mandatory_in(PaymentsCreateRequest)]
|
||||
// Makes the field mandatory in PaymentsCreateRequest
|
||||
pub amount: Option<Amount>,
|
||||
|
||||
#[schema(value_type = Option<RoutingAlgorithm>, example = json!({
|
||||
@ -78,6 +89,7 @@ pub struct PaymentsRequest {
|
||||
|
||||
/// The currency of the payment request can be specified here
|
||||
#[schema(value_type = Option<Currency>, example = "USD")]
|
||||
#[mandatory_in(PaymentsCreateRequest)]
|
||||
pub currency: Option<api_enums::Currency>,
|
||||
|
||||
/// This is the instruction for capture/ debit the money from the users' card. On the other hand authorization refers to blocking the amount on the users' payment method.
|
||||
@ -205,7 +217,7 @@ pub struct PaymentsRequest {
|
||||
pub payment_method_type: Option<api_enums::PaymentMethodType>,
|
||||
|
||||
/// Business country of the merchant for this payment
|
||||
#[schema(value_type = CountryAlpha2, example = "US")]
|
||||
#[schema(value_type = Option<CountryAlpha2>, example = "US")]
|
||||
pub business_country: Option<api_enums::CountryAlpha2>,
|
||||
|
||||
/// Business label of the merchant for this payment
|
||||
@ -213,6 +225,7 @@ pub struct PaymentsRequest {
|
||||
pub business_label: Option<String>,
|
||||
|
||||
/// Merchant connector details used to make payments.
|
||||
#[schema(value_type = Option<MerchantConnectorDetailsWrap>)]
|
||||
pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
|
||||
|
||||
/// Allowed Payment Method Types for a given PaymentIntent
|
||||
@ -637,7 +650,13 @@ pub enum BankRedirectData {
|
||||
/// The billing details for bank redirection
|
||||
billing_details: BankRedirectBilling,
|
||||
/// Bank account details for Giropay
|
||||
|
||||
#[schema(value_type = Option<String>)]
|
||||
/// Bank account bic code
|
||||
bank_account_bic: Option<Secret<String>>,
|
||||
|
||||
/// Bank account iban
|
||||
#[schema(value_type = Option<String>)]
|
||||
bank_account_iban: Option<Secret<String>>,
|
||||
},
|
||||
Ideal {
|
||||
@ -653,26 +672,32 @@ pub enum BankRedirectData {
|
||||
#[schema(value_type = CountryAlpha2, example = "US")]
|
||||
country: api_enums::CountryAlpha2,
|
||||
|
||||
#[schema(value_type = String, example = "john.doe@example.com")]
|
||||
email: Email,
|
||||
},
|
||||
OnlineBankingCzechRepublic {
|
||||
// Issuer banks
|
||||
#[schema(value_type = BankNames)]
|
||||
issuer: api_enums::BankNames,
|
||||
},
|
||||
OnlineBankingFinland {
|
||||
// Shopper Email
|
||||
#[schema(value_type = Option<String>)]
|
||||
email: Option<Email>,
|
||||
},
|
||||
OnlineBankingPoland {
|
||||
// Issuer banks
|
||||
#[schema(value_type = BankNames)]
|
||||
issuer: api_enums::BankNames,
|
||||
},
|
||||
OnlineBankingSlovakia {
|
||||
// Issuer value corresponds to the bank
|
||||
#[schema(value_type = BankNames)]
|
||||
issuer: api_enums::BankNames,
|
||||
},
|
||||
Przelewy24 {
|
||||
//Issuer banks
|
||||
#[schema(value_type = Option<BankNames>)]
|
||||
bank_name: Option<api_enums::BankNames>,
|
||||
|
||||
// The billing details for bank redirect
|
||||
@ -796,6 +821,7 @@ pub struct MobilePayRedirection {}
|
||||
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
||||
pub struct MbWayRedirection {
|
||||
/// Telephone number of the shopper. Should be Portuguese phone number.
|
||||
#[schema(value_type = String)]
|
||||
pub telephone_number: Secret<String>,
|
||||
}
|
||||
|
||||
@ -1008,6 +1034,7 @@ pub struct PaymentsCaptureRequest {
|
||||
/// Concatenated with the statement descriptor suffix that’s set on the account to form the complete statement descriptor.
|
||||
pub statement_descriptor_prefix: Option<String>,
|
||||
/// Merchant connector details used to make payments.
|
||||
#[schema(value_type = Option<MerchantConnectorDetailsWrap>)]
|
||||
pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
|
||||
}
|
||||
|
||||
@ -1231,6 +1258,7 @@ pub struct PaymentsResponse {
|
||||
pub connector_label: Option<String>,
|
||||
|
||||
/// The business country of merchant for this payment
|
||||
#[schema(value_type = CountryAlpha2)]
|
||||
pub business_country: api_enums::CountryAlpha2,
|
||||
|
||||
/// The business label of merchant for this payment
|
||||
@ -1487,6 +1515,7 @@ pub struct PaymentsRetrieveRequest {
|
||||
/// The name of the connector
|
||||
pub connector: Option<String>,
|
||||
/// Merchant connector details used to make payments.
|
||||
#[schema(value_type = Option<MerchantConnectorDetailsWrap>)]
|
||||
pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
|
||||
}
|
||||
|
||||
@ -1510,7 +1539,9 @@ pub struct Metadata {
|
||||
#[schema(value_type = Object, example = r#"{ "city": "NY", "unit": "245" }"#)]
|
||||
#[serde(flatten)]
|
||||
pub data: pii::SecretSerdeValue,
|
||||
|
||||
/// Payload coming in request as a metadata field
|
||||
#[schema(value_type = Option<Object>)]
|
||||
pub payload: Option<pii::SecretSerdeValue>,
|
||||
|
||||
/// Allowed payment method types for a payment intent
|
||||
@ -1528,6 +1559,7 @@ pub struct PaymentsSessionRequest {
|
||||
#[schema(value_type = Vec<PaymentMethodType>)]
|
||||
pub wallets: Vec<api_enums::PaymentMethodType>,
|
||||
/// Merchant connector details used to make payments.
|
||||
#[schema(value_type = Option<MerchantConnectorDetailsWrap>)]
|
||||
pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
|
||||
}
|
||||
|
||||
@ -1796,6 +1828,7 @@ pub struct PaymentsCancelRequest {
|
||||
/// The reason for the payment cancel
|
||||
pub cancellation_reason: Option<String>,
|
||||
/// Merchant connector details used to make payments.
|
||||
#[schema(value_type = MerchantConnectorDetailsWrap)]
|
||||
pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
|
||||
}
|
||||
|
||||
|
||||
@ -45,6 +45,7 @@ pub struct RefundRequest {
|
||||
pub metadata: Option<pii::SecretSerdeValue>,
|
||||
|
||||
/// Merchant connector details used to make payments.
|
||||
#[schema(value_type = Option<MerchantConnectorDetailsWrap>)]
|
||||
pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
|
||||
}
|
||||
|
||||
|
||||
@ -205,6 +205,7 @@ pub struct CurrencyCountryFlowFilter {
|
||||
pub country: Option<HashSet<api_models::enums::CountryAlpha2>>,
|
||||
pub not_available_flows: Option<NotAvailableFlows>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Copy, Clone, Default)]
|
||||
#[serde(default)]
|
||||
pub struct NotAvailableFlows {
|
||||
|
||||
@ -29,7 +29,7 @@ Use the following base URLs when making requests to the APIs:
|
||||
| Environment | Base URL |
|
||||
|---------------|------------------------------------|
|
||||
| Sandbox | <https://sandbox.hyperswitch.io> |
|
||||
| Production | Coming Soon! |
|
||||
| Production | <https://api.hyperswitch.io> |
|
||||
|
||||
## Authentication
|
||||
|
||||
@ -38,10 +38,10 @@ account, you are given a secret key (also referred as api-key) and a publishable
|
||||
You may authenticate all API requests with Hyperswitch server by providing the appropriate key in
|
||||
the request Authorization header.
|
||||
|
||||
| Key | Description |
|
||||
|---------------|-----------------------------------------------------------------------------------------------|
|
||||
| Sandbox | Private key. Used to authenticate all API requests from your merchant server |
|
||||
| Production | Unique identifier for your account. Used to authenticate API requests from your app's client |
|
||||
| Key | Description |
|
||||
|-----------------|-----------------------------------------------------------------------------------------------|
|
||||
| api-key | Private key. Used to authenticate all API requests from your merchant server |
|
||||
| publishable key | Unique identifier for your account. Used to authenticate API requests from your app's client |
|
||||
|
||||
Never share your secret api keys. Keep them guarded and secure.
|
||||
"#,
|
||||
@ -65,15 +65,16 @@ Never share your secret api keys. Keep them guarded and secure.
|
||||
crate::routes::refunds::refunds_retrieve,
|
||||
crate::routes::refunds::refunds_update,
|
||||
crate::routes::refunds::refunds_list,
|
||||
crate::routes::admin::merchant_account_create,
|
||||
crate::routes::admin::retrieve_merchant_account,
|
||||
crate::routes::admin::update_merchant_account,
|
||||
crate::routes::admin::delete_merchant_account,
|
||||
crate::routes::admin::payment_connector_create,
|
||||
crate::routes::admin::payment_connector_retrieve,
|
||||
crate::routes::admin::payment_connector_list,
|
||||
crate::routes::admin::payment_connector_update,
|
||||
crate::routes::admin::payment_connector_delete,
|
||||
// Commenting this out as these are admin apis and not to be used by the merchant
|
||||
// crate::routes::admin::merchant_account_create,
|
||||
// crate::routes::admin::retrieve_merchant_account,
|
||||
// crate::routes::admin::update_merchant_account,
|
||||
// crate::routes::admin::delete_merchant_account,
|
||||
// crate::routes::admin::payment_connector_create,
|
||||
// crate::routes::admin::payment_connector_retrieve,
|
||||
// crate::routes::admin::payment_connector_list,
|
||||
// crate::routes::admin::payment_connector_update,
|
||||
// crate::routes::admin::payment_connector_delete,
|
||||
crate::routes::mandates::get_mandate,
|
||||
crate::routes::mandates::revoke_mandate,
|
||||
crate::routes::payments::payments_create,
|
||||
@ -114,6 +115,7 @@ Never share your secret api keys. Keep them guarded and secure.
|
||||
crate::types::api::admin::MerchantAccountUpdate,
|
||||
crate::types::api::admin::MerchantAccountDeleteResponse,
|
||||
crate::types::api::admin::MerchantConnectorDeleteResponse,
|
||||
crate::types::api::admin::MerchantConnectorResponse,
|
||||
crate::types::api::customers::CustomerRequest,
|
||||
crate::types::api::customers::CustomerDeleteResponse,
|
||||
crate::types::api::payment_methods::PaymentMethodCreate,
|
||||
@ -148,10 +150,25 @@ Never share your secret api keys. Keep them guarded and secure.
|
||||
api_models::enums::DisputeStage,
|
||||
api_models::enums::DisputeStatus,
|
||||
api_models::enums::CountryAlpha2,
|
||||
api_models::enums::FrmAction,
|
||||
api_models::enums::FrmPreferredFlowTypes,
|
||||
api_models::admin::MerchantConnectorCreate,
|
||||
api_models::admin::MerchantConnectorUpdate,
|
||||
api_models::admin::PrimaryBusinessDetails,
|
||||
api_models::admin::FrmConfigs,
|
||||
api_models::admin::PaymentMethodsEnabled,
|
||||
api_models::admin::MerchantConnectorDetailsWrap,
|
||||
api_models::admin::MerchantConnectorDetails,
|
||||
api_models::disputes::DisputeResponse,
|
||||
api_models::disputes::DisputeResponsePaymentsRetrieve,
|
||||
api_models::payments::AddressDetails,
|
||||
api_models::payments::BankDebitData,
|
||||
api_models::payments::AliPayRedirection,
|
||||
api_models::payments::MbWayRedirection,
|
||||
api_models::payments::MobilePayRedirection,
|
||||
api_models::payments::WeChatPayRedirection,
|
||||
api_models::payments::BankDebitBilling,
|
||||
api_models::payments::CryptoData,
|
||||
api_models::payments::Address,
|
||||
api_models::payments::BankRedirectData,
|
||||
api_models::payments::BankRedirectBilling,
|
||||
@ -172,6 +189,7 @@ Never share your secret api keys. Keep them guarded and secure.
|
||||
api_models::payments::Card,
|
||||
api_models::payments::CustomerAcceptance,
|
||||
api_models::payments::PaymentsRequest,
|
||||
api_models::payments::PaymentsCreateRequest,
|
||||
api_models::payments::PaymentsResponse,
|
||||
api_models::payments::PaymentsStartRequest,
|
||||
api_models::payments::PaymentRetrieveBody,
|
||||
|
||||
@ -158,9 +158,9 @@ pub async fn delete_merchant_account(
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/accounts/{account_id}/connectors",
|
||||
request_body = MerchantConnector,
|
||||
request_body = MerchantConnectorCreate,
|
||||
responses(
|
||||
(status = 200, description = "Merchant Connector Created", body = MerchantConnector),
|
||||
(status = 200, description = "Merchant Connector Created", body = MerchantConnectorResponse),
|
||||
(status = 400, description = "Missing Mandatory fields"),
|
||||
),
|
||||
tag = "Merchant Connector Account",
|
||||
@ -198,7 +198,7 @@ pub async fn payment_connector_create(
|
||||
("connector_id" = i32, Path, description = "The unique identifier for the Merchant Connector")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Merchant Connector retrieved successfully", body = MerchantConnector),
|
||||
(status = 200, description = "Merchant Connector retrieved successfully", body = MerchantConnectorResponse),
|
||||
(status = 404, description = "Merchant Connector does not exist in records"),
|
||||
(status = 401, description = "Unauthorized request")
|
||||
),
|
||||
@ -242,7 +242,7 @@ pub async fn payment_connector_retrieve(
|
||||
("account_id" = String, Path, description = "The unique identifier for the merchant account"),
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Merchant Connector list retrieved successfully", body = Vec<MerchantConnector>),
|
||||
(status = 200, description = "Merchant Connector list retrieved successfully", body = Vec<MerchantConnectorResponse>),
|
||||
(status = 404, description = "Merchant Connector does not exist in records"),
|
||||
(status = 401, description = "Unauthorized request")
|
||||
),
|
||||
@ -275,13 +275,13 @@ pub async fn payment_connector_list(
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/accounts/{account_id}/connectors/{connector_id}",
|
||||
request_body = MerchantConnector,
|
||||
request_body = MerchantConnectorUpdate,
|
||||
params(
|
||||
("account_id" = String, Path, description = "The unique identifier for the merchant account"),
|
||||
("connector_id" = i32, Path, description = "The unique identifier for the Merchant Connector")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Merchant Connector Updated", body = MerchantConnector),
|
||||
(status = 200, description = "Merchant Connector Updated", body = MerchantConnectorResponse),
|
||||
(status = 404, description = "Merchant Connector does not exist in records"),
|
||||
(status = 401, description = "Unauthorized request")
|
||||
),
|
||||
|
||||
@ -11,7 +11,7 @@ use crate::{
|
||||
types::api::disputes as dispute_types,
|
||||
};
|
||||
|
||||
/// Diputes - Retrieve Dispute
|
||||
/// Disputes - Retrieve Dispute
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/disputes/{dispute_id}",
|
||||
@ -47,7 +47,7 @@ pub async fn retrieve_dispute(
|
||||
.await
|
||||
}
|
||||
|
||||
/// Diputes - List Disputes
|
||||
/// Disputes - List Disputes
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/disputes/list",
|
||||
@ -90,7 +90,7 @@ pub async fn retrieve_disputes_list(
|
||||
.await
|
||||
}
|
||||
|
||||
/// Diputes - Accept Dispute
|
||||
/// Disputes - Accept Dispute
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/disputes/accept/{dispute_id}",
|
||||
@ -126,7 +126,7 @@ pub async fn accept_dispute(
|
||||
.await
|
||||
}
|
||||
|
||||
/// Diputes - Submit Dispute Evidence
|
||||
/// Disputes - Submit Dispute Evidence
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/disputes/evidence",
|
||||
|
||||
@ -48,7 +48,7 @@ pub async fn create_payment_method_api(
|
||||
/// To filter and list the applicable payment methods for a particular Merchant ID
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/payment_methods/{account_id}",
|
||||
path = "/account/payment_methods",
|
||||
params (
|
||||
("account_id" = String, Path, description = "The unique identifier for the merchant account"),
|
||||
("accepted_country" = Vec<String>, Query, description = "The two-letter ISO currency code"),
|
||||
@ -96,7 +96,7 @@ pub async fn list_payment_method_api(
|
||||
/// To filter and list the applicable payment methods for a particular Customer ID
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/payment_methods/{customer_id}",
|
||||
path = "/customer/{customer_id}/payment_methods",
|
||||
params (
|
||||
("customer_id" = String, Path, description = "The unique identifier for the customer account"),
|
||||
("accepted_country" = Vec<String>, Query, description = "The two-letter ISO currency code"),
|
||||
|
||||
@ -18,7 +18,7 @@ use crate::{
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/payments",
|
||||
request_body=PaymentsRequest,
|
||||
request_body=PaymentsCreateRequest,
|
||||
responses(
|
||||
(status = 200, description = "Payment created", body = PaymentsResponse),
|
||||
(status = 400, description = "Missing Mandatory fields")
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
pub use api_models::admin::{
|
||||
MerchantAccountCreate, MerchantAccountDeleteResponse, MerchantAccountResponse,
|
||||
MerchantAccountUpdate, MerchantConnectorCreate, MerchantConnectorDeleteResponse,
|
||||
MerchantConnectorDetails, MerchantConnectorDetailsWrap, MerchantConnectorId, MerchantDetails,
|
||||
MerchantId, PaymentMethodsEnabled, RoutingAlgorithm, StraightThroughAlgorithm, ToggleKVRequest,
|
||||
ToggleKVResponse, WebhookDetails,
|
||||
MerchantConnectorDetails, MerchantConnectorDetailsWrap, MerchantConnectorId,
|
||||
MerchantConnectorResponse, MerchantDetails, MerchantId, PaymentMethodsEnabled,
|
||||
RoutingAlgorithm, StraightThroughAlgorithm, ToggleKVRequest, ToggleKVResponse, WebhookDetails,
|
||||
};
|
||||
use common_utils::ext_traits::ValueExt;
|
||||
|
||||
|
||||
@ -462,3 +462,50 @@ pub fn operation_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
|
||||
let input = syn::parse_macro_input!(input as syn::DeriveInput);
|
||||
macros::operation_derive_inner(input).unwrap_or_else(|err| err.to_compile_error().into())
|
||||
}
|
||||
|
||||
/// Generates different schemas with the ability to mark few fields as mandatory for certain schema
|
||||
/// Usage
|
||||
/// ```
|
||||
/// #[derive(PolymorphicSchema)]
|
||||
/// #[generate_schemas(PaymentsCreateRequest, PaymentsConfirmRequest)]
|
||||
/// struct PaymentsRequest {
|
||||
/// #[mandatory_in(PaymentsCreateRequest)]
|
||||
/// amount: Option<u64>,
|
||||
/// #[mandatory_in(PaymentsCreateRequest)]
|
||||
/// currency: Option<String>,
|
||||
/// payment_method: String,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This will create two structs `PaymentsCreateRequest` and `PaymentsConfirmRequest` as follows
|
||||
/// It will retain all the other attributes that are used in the original struct, and only consume
|
||||
/// the #[mandatory_in] attribute to generate schemas
|
||||
///
|
||||
/// ```
|
||||
/// #[derive(utoipa::ToSchema)]
|
||||
/// struct PaymentsCreateRequest {
|
||||
/// #[schema(required = true)]
|
||||
/// amount: Option<u64>,
|
||||
///
|
||||
/// #[schema(required = true)]
|
||||
/// currency: Option<String>,
|
||||
///
|
||||
/// payment_method: String,
|
||||
/// }
|
||||
///
|
||||
/// #[derive(utoipa::ToSchema)]
|
||||
/// struct PaymentsConfirmRequest {
|
||||
/// amount: Option<u64>,
|
||||
/// currency: Option<String>,
|
||||
/// payment_method: String,
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
#[proc_macro_derive(PolymorphicSchema, attributes(mandatory_in, generate_schemas))]
|
||||
pub fn polymorphic_schema(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = syn::parse_macro_input!(input as syn::DeriveInput);
|
||||
|
||||
macros::polymorphic_macro_derive_inner(input)
|
||||
.unwrap_or_else(|error| error.into_compile_error())
|
||||
.into()
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
pub(crate) mod api_error;
|
||||
pub(crate) mod diesel;
|
||||
mod helpers;
|
||||
pub(crate) mod generate_schema;
|
||||
pub(crate) mod operation;
|
||||
|
||||
mod helpers;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::DeriveInput;
|
||||
@ -12,6 +14,7 @@ pub(crate) use self::{
|
||||
diesel::{
|
||||
diesel_enum_attribute_inner, diesel_enum_derive_inner, diesel_enum_text_derive_inner,
|
||||
},
|
||||
generate_schema::polymorphic_macro_derive_inner,
|
||||
operation::operation_derive_inner,
|
||||
};
|
||||
|
||||
|
||||
140
crates/router_derive/src/macros/generate_schema.rs
Normal file
140
crates/router_derive/src/macros/generate_schema.rs
Normal file
@ -0,0 +1,140 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use syn::{self, parse_quote, punctuated::Punctuated, Token};
|
||||
|
||||
use crate::macros::helpers;
|
||||
|
||||
/// Parse schemas from attribute
|
||||
/// Example
|
||||
///
|
||||
/// #[mandatory_in(PaymentsCreateRequest, PaymentsUpdateRequest)]
|
||||
/// would return
|
||||
///
|
||||
/// [PaymentsCreateRequest, PaymentsUpdateRequest]
|
||||
fn get_inner_path_ident(attribute: &syn::Attribute) -> syn::Result<Vec<syn::Ident>> {
|
||||
Ok(attribute
|
||||
.parse_args_with(Punctuated::<syn::Ident, Token![,]>::parse_terminated)?
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
fn get_struct_fields(data: syn::Data) -> syn::Result<Punctuated<syn::Field, syn::token::Comma>> {
|
||||
if let syn::Data::Struct(syn::DataStruct {
|
||||
fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
|
||||
..
|
||||
}) = data
|
||||
{
|
||||
Ok(named.to_owned())
|
||||
} else {
|
||||
Err(syn::Error::new(
|
||||
proc_macro2::Span::call_site(),
|
||||
"This macro cannot be used on structs with no fields",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn polymorphic_macro_derive_inner(
|
||||
input: syn::DeriveInput,
|
||||
) -> syn::Result<proc_macro2::TokenStream> {
|
||||
let schemas_to_create =
|
||||
helpers::get_metadata_inner::<syn::Ident>("generate_schemas", &input.attrs)?;
|
||||
|
||||
let fields = get_struct_fields(input.data)
|
||||
.map_err(|error| syn::Error::new(proc_macro2::Span::call_site(), error))?;
|
||||
|
||||
// Go through all the fields and create a mapping of required fields for a schema
|
||||
// PaymentsCreate -> ["amount","currency"]
|
||||
// This will be stored in a hashset
|
||||
// mandatory_hashset -> ((PaymentsCreate, amount), (PaymentsCreate,currency))
|
||||
let mut mandatory_hashset = HashSet::<(syn::Ident, syn::Ident)>::new();
|
||||
let mut other_fields_hm = HashMap::<syn::Field, Vec<syn::Attribute>>::new();
|
||||
|
||||
fields.iter().for_each(|field| {
|
||||
// Partition the attributes of a field into two vectors
|
||||
// One with #[mandatory_in] attributes present
|
||||
// Rest of the attributes ( include only the schema attribute, serde is not required)
|
||||
let (mandatory_attribute, other_attributes) = field
|
||||
.attrs
|
||||
.iter()
|
||||
.partition::<Vec<_>, _>(|attribute| attribute.path.is_ident("mandatory_in"));
|
||||
|
||||
// Other attributes ( schema ) are to be printed as is
|
||||
other_attributes
|
||||
.iter()
|
||||
.filter(|attribute| attribute.path.is_ident("schema") || attribute.path.is_ident("doc"))
|
||||
.for_each(|attribute| {
|
||||
// Since attributes will be modified, the field should not contain any attributes
|
||||
// So create a field, with previous attributes removed
|
||||
let mut field_without_attributes = field.clone();
|
||||
field_without_attributes.attrs.clear();
|
||||
|
||||
other_fields_hm
|
||||
.entry(field_without_attributes.to_owned())
|
||||
.or_insert(vec![])
|
||||
.push(attribute.to_owned().to_owned());
|
||||
});
|
||||
|
||||
// Mandatory attributes are to be inserted into hashset
|
||||
// The hashset will store it in this format
|
||||
// (PaymentsCreateRequest, "amount")
|
||||
// (PaymentsConfirmRequest, "currency")
|
||||
//
|
||||
// For these attributes, we need to later add #[schema(required = true)] attribute
|
||||
_ = mandatory_attribute
|
||||
.iter()
|
||||
// Filter only #[mandatory_in] attributes
|
||||
.map(|&attribute| get_inner_path_ident(attribute))
|
||||
.try_for_each(|schemas| {
|
||||
let res = schemas
|
||||
.map_err(|error| syn::Error::new(proc_macro2::Span::call_site(), error))?
|
||||
.iter()
|
||||
.filter_map(|schema| field.ident.to_owned().zip(Some(schema.to_owned())))
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
mandatory_hashset.extend(res);
|
||||
Ok::<_, syn::Error>(())
|
||||
});
|
||||
});
|
||||
|
||||
let schemas = schemas_to_create
|
||||
.iter()
|
||||
.map(|schema| {
|
||||
let fields = other_fields_hm
|
||||
.iter()
|
||||
.flat_map(|(field, value)| {
|
||||
let mut attributes = value
|
||||
.iter()
|
||||
.map(|attribute| quote::quote!(#attribute))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// If the field is required for this schema, then add
|
||||
// #[schema(required = true)] for this field
|
||||
let required_attribute: syn::Attribute =
|
||||
parse_quote!(#[schema(required = true)]);
|
||||
|
||||
// Can be none, because tuple fields have no ident
|
||||
field.ident.to_owned().and_then(|field_ident| {
|
||||
mandatory_hashset
|
||||
.contains(&(field_ident, schema.to_owned()))
|
||||
.then(|| attributes.push(quote::quote!(#required_attribute)))
|
||||
});
|
||||
|
||||
quote::quote! {
|
||||
#(#attributes)*
|
||||
#field,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
quote::quote! {
|
||||
#[derive(utoipa::ToSchema)]
|
||||
pub struct #schema {
|
||||
#(#fields)*
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(quote::quote! {
|
||||
#(#schemas)*
|
||||
})
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user