fedimint_api_client/api/global_api/
with_request_hook.rs

1use std::collections::BTreeSet;
2use std::sync::Arc;
3
4use fedimint_core::core::ModuleInstanceId;
5use fedimint_core::module::ApiRequestErased;
6use fedimint_core::task::{MaybeSend, MaybeSync};
7use fedimint_core::{PeerId, apply, async_trait_maybe_send, maybe_add_send_sync};
8use serde_json::Value;
9
10use super::super::{DynModuleApi, IRawFederationApi};
11use crate::api::PeerResult;
12
13/// "Api Request Hook"
14///
15/// An "api request hook" is a function that gets a raw federation api and
16/// can either pass it unmodified (no hook) or wrap it in whatever custom
17/// logic and return as a new raw federation api, possibly forwarding the call
18/// to the original one.
19///
20/// This is meant to allow downstream users to add custom logic for debugging,
21/// testing (e.g. simulating network being down), collecting stats, notifing
22/// about slow calls, errors, etc.
23pub type ApiRequestHook =
24    Arc<maybe_add_send_sync!(dyn Fn(DynIRawFederationApi) -> DynIRawFederationApi + 'static)>;
25
26pub type DynIRawFederationApi = Box<maybe_add_send_sync!(dyn IRawFederationApi + 'static)>;
27
28/// Convenience extension trait used for wrapping [`IRawFederationApi`] in
29/// a [`RawFederationApiWithRequestHook`]
30pub trait RawFederationApiWithRequestHookExt
31where
32    Self: Sized,
33{
34    fn with_request_hook(self, hook: &ApiRequestHook) -> RawFederationApiWithRequestHook;
35}
36
37impl<T> RawFederationApiWithRequestHookExt for T
38where
39    T: IRawFederationApi + MaybeSend + MaybeSync + 'static,
40{
41    fn with_request_hook(self, hook: &ApiRequestHook) -> RawFederationApiWithRequestHook {
42        RawFederationApiWithRequestHook::new(self, hook)
43    }
44}
45
46/// [`IRawFederationApi`] wrapping some `T: IRawFederationApi` in a user hook
47///
48/// Use [`RawFederationApiWithRequestHookExt::with_request_hook`] to
49/// create.
50#[derive(Debug)]
51pub struct RawFederationApiWithRequestHook {
52    pub(crate) inner: DynIRawFederationApi,
53}
54
55impl RawFederationApiWithRequestHook {
56    pub fn new<T>(inner: T, hook: &ApiRequestHook) -> RawFederationApiWithRequestHook
57    where
58        T: IRawFederationApi + MaybeSend + MaybeSync + 'static,
59    {
60        RawFederationApiWithRequestHook {
61            inner: hook(Box::new(inner)),
62        }
63    }
64}
65
66#[apply(async_trait_maybe_send!)]
67impl IRawFederationApi for RawFederationApiWithRequestHook {
68    fn all_peers(&self) -> &BTreeSet<PeerId> {
69        self.inner.all_peers()
70    }
71
72    fn self_peer(&self) -> Option<PeerId> {
73        self.inner.self_peer()
74    }
75
76    fn with_module(&self, id: ModuleInstanceId) -> DynModuleApi {
77        self.inner.with_module(id)
78    }
79
80    async fn request_raw(
81        &self,
82        peer_id: PeerId,
83        method: &str,
84        params: &ApiRequestErased,
85    ) -> PeerResult<Value> {
86        self.inner.request_raw(peer_id, method, params).await
87    }
88}