Skip to content

Commit

Permalink
Merge pull request #18 from lightsparkdev/feat/umainvites
Browse files Browse the repository at this point in the history
Add UMA invitation functions to the SDK.
  • Loading branch information
jklein24 authored Nov 21, 2023
2 parents 3c52f91 + a242402 commit 23ae9f5
Show file tree
Hide file tree
Showing 28 changed files with 1,881 additions and 41 deletions.
225 changes: 224 additions & 1 deletion lightspark/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ use crate::objects::invoice_type::InvoiceType;
use crate::objects::lightning_fee_estimate_output::LightningFeeEstimateOutput;
use crate::objects::outgoing_payment::OutgoingPayment;
use crate::objects::permission::Permission;
use crate::objects::region_code::RegionCode;
use crate::objects::risk_rating::RiskRating;
use crate::objects::uma_invitation::UmaInvitation;
use crate::objects::withdrawal_mode::WithdrawalMode;
use crate::objects::withdrawal_request::WithdrawalRequest;
use crate::objects::{account, invoice_data};
use crate::objects::{account, invoice_data, uma_invitation};
use crate::objects::{api_token, incoming_payment, outgoing_payment};
use crate::objects::{bitcoin_network, withdrawal_request};
use crate::objects::{fee_estimate, lightning_fee_estimate_output};
Expand Down Expand Up @@ -965,4 +967,225 @@ impl<K: OperationSigningKey> LightsparkClient<K> {
.map_err(Error::JsonError)?;
Ok(result)
}

/// Creates an UMA invitation. If you are part of the incentive program, you should use
/// `create_uma_invitation_with_incentives`.
pub async fn create_uma_invitation(&self, inviter_uma: &str) -> Result<UmaInvitation, Error> {
let operation = format!(
"mutation CreateUmaInvitation(
$inviter_uma: String!
) {{
create_uma_invitation(input: {{
inviter_uma: $inviter_uma
}}) {{
invitation {{
...UmaInvitationFragment
}}
}}
}}
{}
",
uma_invitation::FRAGMENT
);

let mut variables: HashMap<&str, Value> = HashMap::new();
variables.insert("inviter_uma", inviter_uma.into());

let value = serde_json::to_value(variables).map_err(Error::ConversionError)?;
let json = self
.requester
.execute_graphql(&operation, Some(value))
.await?;

let result = serde_json::from_value(json["create_uma_invitation"]["invitation"].clone())
.map_err(Error::JsonError)?;
Ok(result)
}

/// Creates an UMA invitation as part of the incentive program. If you are not part of the
/// incentive program, you should use `create_uma_invitation`.
pub async fn create_uma_invitation_with_incentives(
&self,
inviter_uma: &str,
inviter_phone_number_e164: &str,
inviter_region: RegionCode,
) -> Result<UmaInvitation, Error> {
let operation = format!(
"mutation CreateUmaInvitationWithIncentives(
$inviter_uma: String!
$inviter_phone_hash: String!
$inviter_region: RegionCode!
) {{
create_uma_invitation_with_incentives(input: {{
inviter_uma: $inviter_uma
inviter_phone_hash: $inviter_phone_hash
inviter_region: $inviter_region
}}) {{
invitation {{
...UmaInvitationFragment
}}
}}
}}
{}
",
uma_invitation::FRAGMENT
);

let mut variables: HashMap<&str, Value> = HashMap::new();
variables.insert("inviter_uma", inviter_uma.into());
let inviter_phone_hash = Self::hash_phone_number(inviter_phone_number_e164)?;
variables.insert("inviter_phone_hash", inviter_phone_hash.into());
variables.insert("inviter_region", inviter_region.into());

let value = serde_json::to_value(variables).map_err(Error::ConversionError)?;
let json = self
.requester
.execute_graphql(&operation, Some(value))
.await?;

let result = serde_json::from_value(
json["create_uma_invitation_with_incentives"]["invitation"].clone(),
)
.map_err(Error::JsonError)?;
Ok(result)
}

/// Claims an UMA invitation. If you are part of the incentive program, you should use
/// `claim_uma_invitation_with_incentives`.
pub async fn claim_uma_invitation(
&self,
invitation_code: &str,
invitee_uma: &str,
) -> Result<UmaInvitation, Error> {
let operation = format!(
"mutation ClaimUmaInvitation(
$invitation_code: String!
$invitee_uma: String!
) {{
claim_uma_invitation(input: {{
invitation_code: $invitation_code
invitee_uma: $invitee_uma
}}) {{
invitation {{
...UmaInvitationFragment
}}
}}
}}
{}
",
uma_invitation::FRAGMENT
);

let mut variables: HashMap<&str, Value> = HashMap::new();
variables.insert("invitation_code", invitation_code.into());
variables.insert("invitee_uma", invitee_uma.into());

let value = serde_json::to_value(variables).map_err(Error::ConversionError)?;
let json = self
.requester
.execute_graphql(&operation, Some(value))
.await?;

let result = serde_json::from_value(json["claim_uma_invitation"]["invitation"].clone())
.map_err(Error::JsonError)?;
Ok(result)
}

/// Claims an UMA invitation as part of the incentive program. If you are not part of the
/// incentive program, you should use `claim_uma_invitation`.
pub async fn claim_uma_invitation_with_incentives(
&self,
invitation_code: &str,
invitee_uma: &str,
invitee_phone_number_e164: &str,
invitee_region: RegionCode,
) -> Result<UmaInvitation, Error> {
let operation = format!(
"mutation ClaimUmaInvitation(
$invitation_code: String!
$invitee_uma: String!
$invitee_phone_hash: String!
$invitee_region: RegionCode!
) {{
claim_uma_invitation_with_incentives(input: {{
invitation_code: $invitation_code
invitee_uma: $invitee_uma
invitee_phone_hash: $invitee_phone_hash
invitee_region: $invitee_region
}}) {{
invitation {{
...UmaInvitationFragment
}}
}}
}}
{}
",
uma_invitation::FRAGMENT
);

let mut variables: HashMap<&str, Value> = HashMap::new();
variables.insert("invitation_code", invitation_code.into());
variables.insert("invitee_uma", invitee_uma.into());
let invitee_phone_hash = Self::hash_phone_number(invitee_phone_number_e164)?;
variables.insert("invitee_phone_hash", invitee_phone_hash.into());
variables.insert("invitee_region", invitee_region.into());

let value = serde_json::to_value(variables).map_err(Error::ConversionError)?;
let json = self
.requester
.execute_graphql(&operation, Some(value))
.await?;

let result = serde_json::from_value(
json["claim_uma_invitation_with_incentives"]["invitation"].clone(),
)
.map_err(Error::JsonError)?;
Ok(result)
}

/// Fetches a UMA invitation by its code.
pub async fn fetch_uma_invitation(
&self,
invitation_code: &str,
) -> Result<UmaInvitation, Error> {
let operation = format!(
"query FetchUmaInvitation(
$invitation_code: String!
) {{
uma_invitation_by_code(code: $invitation_code) {{
...UmaInvitationFragment
}}
}}
{}
",
uma_invitation::FRAGMENT
);

let mut variables: HashMap<&str, Value> = HashMap::new();
variables.insert("invitation_code", invitation_code.into());

let value = serde_json::to_value(variables).map_err(Error::ConversionError)?;
let json = self
.requester
.execute_graphql(&operation, Some(value))
.await?;

let result = serde_json::from_value(json["uma_invitation_by_code"].clone())
.map_err(Error::JsonError)?;
Ok(result)
}

fn hash_phone_number(phone_number_e164: &str) -> Result<String, Error> {
let e164_regex = regex::Regex::new(r"^\+[1-9]\d{1,14}$").unwrap();
if !e164_regex.is_match(phone_number_e164) {
return Err(Error::InvalidPhoneNumber);
}
let mut hasher = Sha256::new();
hasher.update(phone_number_e164.as_bytes());
Ok(hex::encode(hasher.finalize()))
}
}
2 changes: 2 additions & 0 deletions lightspark/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub enum Error {
WebhookSignatureError,
SigningKeyNotFound,
InvalidCurrencyConversion,
InvalidPhoneNumber,
}

impl fmt::Display for Error {
Expand All @@ -33,6 +34,7 @@ impl fmt::Display for Error {
}
Self::SigningKeyNotFound => write!(f, "Signing key not found"),
Self::InvalidCurrencyConversion => write!(f, "Invalid currency conversion"),
Self::InvalidPhoneNumber => write!(f, "Invalid phone number. Must be E.164 format."),
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion lightspark/src/objects/account_to_nodes_connection.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Copyright ©, 2023-present, Lightspark Group, Inc. - All Rights Reserved
use crate::objects::connection::Connection;
use serde::{Deserialize, Serialize};

use crate::objects::lightspark_node::LightsparkNodeEnum;
use crate::objects::page_info::PageInfo;
use serde::{Deserialize, Serialize};
use std::vec::Vec;

/// A connection between an account and the nodes it manages.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Copyright ©, 2023-present, Lightspark Group, Inc. - All Rights Reserved
use crate::objects::connection::Connection;
use crate::objects::payment_request::PaymentRequestEnum;
use serde::{Deserialize, Serialize};
use std::vec::Vec;

use crate::objects::page_info::PageInfo;
use crate::objects::payment_request::PaymentRequestEnum;
use std::vec::Vec;

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AccountToPaymentRequestsConnection {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::objects::currency_amount::CurrencyAmount;
use crate::objects::page_info::PageInfo;
use crate::objects::transaction::TransactionEnum;
use serde::{Deserialize, Serialize};

use std::vec::Vec;

#[derive(Debug, Clone, Deserialize, Serialize)]
Expand Down
9 changes: 9 additions & 0 deletions lightspark/src/objects/claim_uma_invitation_input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright ©, 2023-present, Lightspark Group, Inc. - All Rights Reserved
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ClaimUmaInvitationInput {
pub invitation_code: String,

pub invitee_uma: String,
}
18 changes: 18 additions & 0 deletions lightspark/src/objects/claim_uma_invitation_output.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright ©, 2023-present, Lightspark Group, Inc. - All Rights Reserved
use crate::types::entity_wrapper::EntityWrapper;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ClaimUmaInvitationOutput {
#[serde(rename = "claim_uma_invitation_output_invitation")]
pub invitation: EntityWrapper,
}

pub const FRAGMENT: &str = "
fragment ClaimUmaInvitationOutputFragment on ClaimUmaInvitationOutput {
__typename
claim_uma_invitation_output_invitation: invitation {
id
}
}
";
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright ©, 2023-present, Lightspark Group, Inc. - All Rights Reserved
use crate::objects::region_code::RegionCode;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ClaimUmaInvitationWithIncentivesInput {
pub invitation_code: String,

pub invitee_uma: String,

pub invitee_phone_hash: String,

pub invitee_region: RegionCode,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright ©, 2023-present, Lightspark Group, Inc. - All Rights Reserved
use crate::types::entity_wrapper::EntityWrapper;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ClaimUmaInvitationWithIncentivesOutput {
#[serde(rename = "claim_uma_invitation_with_incentives_output_invitation")]
pub invitation: EntityWrapper,
}

pub const FRAGMENT: &str = "
fragment ClaimUmaInvitationWithIncentivesOutputFragment on ClaimUmaInvitationWithIncentivesOutput {
__typename
claim_uma_invitation_with_incentives_output_invitation: invitation {
id
}
}
";
12 changes: 12 additions & 0 deletions lightspark/src/objects/create_invitation_with_incentives_input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright ©, 2023-present, Lightspark Group, Inc. - All Rights Reserved
use crate::objects::region_code::RegionCode;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CreateInvitationWithIncentivesInput {
pub inviter_uma: String,

pub inviter_phone_hash: String,

pub inviter_region: RegionCode,
}
18 changes: 18 additions & 0 deletions lightspark/src/objects/create_invitation_with_incentives_output.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright ©, 2023-present, Lightspark Group, Inc. - All Rights Reserved
use crate::types::entity_wrapper::EntityWrapper;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CreateInvitationWithIncentivesOutput {
#[serde(rename = "create_invitation_with_incentives_output_invitation")]
pub invitation: EntityWrapper,
}

pub const FRAGMENT: &str = "
fragment CreateInvitationWithIncentivesOutputFragment on CreateInvitationWithIncentivesOutput {
__typename
create_invitation_with_incentives_output_invitation: invitation {
id
}
}
";
7 changes: 7 additions & 0 deletions lightspark/src/objects/create_uma_invitation_input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright ©, 2023-present, Lightspark Group, Inc. - All Rights Reserved
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CreateUmaInvitationInput {
pub inviter_uma: String,
}
Loading

0 comments on commit 23ae9f5

Please sign in to comment.