Skip to content

Commit

Permalink
Fix feature support
Browse files Browse the repository at this point in the history
Add a no-op code handle for subscription creation when the crate is compiled
without the mint feature
  • Loading branch information
crodas committed Nov 9, 2024
1 parent 50d7afc commit a4e12b7
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 110 deletions.
2 changes: 1 addition & 1 deletion crates/cdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ serde_json = "1"
serde_with = "3"
tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] }
thiserror = "1"
futures = { version = "0.3.28", default-features = false, optional = true }
futures = { version = "0.3.28", default-features = false, optional = true, features = ["alloc"] }
url = "2.3"
utoipa = { version = "4", optional = true }
uuid = { version = "1", features = ["v4"] }
Expand Down
134 changes: 25 additions & 109 deletions crates/cdk/src/nuts/nut17.rs → crates/cdk/src/nuts/nut17/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
//! Specific Subscription for the cdk crate

use super::{BlindSignature, CurrencyUnit, PaymentMethod};
#[cfg(feature = "mint")]
use crate::cdk_database::{self, MintDatabase};
pub use crate::pub_sub::SubId;
use crate::{
cdk_database::{self, MintDatabase},
nuts::{
MeltQuoteBolt11Response, MeltQuoteState, MintQuoteBolt11Response, MintQuoteState,
ProofState,
},
pub_sub::{self, Index, Indexable, OnNewSubscription, SubscriptionGlobalId},
pub_sub::{self, Index, Indexable, SubscriptionGlobalId},
};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, ops::Deref, sync::Arc};
use std::ops::Deref;
#[cfg(feature = "mint")]
use std::sync::Arc;

#[cfg(feature = "mint")]
mod on_subscription;

#[cfg(feature = "mint")]
pub use on_subscription::OnSubscription;

#[cfg(not(feature = "mint"))]
mod on_subscription_no_op;

#[cfg(not(feature = "mint"))]
pub use on_subscription_no_op::OnSubscription;

/// Subscription Parameter according to the standard
#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -58,10 +75,6 @@ impl Default for SupportedMethods {
}
}

pub use crate::pub_sub::SubId;

use super::{BlindSignature, CurrencyUnit, PaymentMethod, PublicKey};

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
/// Subscription response
Expand Down Expand Up @@ -145,125 +158,28 @@ impl From<Params> for Vec<Index<(String, Kind)>> {
}
}

#[derive(Default)]
/// Subscription Init
///
/// This struct triggers code when a new subscription is created.
///
/// It is used to send the initial state of the subscription to the client.
pub struct SubscriptionInit(Option<Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>>);

#[async_trait::async_trait]
impl OnNewSubscription for SubscriptionInit {
type Event = NotificationPayload;
type Index = (String, Kind);

async fn on_new_subscription(
&self,
request: &[&Self::Index],
) -> Result<Vec<Self::Event>, String> {
let datastore = if let Some(localstore) = self.0.as_ref() {
localstore
} else {
return Ok(vec![]);
};

let mut to_return = vec![];

for (kind, values) in request.iter().fold(
HashMap::new(),
|mut acc: HashMap<&Kind, Vec<&String>>, (data, kind)| {
acc.entry(kind).or_default().push(data);
acc
},
) {
match kind {
Kind::Bolt11MeltQuote => {
let queries = values
.iter()
.map(|id| datastore.get_melt_quote(id))
.collect::<Vec<_>>();

to_return.extend(
futures::future::try_join_all(queries)
.await
.map(|quotes| {
quotes
.into_iter()
.filter_map(|quote| quote.map(|x| x.into()))
.map(|x: MeltQuoteBolt11Response| x.into())
.collect::<Vec<_>>()
})
.map_err(|e| e.to_string())?,
);
}
Kind::Bolt11MintQuote => {
let queries = values
.iter()
.map(|id| datastore.get_mint_quote(id))
.collect::<Vec<_>>();

to_return.extend(
futures::future::try_join_all(queries)
.await
.map(|quotes| {
quotes
.into_iter()
.filter_map(|quote| quote.map(|x| x.into()))
.map(|x: MintQuoteBolt11Response| x.into())
.collect::<Vec<_>>()
})
.map_err(|e| e.to_string())?,
);
}
Kind::ProofState => {
let public_keys = values
.iter()
.map(PublicKey::from_hex)
.collect::<Result<Vec<PublicKey>, _>>()
.map_err(|e| e.to_string())?;

to_return.extend(
datastore
.get_proofs_states(&public_keys)
.await
.map_err(|e| e.to_string())?
.into_iter()
.enumerate()
.filter_map(|(idx, state)| {
state.map(|state| (public_keys[idx], state).into())
})
.map(|state: ProofState| state.into()),
);
}
}
}

Ok(to_return)
}
}

/// Manager
/// Publish–subscribe manager
///
/// Nut-17 implementation is system-wide and not only through the WebSocket, so
/// it is possible for another part of the system to subscribe to events.
pub struct PubSubManager(pub_sub::Manager<NotificationPayload, (String, Kind), SubscriptionInit>);
pub struct PubSubManager(pub_sub::Manager<NotificationPayload, (String, Kind), OnSubscription>);

impl Default for PubSubManager {
fn default() -> Self {
PubSubManager(SubscriptionInit::default().into())
PubSubManager(OnSubscription::default().into())
}
}

#[cfg(feature = "mint")]
impl From<Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>> for PubSubManager {
fn from(val: Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>) -> Self {
PubSubManager(SubscriptionInit(Some(val)).into())
PubSubManager(OnSubscription(Some(val)).into())
}
}

impl Deref for PubSubManager {
type Target = pub_sub::Manager<NotificationPayload, (String, Kind), SubscriptionInit>;
type Target = pub_sub::Manager<NotificationPayload, (String, Kind), OnSubscription>;

fn deref(&self) -> &Self::Target {
&self.0
Expand Down
110 changes: 110 additions & 0 deletions crates/cdk/src/nuts/nut17/on_subscription.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//! On Subscription
//!
//! This module contains the code that is triggered when a new subscription is created.
use super::{Kind, NotificationPayload};
use crate::{
cdk_database::{self, MintDatabase},
nuts::{MeltQuoteBolt11Response, MintQuoteBolt11Response, ProofState, PublicKey},
pub_sub::OnNewSubscription,
};
use std::{collections::HashMap, sync::Arc};

#[derive(Default)]
/// Subscription Init
///
/// This struct triggers code when a new subscription is created.
///
/// It is used to send the initial state of the subscription to the client.
pub struct OnSubscription(
pub(crate) Option<Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>>,
);

#[async_trait::async_trait]
impl OnNewSubscription for OnSubscription {
type Event = NotificationPayload;
type Index = (String, Kind);

async fn on_new_subscription(
&self,
request: &[&Self::Index],
) -> Result<Vec<Self::Event>, String> {
let datastore = if let Some(localstore) = self.0.as_ref() {
localstore
} else {
return Ok(vec![]);
};

let mut to_return = vec![];

for (kind, values) in request.iter().fold(
HashMap::new(),
|mut acc: HashMap<&Kind, Vec<&String>>, (data, kind)| {
acc.entry(kind).or_default().push(data);
acc
},
) {
match kind {
Kind::Bolt11MeltQuote => {
let queries = values
.iter()
.map(|id| datastore.get_melt_quote(id))
.collect::<Vec<_>>();

to_return.extend(
futures::future::try_join_all(queries)
.await
.map(|quotes| {
quotes
.into_iter()
.filter_map(|quote| quote.map(|x| x.into()))
.map(|x: MeltQuoteBolt11Response| x.into())
.collect::<Vec<_>>()
})
.map_err(|e| e.to_string())?,
);
}
Kind::Bolt11MintQuote => {
let queries = values
.iter()
.map(|id| datastore.get_mint_quote(id))
.collect::<Vec<_>>();

to_return.extend(
futures::future::try_join_all(queries)
.await
.map(|quotes| {
quotes
.into_iter()
.filter_map(|quote| quote.map(|x| x.into()))
.map(|x: MintQuoteBolt11Response| x.into())
.collect::<Vec<_>>()
})
.map_err(|e| e.to_string())?,
);
}
Kind::ProofState => {
let public_keys = values
.iter()
.map(PublicKey::from_hex)
.collect::<Result<Vec<PublicKey>, _>>()
.map_err(|e| e.to_string())?;

to_return.extend(
datastore
.get_proofs_states(&public_keys)
.await
.map_err(|e| e.to_string())?
.into_iter()
.enumerate()
.filter_map(|(idx, state)| {
state.map(|state| (public_keys[idx], state).into())
})
.map(|state: ProofState| state.into()),
);
}
}
}

Ok(to_return)
}
}
28 changes: 28 additions & 0 deletions crates/cdk/src/nuts/nut17/on_subscription_no_op.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//! On Subscription - No
//!
//! This module contains the code that is triggered when a new subscription is
//! created, which is a no-op, since the crate is not compiled with the `mint`
//! feature, therefore there is no local store to query.
use super::{Kind, NotificationPayload};
use crate::pub_sub::OnNewSubscription;

#[derive(Default)]
/// Subscription Init
///
/// This struct triggers code when a new subscription is created.
///
/// It is used to send the initial state of the subscription to the client.
pub struct OnSubscription;

#[async_trait::async_trait]
impl OnNewSubscription for OnSubscription {
type Event = NotificationPayload;
type Index = (String, Kind);

async fn on_new_subscription(
&self,
_request: &[&Self::Index],
) -> Result<Vec<Self::Event>, String> {
Ok(vec![])
}
}

0 comments on commit a4e12b7

Please sign in to comment.