Convert PaymentOption type into enum

This commit is contained in:
silverpill 2022-08-25 00:09:37 +00:00
parent 318d446dbd
commit 742e731b95
5 changed files with 93 additions and 77 deletions

View file

@ -15,8 +15,8 @@ use crate::frontend::get_subscription_page_url;
use crate::models::profiles::types::{
ExtraField,
IdentityProof,
PaymentLink,
PaymentOption,
PaymentType,
};
use super::types::ActorAttachment;
@ -67,10 +67,10 @@ pub fn attach_payment_option(
user_id: &Uuid,
payment_option: PaymentOption,
) -> ActorAttachment {
match payment_option.payment_type {
PaymentType::Link => unimplemented!(),
PaymentType::EthereumSubscription => {
let name = format!("{:?}", payment_option.payment_type);
match payment_option {
PaymentOption::Link(_) => unimplemented!(),
PaymentOption::EthereumSubscription => {
let name = "EthereumSubscription".to_string();
let subscription_page_url =
get_subscription_page_url(instance_url, user_id);
ActorAttachment {
@ -91,11 +91,13 @@ pub fn parse_payment_option(
if attachment.object_type != LINK {
return Err(ValidationError("invalid attachment type"));
};
let payment_option = PaymentOption {
payment_type: PaymentType::Link,
name: Some(attachment.name.clone()),
href: attachment.href.clone(),
};
let href = attachment.href.as_ref()
.ok_or(ValidationError("href attribute is required"))?
.to_string();
let payment_option = PaymentOption::Link(PaymentLink {
name: attachment.name.clone(),
href: href,
});
Ok(payment_option)
}
@ -153,7 +155,7 @@ mod tests {
#[test]
fn test_payment_option() {
let user_id = new_uuid();
let payment_option = PaymentOption::subscription();
let payment_option = PaymentOption::EthereumSubscription;
let subscription_page_url =
format!("https://example.com/profile/{}/subscription", user_id);
let attachment = attach_payment_option(
@ -166,8 +168,11 @@ mod tests {
assert_eq!(attachment.href.as_deref().unwrap(), subscription_page_url);
let parsed_option = parse_payment_option(&attachment).unwrap();
assert!(matches!(parsed_option.payment_type, PaymentType::Link));
assert_eq!(parsed_option.name.unwrap(), "EthereumSubscription");
assert_eq!(parsed_option.href.unwrap(), subscription_page_url);
let link = match parsed_option {
PaymentOption::Link(link) => link,
_ => panic!("wrong option"),
};
assert_eq!(link.name, "EthereumSubscription");
assert_eq!(link.href, subscription_page_url);
}
}

View file

@ -12,7 +12,6 @@ use crate::models::profiles::types::{
ExtraField,
IdentityProof,
PaymentOption,
PaymentType,
ProfileUpdateData,
};
use crate::models::profiles::validators::validate_username;
@ -97,9 +96,9 @@ impl Account {
let subscription_page_url = profile.payment_options.clone()
.into_inner().into_iter()
.map(|option| {
match option.payment_type {
PaymentType::Link => option.href.unwrap_or_default(),
PaymentType::EthereumSubscription => {
match option {
PaymentOption::Link(link) => link.href,
PaymentOption::EthereumSubscription => {
get_subscription_page_url(instance_url, &profile.id)
},
}

View file

@ -345,7 +345,7 @@ async fn subscriptions_enabled(
if current_user.profile.payment_options.is_empty() {
// Add payment option to profile
let mut profile_data = ProfileUpdateData::from(&current_user.profile);
profile_data.payment_options = vec![PaymentOption::subscription()];
profile_data.payment_options = vec![PaymentOption::EthereumSubscription];
current_user.profile = update_profile(
db_client,
&current_user.id,

View file

@ -5,6 +5,8 @@ use postgres_types::FromSql;
use serde::{
Deserialize, Deserializer, Serialize, Serializer,
de::Error as DeserializerError,
ser::SerializeMap,
__private::ser::FlatMapSerializer,
};
use uuid::Uuid;
@ -16,7 +18,6 @@ use crate::ethereum::identity::DidPkh;
use super::validators::{
validate_username,
validate_display_name,
validate_payment_options,
clean_bio,
clean_extra_fields,
};
@ -41,7 +42,6 @@ impl IdentityProofs {
json_from_sql!(IdentityProofs);
json_to_sql!(IdentityProofs);
#[derive(Clone, Debug)]
pub enum PaymentType {
Link,
EthereumSubscription,
@ -69,38 +69,58 @@ impl TryFrom<i16> for PaymentType {
}
}
impl Serialize for PaymentType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
let value: i16 = self.into();
serializer.serialize_i16(value)
}
}
impl<'de> Deserialize<'de> for PaymentType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>
{
let value: i16 = Deserialize::deserialize(deserializer)?;
Self::try_from(value).map_err(DeserializerError::custom)
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct PaymentOption {
pub payment_type: PaymentType,
pub name: Option<String>,
pub href: Option<String>,
pub struct PaymentLink {
pub name: String,
pub href: String,
}
impl PaymentOption {
pub fn subscription() -> Self {
Self {
payment_type: PaymentType::EthereumSubscription,
name: None,
href: None,
}
#[derive(Clone, Debug)]
pub enum PaymentOption {
Link(PaymentLink),
EthereumSubscription,
}
// Integer tags are not supported https://github.com/serde-rs/serde/issues/745
// Workaround: https://stackoverflow.com/a/65576570
impl<'de> Deserialize<'de> for PaymentOption {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>
{
let value = serde_json::Value::deserialize(deserializer)?;
let payment_type = value.get("payment_type")
.and_then(serde_json::Value::as_u64)
.and_then(|val| i16::try_from(val).ok())
.and_then(|val| PaymentType::try_from(val).ok())
.ok_or(DeserializerError::custom("invalid payment type"))?;
let payment_option = match payment_type {
PaymentType::Link => {
let link = PaymentLink::deserialize(value)
.map_err(DeserializerError::custom)?;
Self::Link(link)
},
PaymentType::EthereumSubscription => Self::EthereumSubscription,
};
Ok(payment_option)
}
}
impl Serialize for PaymentOption {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer,
{
let mut map = serializer.serialize_map(None)?;
let payment_type = match self {
Self::Link(_) => PaymentType::Link,
Self::EthereumSubscription => PaymentType::EthereumSubscription,
};
map.serialize_entry("payment_type", &i16::from(&payment_type))?;
match self {
Self::Link(link) => link.serialize(FlatMapSerializer(&mut map))?,
Self::EthereumSubscription => (),
};
map.end()
}
}
@ -268,7 +288,6 @@ impl ProfileCreateData {
let cleaned_bio = clean_bio(bio, self.actor_json.is_some())?;
self.bio = Some(cleaned_bio);
};
validate_payment_options(&self.payment_options)?;
self.extra_fields = clean_extra_fields(&self.extra_fields)?;
Ok(())
}
@ -296,7 +315,6 @@ impl ProfileUpdateData {
let cleaned_bio = clean_bio(bio, self.actor_json.is_some())?;
self.bio = Some(cleaned_bio);
};
validate_payment_options(&self.payment_options)?;
self.extra_fields = clean_extra_fields(&self.extra_fields)?;
Ok(())
}
@ -336,15 +354,29 @@ mod tests {
}
#[test]
fn test_payment_option_serialization() {
fn test_payment_option_link_serialization() {
let json_data = r#"{"payment_type":1,"name":"test","href":"https://test.com"}"#;
let payment_option: PaymentOption = serde_json::from_str(json_data).unwrap();
let link = match payment_option {
PaymentOption::Link(ref link) => link,
_ => panic!("wrong option"),
};
assert_eq!(link.name, "test");
assert_eq!(link.href, "https://test.com");
let serialized = serde_json::to_string(&payment_option).unwrap();
assert_eq!(serialized, json_data);
}
#[test]
fn test_payment_option_ethereum_subscription_serialization() {
let json_data = r#"{"payment_type":2,"name":null,"href":null}"#;
let payment_option: PaymentOption = serde_json::from_str(json_data).unwrap();
assert!(matches!(
payment_option.payment_type,
PaymentType::EthereumSubscription,
payment_option,
PaymentOption::EthereumSubscription,
));
let serialized = serde_json::to_string(&payment_option).unwrap();
assert_eq!(serialized, json_data);
assert_eq!(serialized, r#"{"payment_type":2}"#);
}
#[test]

View file

@ -1,7 +1,7 @@
use regex::Regex;
use crate::errors::ValidationError;
use crate::utils::html::{clean_html, clean_html_strict};
use super::types::{ExtraField, PaymentOption, PaymentType};
use super::types::ExtraField;
const USERNAME_RE: &str = r"^[a-zA-Z0-9_\.-]+$";
@ -46,26 +46,6 @@ pub fn clean_bio(bio: &str, is_remote: bool) -> Result<String, ValidationError>
Ok(cleaned_bio)
}
pub fn validate_payment_options(payment_options: &[PaymentOption])
-> Result<(), ValidationError>
{
for option in payment_options {
match option.payment_type {
PaymentType::Link => {
if option.name.is_none() || option.href.is_none() {
return Err(ValidationError("invalid payment option"));
};
},
PaymentType::EthereumSubscription => {
if option.name.is_some() || option.href.is_some() {
return Err(ValidationError("invalid payment option"));
};
},
};
};
Ok(())
}
const FIELD_NAME_MAX_SIZE: usize = 500;
const FIELD_VALUE_MAX_SIZE: usize = 5000;