Closed dpc closed 1 year ago
@elsirion @jkitman :+1: / :-1: :question:
That could make sense...the API needs to be modularized as well, maybe this would help?
That could make sense...the API needs to be modularized as well, maybe this would help?
After this there will be no need to modularize as we do not list each individual call anymore.
Each client module Foo
could define a trait FooFedimintAPI
[^1] and impl it for any dyn IFederationAPI
, e.g.
impl LnFederationAPI for &dyn IFederationAPI { // alternatively for T: IFederationAPI
async fn get_contract(id: ContractID) -> Contract {
self.request_current_consensus("/ln/contract", id).await
}
}
[^1]: will be slightly different now with instance IDs, maybe needs some wrapper type that contains the instance id and a ref to the general API client
Ah, OK, sorry, I didn't read carefully. Yes, the types modules can wrap the DynFederationApi
however they desire to avoid mistakes.
I don't know if another trait is not overdoing it (struct LnFederationApi(DynFederationApi)
would do just fine, I guess), but whatever works, so if there's a reason for traits or combination of wrappers and traits, we'll do that.
We really don't need to play play with a per-module-kind apis. At the end of the day everything goes through a same calling convention which is stringly. I know that in Rust stringly makes you :vomiting_face: , but such is the nature of the network APIs, and we should just accept it and embrace the benefits (it being a general purpose/abstract without any extra effort).
The caller knows:
The caller can use constants, enums, functions, whatever to avoid mistakes with 1. - which is the biggest issue with stringly apis.
We can add extension traits / extra methods with default impls to make calling the right consensus strategy short and easy if the caller has one of common needs.
The job of this interface is really to give us a single call where we can intercept / stub the responses, validate some stuff etc. and switch the underlying delivery mechanism.
The job of the trait implementation is to just deliver the call and return the response.
The
IFederationApi
doesn't scale well (we have to keep makingunimplemented!()
in every mock implementations), and I'm quite convinced it's not the right interface to put there. And it's up to the caller to know both the call meaning and the implementation (like types to be serialized, but also who to call and when exactly ("strategy")).It seems to me that what we really need is:
+/- some tweaks and helper functions to auto-convert stuff to/from
serde_json::Value
The mocking will have to happen on string level (intercept whole
request_member_raw
andmatch
method
etc.)This way picking the strategy, dealing with serialization types and any other logic is entirely on the caller of
IFederationApi
, while the implementation takes care of delivering the requests, and possibly faking responses.Feedback appreciated.