Skip to content
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

Merged
merged 15 commits into from
Feb 20, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ import signPaymentChannelClaim from './offline/sign-payment-channel-claim'
import verifyPaymentChannelClaim from './offline/verify-payment-channel-claim'
import getLedger from './ledger/ledger'

import {
AccountOffersRequest, AccountOffersResponse,
AccountInfoRequest, AccountInfoResponse,
AccountLinesRequest, AccountLinesResponse,
BookOffersRequest, BookOffersResponse,
GatewayBalancesRequest, GatewayBalancesResponse
} from './common/types/commands'


import RangeSet from './common/rangeset'
import * as ledgerUtils from './ledger/utils'
Expand All @@ -52,6 +60,23 @@ type APIOptions = {
timeout?: number
}

/**
* Get the response key / property name that contains the listed data for a
* command. This varies from command to command, but we need to know it to
* properly count across many requests.
*/
function getCollectKeyFromCommand(command: string): string|undefined {
switch (command) {
case 'account_offers':
case 'book_offers':
return 'offers'
case 'account_lines':
return 'lines'
default:
return undefined
}
}

// prevent access to non-validated ledger versions
class RestrictedConnection extends Connection {
request(request: any, timeout?: number) {
Expand Down Expand Up @@ -106,6 +131,84 @@ class RippleAPI extends EventEmitter {
}
}

/**
* Makes a simple request to the API with the given command and any
* additional request body parameters.
*
* NOTE: This command is under development and should not yet be relied
* on by external consumers.
*/
async _request(command: 'account_info', params: AccountInfoRequest):
Promise<AccountInfoResponse>
async _request(command: 'account_lines', params: AccountLinesRequest):
Promise<AccountLinesResponse>
async _request(command: 'account_offers', params: AccountOffersRequest):
Promise<AccountOffersResponse>
async _request(command: 'book_offers', params: BookOffersRequest):
Promise<BookOffersResponse>
async _request(command: 'gateway_balances', params: GatewayBalancesRequest):
Promise<GatewayBalancesResponse>
async _request(command: string, params: any = {}) {
return this.connection.request({
...params,
command
})
}

/**
* Makes multiple paged requests to the API to return a given number of
* resources. __requestAll() will make multiple requests until the `limit`
* number of resources is reached (if no `limit` is provided, a single request
* will be made).
*
* If the command is unknown, an additional `collect` property is required to
* know which response key contains the array of resources.
*
* NOTE: This command is under development and should not yet be relied
* on by external consumers.
*/
async _requestAll(command: 'account_offers', params: AccountOffersRequest):
Promise<AccountOffersResponse[]>
async _requestAll(command: 'book_offers', params: BookOffersRequest):
Promise<BookOffersResponse[]>
async _requestAll(command: 'account_lines', params: AccountLinesRequest):
Promise<AccountLinesResponse[]>
async _requestAll(
command: string,
params: any = {},
options: {collect?: string} = {}): Promise<any[]> {
// The data under collection is keyed based on the command. Fail if command
// not recognized and collection key not provided.
const collectKey = options.collect || getCollectKeyFromCommand(command)
if (!collectKey) {
throw new errors.ValidationError(`no collect key for command ${command}`)
}
// If limit is not provided, fetches all data over multiple requests.
// NOTE: This may return much more than needed. Set limit when possible.
const countTo = (params.limit !== undefined) ? params.limit : Infinity
const results = []
let count = 0
let marker = params.marker
let lastBatchLength
do {
const countRemaining = countTo - count
const repeatProps = {
...params,
limit: countRemaining,
marker
}
// NOTE: We have to generalize the `this._request()` function signature
// here until we add support for unknown commands (since command is some
// unknown string).
const singleResult = await (<Function>this._request)(command, repeatProps)
marker = singleResult.marker
count += singleResult[collectKey].length
Copy link
Collaborator

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?

Copy link
Contributor Author

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?

Copy link
Collaborator

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.

Copy link
Collaborator

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.

lastBatchLength = singleResult[collectKey].length
results.push(singleResult)
} while(!!marker && count < countTo && lastBatchLength !== 0)
return results
}

connect = connect
disconnect = disconnect
isConnected = isConnected
Expand Down
63 changes: 0 additions & 63 deletions src/common/types.ts

This file was deleted.

36 changes: 36 additions & 0 deletions src/common/types/commands/account_info.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {AccountRoot, SignerList} from '../objects';

export interface AccountInfoRequest {
account: string;
strict?: boolean;
queue?: boolean;
ledger_hash?: string;
ledger_index?: number | ("validated" | "closed" | "current");
signer_lists?: boolean;
}

export interface AccountInfoResponse {
account_data: AccountRoot;
signer_lists?: SignerList[];
ledger_current_index?: number;
ledger_index?: number;
queue_data?: QueueData;
validated?: boolean;
}

interface QueueData {
txn_count: number,
auth_change_queued?: boolean,
lowest_sequence?: number,
highest_sequence?: number,
max_spend_drops_total?: string,
transactions?: TransactionData[],
}

interface TransactionData {
Copy link
Collaborator

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:

auth_change: boolean,
fee: string,
fee_level: string,
max_spend_drops: string,
seq: number
}
19 changes: 19 additions & 0 deletions src/common/types/commands/account_lines.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {RippledAmount} from '../objects';

export interface AccountLinesRequest {
account: string;
ledger_hash?: string;
ledger_index?: number | ("validated" | "closed" | "current");
peer?: string;
limit?: number;
marker?: any;
}

export interface AccountLinesResponse {
account: string;
lines: TrustLine[];
ledger_current_index?: number;
ledger_index?: number;
ledger_hash?: string;
marker?: any;
}
25 changes: 25 additions & 0 deletions src/common/types/commands/account_offers.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {RippledAmount} from '../objects';

export interface AccountOffersRequest {
account: string;
ledger_hash?: string;
ledger_index?: number | ("validated" | "closed" | "current");
limit?: number;
marker?: any;
}

export interface AccountOffersResponse {
Copy link
Collaborator

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

account: string;
ledger_current_index?: number;
ledger_index?: number;
marker?: any;
offers?: AccountOffer[];
}

export interface AccountOffer {
Copy link
Collaborator

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

flags: number,
taker_gets: RippledAmount,
taker_pays: RippledAmount,
quality: string,
expiration?: number
}
21 changes: 21 additions & 0 deletions src/common/types/commands/book_offers.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {TakerRequestAmount} from '../objects/amounts'
import {OfferCreateTransaction} from '../objects/transactions'

export interface BookOffersRequest {
taker?: string;
taker_gets: TakerRequestAmount;
taker_pays: TakerRequestAmount;
ledger_hash?: string;
ledger_index?: number | ("validated" | "closed" | "current");
limit?: number;
marker?: any;
}

export interface BookOffersResponse {
account: string;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

account: string;

offers: OfferCreateTransaction[];
Copy link
Collaborator

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?

Copy link
Contributor Author

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

ledger_hash?: string;
ledger_current_index?: number;
ledger_index?: number;
marker?: any;
}
18 changes: 18 additions & 0 deletions src/common/types/commands/gateway_balances.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

export interface GatewayBalancesRequest {
account: string;
strict?: boolean;
hotwallet: string|Array<string>;
ledger_hash?: string;
ledger_index?: number | ("validated" | "closed" | "current");
}

export interface GatewayBalancesResponse {
account: string;
obligations?: {[currency: string]: string};
balances?: {[address: string]: Amount[]};
assets?: {[address: string]: Amount[]};
ledger_hash?: string;
ledger_current_index?: number;
ledger_index?: number;
}
5 changes: 5 additions & 0 deletions src/common/types/commands/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './account_info'
export * from './account_lines'
export * from './account_offers'
export * from './book_offers'
export * from './gateway_balances'
17 changes: 17 additions & 0 deletions src/common/types/objects/accounts.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export interface AccountRoot {
LedgerEntryType: string,
Account: string,
Flags: number,
Sequence: number,
Balance: string,
OwnerCount: number,
PreviousTxnID: string,
PreviousTxnLgrSeq: number,
AccountTxnID?: string,
RegularKey?: string,
EmailHash?: string,
MessageKey?: string
TickSize?: number,
TransferRate?: number,
Domain?: string
}
18 changes: 18 additions & 0 deletions src/common/types/objects/adjustments.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

export type Adjustment = {
address: string,
amount: Amount,
tag?: number
}

export type MaxAdjustment = {
address: string,
maxAmount: Amount,
tag?: number
}

export type MinAdjustment = {
address: string,
minAmount: Amount,
tag?: number
}
26 changes: 26 additions & 0 deletions src/common/types/objects/amounts.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export type Amount = {
value: string,
currency: string,
issuer?: string,
counterparty?: string,
}


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.
Copy link
Collaborator

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)

*/
export interface TakerRequestAmount {
currency: string;
issuer?: string;
}

/**
* A currency-counterparty pair, or just currency if it's XRP.
*/
export type Issue = {
currency: string,
issuer?: string,
counterparty?: string
}
7 changes: 7 additions & 0 deletions src/common/types/objects/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export * from './accounts'
export * from './adjustments'
export * from './amounts'
export * from './memo'
export * from './signers'
export * from './transactions'
export * from './trustlines'
6 changes: 6 additions & 0 deletions src/common/types/objects/memo.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

export type Memo = {
type?: string,
format?: string,
data?: string
}
Loading