mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +08:00
feat: add a domain type for customer_id (#4705)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -83,3 +83,9 @@ pub const DEFAULT_TTL_FOR_EXTENDED_CARD_INFO: u16 = 15 * 60;
|
||||
|
||||
/// Max ttl for Extended card info in redis (in seconds)
|
||||
pub const MAX_TTL_FOR_EXTENDED_CARD_INFO: u16 = 60 * 60 * 2;
|
||||
|
||||
/// Max Length for MerchantReferenceId
|
||||
pub const MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH: u8 = 64;
|
||||
|
||||
/// Minimum allowed length for MerchantReferenceId
|
||||
pub const MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH: u8 = 1;
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
use common_enums::{PaymentMethod, PaymentMethodType};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::id_type;
|
||||
|
||||
pub trait ApiEventMetric {
|
||||
fn get_api_event_type(&self) -> Option<ApiEventsType> {
|
||||
None
|
||||
@ -24,7 +26,7 @@ pub enum ApiEventsType {
|
||||
payment_method_type: Option<PaymentMethodType>,
|
||||
},
|
||||
Customer {
|
||||
customer_id: String,
|
||||
customer_id: id_type::CustomerId,
|
||||
},
|
||||
User {
|
||||
//specified merchant_id will overridden on global defined
|
||||
|
||||
299
crates/common_utils/src/id_type.rs
Normal file
299
crates/common_utils/src/id_type.rs
Normal file
@ -0,0 +1,299 @@
|
||||
//! Common ID types
|
||||
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fmt::{Debug, Display},
|
||||
};
|
||||
|
||||
mod customer;
|
||||
|
||||
pub use customer::CustomerId;
|
||||
use diesel::{
|
||||
backend::Backend,
|
||||
deserialize::FromSql,
|
||||
expression::AsExpression,
|
||||
serialize::{Output, ToSql},
|
||||
sql_types,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{fp_utils::when, generate_id_with_default_len};
|
||||
|
||||
/// This functions checks for the input string to contain valid characters
|
||||
/// Returns Some(char) if there are any invalid characters, else None
|
||||
fn get_invalid_input_character(input_string: Cow<'static, str>) -> Option<char> {
|
||||
input_string
|
||||
.trim()
|
||||
.chars()
|
||||
.find(|char| !char.is_ascii_alphanumeric() && !matches!(char, '_' | '-'))
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Clone, Eq)]
|
||||
/// A type for alphanumeric ids
|
||||
pub(crate) struct AlphaNumericId(String);
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Error, Eq, PartialEq)]
|
||||
#[error("value `{0}` contains invalid character `{1}`")]
|
||||
/// The error type for alphanumeric id
|
||||
pub(crate) struct AlphaNumericIdError(String, char);
|
||||
|
||||
impl<'de> Deserialize<'de> for AlphaNumericId {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let deserialized_string = String::deserialize(deserializer)?;
|
||||
Self::from(deserialized_string.into()).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl AlphaNumericId {
|
||||
/// Creates a new alphanumeric id from string by applying validation checks
|
||||
pub fn from(input_string: Cow<'static, str>) -> Result<Self, AlphaNumericIdError> {
|
||||
let invalid_character = get_invalid_input_character(input_string.clone());
|
||||
|
||||
if let Some(invalid_character) = invalid_character {
|
||||
Err(AlphaNumericIdError(
|
||||
input_string.to_string(),
|
||||
invalid_character,
|
||||
))?
|
||||
}
|
||||
|
||||
Ok(Self(input_string.to_string()))
|
||||
}
|
||||
|
||||
/// Create a new alphanumeric id without any validations
|
||||
pub(crate) fn new_unchecked(input_string: String) -> Self {
|
||||
Self(input_string)
|
||||
}
|
||||
|
||||
/// Generate a new alphanumeric id of default length
|
||||
pub(crate) fn new(prefix: &str) -> Self {
|
||||
Self(generate_id_with_default_len(prefix))
|
||||
}
|
||||
}
|
||||
|
||||
/// A common type of id that can be used for merchant reference ids
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, Eq, AsExpression)]
|
||||
#[diesel(sql_type = sql_types::Text)]
|
||||
pub(crate) struct MerchantReferenceId<const MAX_LENGTH: u8, const MIN_LENGTH: u8>(AlphaNumericId);
|
||||
|
||||
/// Error generated from violation of constraints for MerchantReferenceId
|
||||
#[derive(Debug, Deserialize, Serialize, Error, PartialEq, Eq)]
|
||||
pub(crate) enum MerchantReferenceIdError<const MAX_LENGTH: u8, const MIN_LENGTH: u8> {
|
||||
#[error("the maximum allowed length for this field is {MAX_LENGTH}")]
|
||||
/// Maximum length of string violated
|
||||
MaxLengthViolated,
|
||||
|
||||
#[error("the minimum required length for this field is {MIN_LENGTH}")]
|
||||
/// Minimum length of string violated
|
||||
MinLengthViolated,
|
||||
|
||||
#[error("{0}")]
|
||||
/// Input contains invalid characters
|
||||
AlphanumericIdError(AlphaNumericIdError),
|
||||
}
|
||||
|
||||
impl From<AlphaNumericIdError> for MerchantReferenceIdError<0, 0> {
|
||||
fn from(alphanumeric_id_error: AlphaNumericIdError) -> Self {
|
||||
Self::AlphanumericIdError(alphanumeric_id_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const MAX_LENGTH: u8, const MIN_LENGTH: u8> MerchantReferenceId<MAX_LENGTH, MIN_LENGTH> {
|
||||
/// Generates new [MerchantReferenceId] from the given input string
|
||||
pub fn from(
|
||||
input_string: Cow<'static, str>,
|
||||
) -> Result<Self, MerchantReferenceIdError<MAX_LENGTH, MIN_LENGTH>> {
|
||||
let trimmed_input_string = input_string.trim().to_string();
|
||||
let length_of_input_string = u8::try_from(trimmed_input_string.len())
|
||||
.map_err(|_| MerchantReferenceIdError::MaxLengthViolated)?;
|
||||
|
||||
when(length_of_input_string > MAX_LENGTH, || {
|
||||
Err(MerchantReferenceIdError::MaxLengthViolated)
|
||||
})?;
|
||||
|
||||
when(length_of_input_string < MIN_LENGTH, || {
|
||||
Err(MerchantReferenceIdError::MinLengthViolated)
|
||||
})?;
|
||||
|
||||
let alphanumeric_id = match AlphaNumericId::from(trimmed_input_string.into()) {
|
||||
Ok(valid_alphanumeric_id) => valid_alphanumeric_id,
|
||||
Err(error) => Err(MerchantReferenceIdError::AlphanumericIdError(error))?,
|
||||
};
|
||||
|
||||
Ok(Self(alphanumeric_id))
|
||||
}
|
||||
|
||||
/// Generate a new MerchantRefId of default length with the given prefix
|
||||
pub fn new(prefix: &str) -> Self {
|
||||
Self(AlphaNumericId::new(prefix))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, const MAX_LENGTH: u8, const MIN_LENGTH: u8> Deserialize<'de>
|
||||
for MerchantReferenceId<MAX_LENGTH, MIN_LENGTH>
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let deserialized_string = String::deserialize(deserializer)?;
|
||||
Self::from(deserialized_string.into()).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB, const MAX_LENGTH: u8, const MIN_LENGTH: u8> ToSql<sql_types::Text, DB>
|
||||
for MerchantReferenceId<MAX_LENGTH, MIN_LENGTH>
|
||||
where
|
||||
DB: Backend,
|
||||
String: ToSql<sql_types::Text, DB>,
|
||||
{
|
||||
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result {
|
||||
self.0 .0.to_sql(out)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const MAX_LENGTH: u8, const MIN_LENGTH: u8> Display
|
||||
for MerchantReferenceId<MAX_LENGTH, MIN_LENGTH>
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0 .0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB, const MAX_LENGTH: u8, const MIN_LENGTH: u8> FromSql<sql_types::Text, DB>
|
||||
for MerchantReferenceId<MAX_LENGTH, MIN_LENGTH>
|
||||
where
|
||||
DB: Backend,
|
||||
String: FromSql<sql_types::Text, DB>,
|
||||
{
|
||||
fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
|
||||
let string_val = String::from_sql(value)?;
|
||||
Ok(Self(AlphaNumericId::new_unchecked(string_val)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod alphanumeric_id_tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
use super::*;
|
||||
|
||||
const VALID_UNDERSCORE_ID_JSON: &str = r#""cus_abcdefghijklmnopqrstuv""#;
|
||||
const EXPECTED_VALID_UNDERSCORE_ID: &str = "cus_abcdefghijklmnopqrstuv";
|
||||
|
||||
const VALID_HYPHEN_ID_JSON: &str = r#""cus-abcdefghijklmnopqrstuv""#;
|
||||
const VALID_HYPHEN_ID_STRING: &str = "cus-abcdefghijklmnopqrstuv";
|
||||
|
||||
const INVALID_ID_WITH_SPACES: &str = r#""cus abcdefghijklmnopqrstuv""#;
|
||||
const INVALID_ID_WITH_EMOJIS: &str = r#""cus_abc🦀""#;
|
||||
|
||||
#[test]
|
||||
fn test_id_deserialize_underscore() {
|
||||
let parsed_alphanumeric_id =
|
||||
serde_json::from_str::<AlphaNumericId>(VALID_UNDERSCORE_ID_JSON);
|
||||
let alphanumeric_id = AlphaNumericId::from(EXPECTED_VALID_UNDERSCORE_ID.into()).unwrap();
|
||||
|
||||
assert_eq!(parsed_alphanumeric_id.unwrap(), alphanumeric_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_id_deserialize_hyphen() {
|
||||
let parsed_alphanumeric_id = serde_json::from_str::<AlphaNumericId>(VALID_HYPHEN_ID_JSON);
|
||||
let alphanumeric_id = AlphaNumericId::from(VALID_HYPHEN_ID_STRING.into()).unwrap();
|
||||
|
||||
assert_eq!(parsed_alphanumeric_id.unwrap(), alphanumeric_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_id_deserialize_with_spaces() {
|
||||
let parsed_alphanumeric_id = serde_json::from_str::<AlphaNumericId>(INVALID_ID_WITH_SPACES);
|
||||
|
||||
assert!(parsed_alphanumeric_id.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_id_deserialize_with_emojis() {
|
||||
let parsed_alphanumeric_id = serde_json::from_str::<AlphaNumericId>(INVALID_ID_WITH_EMOJIS);
|
||||
|
||||
assert!(parsed_alphanumeric_id.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod merchant_reference_id_tests {
|
||||
use super::*;
|
||||
|
||||
const VALID_REF_ID_JSON: &str = r#""cus_abcdefghijklmnopqrstuv""#;
|
||||
const MAX_LENGTH: u8 = 36;
|
||||
const MIN_LENGTH: u8 = 6;
|
||||
|
||||
const INVALID_REF_ID_JSON: &str = r#""cus abcdefghijklmnopqrstuv""#;
|
||||
const INVALID_REF_ID_LENGTH: &str = r#""cus_abcdefghijklmnopqrstuvwxyzabcdefghij""#;
|
||||
|
||||
#[test]
|
||||
fn test_valid_reference_id() {
|
||||
let parsed_merchant_reference_id =
|
||||
serde_json::from_str::<MerchantReferenceId<MAX_LENGTH, MIN_LENGTH>>(VALID_REF_ID_JSON);
|
||||
|
||||
dbg!(&parsed_merchant_reference_id);
|
||||
|
||||
assert!(parsed_merchant_reference_id.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_ref_id() {
|
||||
let parsed_merchant_reference_id = serde_json::from_str::<
|
||||
MerchantReferenceId<MAX_LENGTH, MIN_LENGTH>,
|
||||
>(INVALID_REF_ID_JSON);
|
||||
|
||||
assert!(parsed_merchant_reference_id.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_ref_id_error_message() {
|
||||
let parsed_merchant_reference_id = serde_json::from_str::<
|
||||
MerchantReferenceId<MAX_LENGTH, MIN_LENGTH>,
|
||||
>(INVALID_REF_ID_JSON);
|
||||
|
||||
let expected_error_message =
|
||||
r#"value `cus abcdefghijklmnopqrstuv` contains invalid character ` `"#.to_string();
|
||||
|
||||
let error_message = parsed_merchant_reference_id
|
||||
.err()
|
||||
.map(|error| error.to_string());
|
||||
|
||||
assert_eq!(error_message, Some(expected_error_message));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_ref_id_length() {
|
||||
let parsed_merchant_reference_id = serde_json::from_str::<
|
||||
MerchantReferenceId<MAX_LENGTH, MIN_LENGTH>,
|
||||
>(INVALID_REF_ID_LENGTH);
|
||||
|
||||
dbg!(&parsed_merchant_reference_id);
|
||||
|
||||
let expected_error_message =
|
||||
format!("the maximum allowed length for this field is {MAX_LENGTH}");
|
||||
|
||||
assert!(parsed_merchant_reference_id
|
||||
.is_err_and(|error_string| error_string.to_string().eq(&expected_error_message)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_ref_id_length_error_type() {
|
||||
let parsed_merchant_reference_id =
|
||||
MerchantReferenceId::<MAX_LENGTH, MIN_LENGTH>::from(INVALID_REF_ID_LENGTH.into());
|
||||
|
||||
dbg!(&parsed_merchant_reference_id);
|
||||
|
||||
assert!(
|
||||
parsed_merchant_reference_id.is_err_and(|error_type| matches!(
|
||||
error_type,
|
||||
MerchantReferenceIdError::MaxLengthViolated
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
111
crates/common_utils/src/id_type/customer.rs
Normal file
111
crates/common_utils/src/id_type/customer.rs
Normal file
@ -0,0 +1,111 @@
|
||||
use std::{borrow::Cow, fmt::Debug};
|
||||
|
||||
use diesel::{
|
||||
backend::Backend,
|
||||
deserialize::FromSql,
|
||||
expression::AsExpression,
|
||||
serialize::{Output, ToSql},
|
||||
sql_types, Queryable,
|
||||
};
|
||||
use error_stack::{Result, ResultExt};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
consts::{MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH, MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH},
|
||||
errors, generate_customer_id_of_default_length,
|
||||
id_type::MerchantReferenceId,
|
||||
};
|
||||
|
||||
/// A type for customer_id that can be used for customer ids
|
||||
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, AsExpression)]
|
||||
#[diesel(sql_type = sql_types::Text)]
|
||||
pub struct CustomerId(
|
||||
MerchantReferenceId<
|
||||
MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH,
|
||||
MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH,
|
||||
>,
|
||||
);
|
||||
|
||||
impl Default for CustomerId {
|
||||
fn default() -> Self {
|
||||
generate_customer_id_of_default_length()
|
||||
}
|
||||
}
|
||||
|
||||
/// This is to display the `CustomerId` as CustomerId(abcd)
|
||||
impl Debug for CustomerId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("CustomerId").field(&self.0 .0 .0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB> Queryable<sql_types::Text, DB> for CustomerId
|
||||
where
|
||||
DB: Backend,
|
||||
Self: FromSql<sql_types::Text, DB>,
|
||||
{
|
||||
type Row = Self;
|
||||
|
||||
fn build(row: Self::Row) -> diesel::deserialize::Result<Self> {
|
||||
Ok(row)
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomerId {
|
||||
pub(crate) fn new(
|
||||
merchant_ref_id: MerchantReferenceId<
|
||||
MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH,
|
||||
MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH,
|
||||
>,
|
||||
) -> Self {
|
||||
Self(merchant_ref_id)
|
||||
}
|
||||
|
||||
/// Get the string representation of customer id
|
||||
pub fn get_string_repr(&self) -> &str {
|
||||
&self.0 .0 .0
|
||||
}
|
||||
|
||||
/// Create a Customer id from string
|
||||
pub fn from(input_string: Cow<'static, str>) -> Result<Self, errors::ValidationError> {
|
||||
let merchant_ref_id = MerchantReferenceId::from(input_string).change_context(
|
||||
errors::ValidationError::IncorrectValueProvided {
|
||||
field_name: "customer_id",
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(Self(merchant_ref_id))
|
||||
}
|
||||
}
|
||||
|
||||
impl masking::SerializableSecret for CustomerId {}
|
||||
|
||||
impl<DB> ToSql<sql_types::Text, DB> for CustomerId
|
||||
where
|
||||
DB: Backend,
|
||||
MerchantReferenceId<
|
||||
MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH,
|
||||
MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH,
|
||||
>: ToSql<sql_types::Text, DB>,
|
||||
{
|
||||
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result {
|
||||
self.0.to_sql(out)
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB> FromSql<sql_types::Text, DB> for CustomerId
|
||||
where
|
||||
DB: Backend,
|
||||
MerchantReferenceId<
|
||||
MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH,
|
||||
MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH,
|
||||
>: FromSql<sql_types::Text, DB>,
|
||||
{
|
||||
fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
|
||||
MerchantReferenceId::<
|
||||
MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH,
|
||||
MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH,
|
||||
>::from_sql(value)
|
||||
.map(Self)
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,11 @@
|
||||
#![warn(missing_docs, missing_debug_implementations)]
|
||||
#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR" ), "/", "README.md"))]
|
||||
|
||||
use crate::{
|
||||
consts::ID_LENGTH,
|
||||
id_type::{CustomerId, MerchantReferenceId},
|
||||
};
|
||||
|
||||
pub mod access_token;
|
||||
pub mod consts;
|
||||
pub mod crypto;
|
||||
@ -11,6 +16,7 @@ pub mod errors;
|
||||
pub mod events;
|
||||
pub mod ext_traits;
|
||||
pub mod fp_utils;
|
||||
pub mod id_type;
|
||||
pub mod macros;
|
||||
pub mod pii;
|
||||
#[allow(missing_docs)] // Todo: add docs
|
||||
@ -193,10 +199,22 @@ pub fn generate_id(length: usize, prefix: &str) -> String {
|
||||
format!("{}_{}", prefix, nanoid::nanoid!(length, &consts::ALPHABETS))
|
||||
}
|
||||
|
||||
/// Generate a MerchantRefId with the default length
|
||||
fn generate_merchant_ref_id_with_default_length<const MAX_LENGTH: u8, const MIN_LENGTH: u8>(
|
||||
prefix: &str,
|
||||
) -> MerchantReferenceId<MAX_LENGTH, MIN_LENGTH> {
|
||||
MerchantReferenceId::<MAX_LENGTH, MIN_LENGTH>::new(prefix)
|
||||
}
|
||||
|
||||
/// Generate a customer id with default length
|
||||
pub fn generate_customer_id_of_default_length() -> CustomerId {
|
||||
CustomerId::new(generate_merchant_ref_id_with_default_length("cus"))
|
||||
}
|
||||
|
||||
/// Generate a nanoid with the given prefix and a default length
|
||||
#[inline]
|
||||
pub fn generate_id_with_default_len(prefix: &str) -> String {
|
||||
let len = consts::ID_LENGTH;
|
||||
let len = ID_LENGTH;
|
||||
format!("{}_{}", prefix, nanoid::nanoid!(len, &consts::ALPHABETS))
|
||||
}
|
||||
|
||||
@ -205,3 +223,31 @@ pub fn generate_id_with_default_len(prefix: &str) -> String {
|
||||
pub fn generate_time_ordered_id(prefix: &str) -> String {
|
||||
format!("{prefix}_{}", uuid::Uuid::now_v7().as_simple())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod nanoid_tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
use super::*;
|
||||
use crate::{
|
||||
consts::{
|
||||
MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH, MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH,
|
||||
},
|
||||
id_type::AlphaNumericId,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_generate_id_with_alphanumeric_id() {
|
||||
let alphanumeric_id = AlphaNumericId::from(generate_id(10, "def").into());
|
||||
assert!(alphanumeric_id.is_ok())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_merchant_ref_id_with_default_length() {
|
||||
let ref_id = MerchantReferenceId::<
|
||||
MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH,
|
||||
MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH,
|
||||
>::from(generate_id_with_default_len("def").into());
|
||||
|
||||
assert!(ref_id.is_ok())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user