-
Notifications
You must be signed in to change notification settings - Fork 524
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expose new request interface, implement first few request typings #843
Conversation
- src/api: add basic implementation of request/requestAll() - src/ledgers/account_info: refactor to simplify with request() - src/ledgers/balances: refactor to simplify with request() - src/ledgers/orderbook: refactor to simplify with requestAll() - src/ledgers/orders: refactor to simplify with requestAll() - src/ledgers/trustlines: refactor to simplify with requestAll()
src/ledger/accountinfo.ts
Outdated
ledgerVersion?: number | ||
} | ||
|
||
type AccountInfoResponse = { | ||
type GetAccountInfoFormattedResponse = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that some types were renamed, like AccountInfoResponse
-> GetAccountInfoFormattedResponse
, TrustlinesOptions
-> GetTrustlinesOptions
, Trustline
-> ParsedTrustline
. Could you explain a bit about how these types should be named?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely, a would love some feedback from you on these as well.
The main idea behind the type renaming/reorganization is to best support the rippled API as the main source of truth. But the current AccountInfoResponse
, TrustlinesOptions
, and Trustline
types all describe special objects created and defined specifically by ripple-lib. So, for example, now that we're adding typing info for the actual standard "account_info" response format, AccountInfoResponse
is a confusing name for the custom/specialized getAccountInfo()
response object. Same with the current Trustline
, which is a special custom parsing of a trustline and not the standard API format for one.
What do you think? So far I haven't had followed any specific rules while renaming existing custom types that conflicted with standard formats. But happy to work out a set of rules to follow if that's a concern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes mostly look reasonable :)
The authorized
property was removed which I think is a bug.
I would be interested in a proposed set of rules for naming of types.
}) | ||
// rippled doesn't provide the counterparty's qualities | ||
const counterparty = removeUndefined({ | ||
limit: trustline.limit_peer, | ||
ripplingDisabled: trustline.no_ripple_peer || undefined, | ||
frozen: trustline.freeze_peer || undefined, | ||
authorized: trustline.peer_authorized || undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to keep authorized
. Unless there's a reason it was removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't see peer_authorized
mentioned anywhere here, and assumed it was an old removed property / always undefined. https://ripple.com/build/rippled-apis/#account-lines
Happy to put it back in if that's not true (& create issue to document)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is missing from the documentation. I've opened a PR for the docs to add it in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 added back
@intelliot Here are the general naming guidelines I've been following so far, lmk what you think:
|
Sounds good to me! |
Aside from |
If a user had specific handling around function input validation, in some situations that may also need to change. It's minor, but may be worth considering when choosing the next version number. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
export type RippledAmount = string | Amount; | ||
|
||
/** | ||
* Specification of which currency the account taking the offer would pay/receive, as an object with currency and issuer fields (omit issuer for XRP). Similar to currency amounts. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this comment is long for one line (could benefit from being hard wrapped)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great stuff @FredKSchott
src/api.ts
Outdated
// unknown string). | ||
const singleResult = await (<Function>this._request)(command, repeatProps) | ||
marker = singleResult.marker | ||
count += singleResult[collectKey].length |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this first check that singleResult[collectKey]
exists in case the wrong collect key was provided?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call, do we have a precedence for rejecting if the response object is malformed?
As long as the API will always return this property for a response I think it's safe to reject if singleResult[collectKey]
=== undefined
. But if that's not a safe assumption maybe we just stop collecting and return the responses collected so far. What do you guys think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure - I think either way should be fine, since this shouldn't actually happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at some of the existing parse functions in src/ledger/parse/
, we seem to be in the habit of not rejecting if the response object is malformed.
https://github.com/ripple/ripple-lib/blob/e311b74dac0a90e7a83b9126f0d64840a395e789/src/ledger/parse/payment-channel.ts#L46-L61
So I'd vote for returning the collected responses.
export interface SignerEntry { | ||
Account: string, | ||
SignerWeight: number | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: missing newline here and in a few other added files
transactions?: TransactionData[], | ||
} | ||
|
||
interface TransactionData { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It sounds like all of these are optional
https://ripple.com/build/rippled-apis/#account-info
Each object in the transactions array, if present, may contain any or all of the following fields:
offers?: AccountOffer[]; | ||
} | ||
|
||
export interface AccountOffer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should also include seq: number
marker?: any; | ||
} | ||
|
||
export interface AccountOffersResponse { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should also include ledger_hash?: string
;
https://ripple.com/build/rippled-apis/#account-offers
} | ||
|
||
export interface BookOffersResponse { | ||
account: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
account: string;
@@ -0,0 +1,14 @@ | |||
import {RippledAmount} from './amounts' | |||
|
|||
export interface OfferCreateTransaction { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this include all common fields?
https://ripple.com/build/transactions/#common-fields
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea, these common fields should eventually go into some Transaction "base"/"common" type that the others can extend from. Let me add them to OfferCreateTransaction
directly for now, and then clean up when we touch the rest of the Transaction types (already looking to be the most complex part of typing).
|
||
export type OrdersOptions = { | ||
limit?: number, | ||
ledgerVersion?: number |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@intelliot I realize this isn't new, but is it possible to specify the closed or current (not validated) ledger versions? Same thing in getOffers
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on the docs, this is intended for specifying a historical ledger version. I'm not sure about closed/current - I'll take a closer look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ripple-lib currently doesn't allow using not-validated ledger versions. This is something we could revisit in the future, but for now, this is intentional.
|
||
export interface BookOffersResponse { | ||
account: string; | ||
offers: OfferCreateTransaction[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://ripple.com/build/rippled-apis/#book-offers
In addition to the standard Offer fields, the following fields may be included in members of the
offers
array
owner_funds?: string;
taker_gets_funded?: RippledAmount;
taker_pays_funded?: RippledAmount;
quality?: number;
Should this instead be a different interface (OrderBookOffers
?) that extends OfferCreateTransaction
and includes those additional fields?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Yea I think that's the right way to handle this
// 2. Make Request | ||
const responses = await this._requestAll('account_offers', { | ||
account: address, | ||
ledger_index: options.ledgerVersion || await this.getLedgerVersion(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use 'validated'
instead of await this.getLedgerVersion()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wilsonianb My understanding is that this.getLedgerVersion()
returns _ledgerVersion
, which is always updated to the ledger_index
of the last message. Would this be exactly similar to validated
?
"validated"
would be so much simpler, I'd love to make the change if we can.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think validated
would be the same. We can make the change in a later PR, though.
Looks like the new types weren't linting, should be all fixed now. @wilsonianb thanks for the feedback! Super helpful to get a second eye on those request/response type definitions. I left a few questions, PTAL when you can |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are clamp
ing the limit
too early, preventing _requestAll
from getting the correct value for countTo
.
In my manual testing, it looks like limit > 400
doesn't cause any harm (I still just get 400 items back). So I doubt that clamp
is actually needed.
src/ledger/orders.ts
Outdated
const responses = await this._requestAll('account_offers', { | ||
account: address, | ||
ledger_index: options.ledgerVersion || await this.getLedgerVersion(), | ||
limit: utils.clamp(options.limit, 10, 400) || undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should just pass options.limit
here
src/ledger/trustlines.ts
Outdated
account: address, | ||
ledger_index: ledgerVersion, | ||
marker: marker, | ||
limit: utils.clamp(limit, 10, 400), | ||
limit: utils.clamp(options.limit, 10, 400), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should not clamp
@intelliot so I wanted to mimic the existing logic as close as possible, which meant continuing to use clamp in the existing API methods. But I totally agree it shouldn't be needed, which is why I was intentionally keeping it out of the new Does that make sense? Happy to remove clamp from the existing logic if you don't think it's doing anything useful. |
I think the existing logic works because it clamps later in the flow, allowing more than 400 results to ultimately be returned. (The limit is 400 for an individual request, but since ripple-lib makes multiple requests, you can get more than 400 items in the end.) |
To clarify - this PR currently fails to mimic the existing logic because it doesn't replicate the behavior of |
This revealed one of the (undoubtedly many) gaps in our unit test coverage. Here's a new test that catches the issue: #850 |
… into FredKSchott-request-interface
@intelliot Ahhhh I see, so you can request That's a bummer, I wanted to avoid manipulating the given request arguments as much as possible. But since we're already setting Longer term, if you said |
Right! I've looked at the relevant rippled code and I believe we can remove the client-side clamping entirely. I'll call out the change in the release notes when that goes out. Out of interest, is there a reason to use the do/while instead of recursion? |
Just pushed the move the clamp inside |
I think that's intentional based on the docs for
However, I don't think that's entirely accurate. Nothing bad happens if the value is beyond this range. I'll put in a PR for updating these docs and see what the rest of the team thinks. In the current implementation, the result is truncated client-side, so if you pass |
No specific reason for a loop vs. recursion. I'll avoid saying "it's cleaner" since that's subjective, but objectively having a flat loop that doesn't involve deeply calling itself / passing around arguments is easier to follow (for humans) and statically analyze (for tooling) than a recursive function. This is only true now that we have |
@intelliot ha! I didn't even realize that. I'll add that behavior back for now as well and then we can revaluate all this again when it's time to make Edit: done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@wilsonianb Could you take another look? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Besides the extra BookOffersResponse
field and checking that singleResult[collectKey]
exists, everything else LGTM
} | ||
|
||
export interface BookOffersResponse { | ||
account: string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
account: string;
https://ripple.com/build/rippled-apis/#book-offers
src/api.ts
Outdated
// unknown string). | ||
const singleResult = await (<Function>this._request)(command, repeatProps) | ||
marker = singleResult.marker | ||
count += singleResult[collectKey].length |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at some of the existing parse functions in src/ledger/parse/
, we seem to be in the habit of not rejecting if the response object is malformed.
https://github.com/ripple/ripple-lib/blob/e311b74dac0a90e7a83b9126f0d64840a395e789/src/ledger/parse/payment-channel.ts#L46-L61
So I'd vote for returning the collected responses.
@wilsonianb thanks for the review! Addressed both 👍 |
src/api.ts
Outdated
@@ -208,7 +208,8 @@ class RippleAPI extends EventEmitter { | |||
lastBatchLength = singleResult[collectKey].length | |||
results.push(singleResult) | |||
} while(!!marker && count < countTo && lastBatchLength !== 0) | |||
return results | |||
// NOTE: We slice out any additional items over the original requested num | |||
return results.slice(0, countTo) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
follow up: this was reverted for now. Since results
is an array of full response objects (where responses can contain multiple data objects).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
A part of #812
/cc @intelliot
Now that the Flow->TypeScript migration is complete, we have support for the function type overloading that will support direct API requests (for all TypeScript projects and JavaScript projects via supported text editors like VS Code).
This PR intentionally only adds typing for a few commands for now. I want to get this reviewed first before going any further. Once this is merged the remaining requests/responses can be split up and parallelized.
The existing methods have been refactored to use the new interface internally, and the goal is still to not make any breaking changes to how they behave.
Summary
request()
/requestAll()
methods (currently marked private until more baked)account_info
,account_lines
,account_offers
,book_offers
,gateway_balances