Closed FredKSchott closed 4 years ago
@FredKSchott This looks really good.
I've been thinking about this proposal all morning, and it's very similar to what I had in mind. In fact, it's better - I hadn't thought about using literal values as types. That provides us with type checking for existing APIs while also allowing new commands to be used. Perfect.
We won't need to make breaking changes (existing methods can be phased out at some point during the next year) and further API improvements/simplifications can be made at the rippled layer.
👍 from me.
@wilsonianb @shekenahglory @mDuo13 What do you think?
This is awesome. I'm totally on board with this. One thing that'll require some work beyond the proposed spec is offline signing though. The API should not use the rippled APIs by default when it can do the signing operation without network access. We should continue to support the existing APIs for this.
sign
request (or similar) when connected via a non-encrypted connection, except to whitelisted "internal" hosts. (For example, it's OK to use unencrypted HTTP when your rippled instance is on localhost
, IPs in the intranet ranges like 10.1.*
and 192.168.*
, etc.) Honestly this maybe should have its own spec for validating this stuff.You've already touched on timestamp conversion. Other formatters that should be supported (this is all implemented already, but may need to be adapted to the new interface):
Flags
integers to maps of booleans and back. This requires some context since the same flag values have different meanings. For example, AccountSet
flag 0x00010000
is tfRequireDestTag, but AccountRoot
flag 0x00010000
is lsfPasswordSpent
.Memos
, Domain
, some others@intelliot nice! That's good to hear this isn't too far off.
@mDuo13 👍 That's a great point. None of those really map 1:1 with rippled API commands either, so it definitely makes sense to leave the offline logic as-is for now. Where they do make requests, we can refactor those to use this new request()
API.
Down the road, it would be nice to make it even more explicit which of those methods are truly offline-only. Right now it's hard to tell which require a connection and what data they'll fetch from it. Also easy to forget which require the "offline" option.
Also great point about Flags
, it sounds like they should be handled by the Data Object (ex: every object has a parsed flags
property object)
Thanks for the feedback! I'll hopefully find some downtime over the holidays to get started on a few of these
@intelliot @mDuo13 I had a bunch of time over the 🦃 holidays to work on this and have a first branch about ready for PR.
A preview of it is up, but I ran into a problem early on:
Flow doesn't support function overloading. I was so used to using TypeScript for JS typings that I'd just assumed Flow supported this as well. I actually ran into a few different issues while digging into the code base, including some obvious violations & bugs that Flow just wasn't detecting.
Has anyone on the team used TypeScript before? Having used both, TypeScript is really miles ahead of Flow:
Would you consider TypeScript for ripple-lib? This is probably something you all want to talk about as a team, but I think it's the right choice for something complex and financially-focused like ripple-lib, even separate from this larger proposal. I'm happy to discuss offline more as well if you have any questions.
I was able to make the move to TypeScript without too much trouble (two full days of flying without wifi didn't hurt). It involved two main changes to the codebase:
Common.js -> ESM (require() -> import, module.exports -> export)
https://github.com/ripple/ripple-lib/compare/develop...FredKSchott:esm
TypeScript uses ESM syntax almost exclusively. It's "strict"
by default, and a much more standard explicit syntax that is easier for tooling/bundlers/browsers to optimize for. I was able to do this conversion first with the existing Babel setup, separate from any TS work, and I'd recommend this change regardless.
Flow -> TypeScript https://github.com/FredKSchott/ripple-lib/compare/esm...FredKSchott:typescript Flow and TypeScript use an almost exactly similar type format, which is nice. Most of the work consisted of fixing type violations & bugs that had gone undetected in Flow. I've erred on the side of caution and documented any issues that weren't easy fixes with TODOs vs. trying to fix them here and introducing breaking changes.
Let me know what you think, happy to create PRs from these branches if you want to move forward.
@intelliot friendly ping! Would love to hear your thoughts on this. Happy to discuss offline as well.
Thanks!
Our team hasn't used TypeScript, but I'm looking into it, and I think it's a good language. These branches look like a great start!
I'll reach out to you via email as well.
This looks good. We would like to move to TypeScript - we'll review PRs when ready.
TypeScript rocks, you'll never wanna fo back to raw JS :)
Sounds good, I'll get those two branches into PRs
Dear god no! If you change again all the interfaces now that I have ported all the old ripple-lib code to RippleAPI I'll leave ripple forever and sell all my XRP and spam FUD on reddit and DDoS the validators :).
@tuloski this work is being done specifically so that you can continue to use the current interface. Each method is being kept and rewritten to use the new general request()
method internally. If ripple-lib ever did officially deprecate them, they wouldn't need to be removed, just moved to a separate helper library and continue to work as-is.
Also, threats against developers or the projects they work on are not appreciated or helpful. Please learn better Open Source manners if you plan to continue contributing.
@FredKSchott I put a smile in the comment because I was joking :)
So do you want to keep the old "getAccountInfo", etc, and just re-implement internally with request/requestAll, such that using request one can use the standard rippled request also? Sounds good. I understood the request was going to replace old methods.
Btw the old ripple-lib already had the same interfaces as rippled. Do you know why rippleAPI modified it to have a separate interface? I agree that having the same common format with rippled answers is very good.
@tuloski Sounds good, tone is hard on Github :)
Correct, that's the immediate-term plan. Personally, I'd eventually like to see the current methods pulled out of ripple-lib and moved to a separate library (since they now use the preferred public request()
interface) but if this ever happened it would be done specifically to be as easy as possible for existing users.
haha, I knew adding this extra "RippleAPI" layer would lead to just more trouble and ultimately more to learn :) You can't escape the lower levels when debugging ...
Excited to see progress in #816! Thanks for the reviews @intelliot.
I've been thinking about an added bonus while I've been adding typing info for different api requests/responses:
Right now, the API is documented on the docs site via markdown. We redefine those interfaces in 1. the docs site, 2. rippled, and 3. ripple-lib (either in Flow or TypeScript). Manually keeping all of these in sync adds extra maintenance + risks bugs/developer issues where they get out of date from each other. It would be great to keep a single source of truth that rippled & the docs site & rippled-lib could all reference and test against:
json-schema-to-typescript
This would be a longer-term project, but the work being done in https://github.com/FredKSchott/ripple-lib/compare/typescript...FredKSchott:request-interface is setting the stage if we end up wanting to go down that road. Those api TypeScript definitions could be converted to JSON Schema and moved to rippled to become the single source of truth.
I've been pushing for a while to get rippled to define JSON Schemas for requests and responses so it could validate input and output. That's not so easy with the way rippled's RPC code is currently architected. But if we did, then yeah, the docs site could easily generate response/request tables from said schemas; the code to do that already exists.
@mDuo13
It would totally be a worthwhile project, getting the schemas in place for the rippled api, even if they didn't use them to validate right away.
further API improvements/simplifications can be made at the rippled layer.
The API layer should be separated from rippled. I think things would move a lot faster if you had "dumb" peers (speaking to rippled via binary) implemented in some higher level language, where the integration guys could actually dogfood the API.
As flawed as this "wash over the rippled api quirks" approach was, it was done for a reason. There's a bunch of inconsistencies in the rippled api.
With a plugin architecture or course ... for community commands ...
But that would be way too cool and make too many people happy
If the goal is to remove all the "high level" request and move everything to "low level" request then here we are. But I don't see why the lib cannot provide at the same time high level request as:
Because sincerely making everything a request is not much more than opening a ws and sending the raw request as described in the rippled API. The big problems the user have is in parsing/filtering the data received.
We want users to be aware of the rippled API and make requests directly. The library does provide helper methods to make it easier to create requests and parse/filter responses.
getBooks is supported with formatBidsAndAsks (coming soon) and getOrderbook (legacy).
I’m not sure what we need to do to support autobridged books, but it’s something I’d like to look into.
Providing helpers for managing subscriptions, maintaining books, and filtering would be great too.
Transaction signing is already supported as well. There’s some code cleanup to do, and it would be great to reduce our dependencies as well.
Thanks for the feedback!
formatBidsAndAsks
is now available. Docs: https://xrpl.org/rippleapi-reference.html#formatbidsandasks
Closing this issue since we've made big strides in this direction, but feel free to open a new issue for any additional 'convenience' helpers that are desired.
Following up from #799:
So I've experimented a bit and have a proposal. Even with 100% proper typing, I'm nervous to make any big changes to the existing API methods and the interfaces/data that they already return. There are a lot of interconnected parts internally, so I'm not just worried about breaking external consumers with a lot of small, hard to detect/message changes.
This proposal aims to reduce the complexity & maintainance costs of having a complete secondary ripple-lib API on top of the standard Rippled API:
Proposal: Simplified API Request Interface
All custom API methods (
getTransactions()
,getTrustlines()
, etc) are deprecated in favor of just two new general-purpose API methods:Examples
Benefits
Support for Request Validation
Request validation can be easily moved into the general
request()
/requestAll()
method:Support for Typing
Overloading the request function signature would allow powerful typing support for
request()
/requestAll()
arguments and response format:Proposal: Flexible API Response Interface
While moving closer to the Rippled API spec is a goal, there are still times where we want to handle/format/parse the response data into an easier-to-use form. Existing API methods handle some properties automatically, but at the expense of flexibility. If the consumer needs other data that the method does not return, they are out of luck.
Data Objects are simple wrappers around raw API response objects, that give the user access to all raw data from the API response (okay for most properties) plus the same helpful formatting that consumers are already used to.
Here's a partial example of what the Offer class would look like:
And then here it is in use, flexible enough to handle any properties that the user wants to work with:
Feedback?
What do you think? Would love your thoughts @intelliot & @contributors.