Skip to content

Commit

Permalink
Merge pull request #345 from radixdlt/merge-birch-updates
Browse files Browse the repository at this point in the history
Merge birch updates
  • Loading branch information
krzlabrdx authored Jul 14, 2023
2 parents c1df2fd + 7041bf7 commit 7cc4f4c
Show file tree
Hide file tree
Showing 17 changed files with 12,094 additions and 4,423 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/publish-typescript-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ jobs:
- run: cat $NPM_CONFIG_USERCONFIG
- name: Build alphanet-gateway-sdk
run: |
yarn
yarn build
npm ci
npm run build
npm run test
- name: Setup tags for npm
id: setup_tags
Expand Down
1 change: 0 additions & 1 deletion sdk/typescript/.yarnrc.yml

This file was deleted.

13 changes: 12 additions & 1 deletion sdk/typescript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ High Level APIs will grow over time as we start encountering repeating patterns
### State

- `getEntityDetailsVaultAggregated(entities: string | string[])` - detailed information about entities
- `getAllEntityMetadata(entity: string)` - get all metadata about given entity
- `getEntityMetadata(entity: string, cursor?: string)` - get paged metadata about given entity
- `getValidators(cursor?: string)` - get paged validators
- `getAllValidators()` - get all validators
- `getNonFungibleIds(address:string, cursor?: string)` - get paged non fungible ids for given address
- `getAllNonFungibleIds(address: string)` - get all non fungible ids for given address
- `getNonFungibleData(address: string, ids: string | string[])` - get non fungible data

### Status

Expand All @@ -81,7 +88,11 @@ High Level APIs will grow over time as we start encountering repeating patterns
### Transaction

- `getStatus(txID: string)` - transaction status for given transaction id (the intent hash)
- `getCommittedDetails(txID: string)` - transaction details for given transaction id (the intent hash)
- `getCommittedDetails(txID: string, options)` - transaction details for given transaction id (the intent hash)

### Stream

- `getTransactionsList(affectedEntities?: string[], cursor?: string)` - get transaction list for given list of entities

## Fetch polyfill

Expand Down
2 changes: 1 addition & 1 deletion sdk/typescript/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export default {
// runner: "jest-runner",

// The paths to modules that run some code to configure or set up the testing environment before each test
setupFiles: ['<rootDir>/setup-tests.js'],
// setupFiles: ['<rootDir>/setup-tests.js'],

// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
Expand Down
5 changes: 5 additions & 0 deletions sdk/typescript/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@
* Maximum number of addresses that can be queried at once when using /state/entity/details endpoint
*/
export const MAX_ADDRESSES_COUNT = 20

/**
* Maximum number of NFT IDs that can be queried at once when using /state/non-fungible/data endpoint
*/
export const MAX_NFT_IDS_COUNT = 29
File renamed without changes.
23 changes: 23 additions & 0 deletions sdk/typescript/lib/helpers/exhaust-pagination.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Exhausts a paginated API resource and returns all the results.
*/
export const exhaustPagination = async <T>(
queryFunction: (
cursor?: string
) => Promise<{ items: T[]; next_cursor?: string | null }>,
start?: string
): Promise<T[]> => {
let next_cursor: string | null | undefined = start
const aggregatedEntities: T[] = []

do {
const queryFunctionResponse: {
next_cursor?: string | null
items: T[]
} = await queryFunction(next_cursor)
aggregatedEntities.push(...queryFunctionResponse.items)
next_cursor = queryFunctionResponse.next_cursor
} while (next_cursor)

return aggregatedEntities
}
2 changes: 2 additions & 0 deletions sdk/typescript/lib/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './chunk'
export * from './exhaust-pagination'
22 changes: 17 additions & 5 deletions sdk/typescript/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { StreamApi } from './generated/apis/StreamApi'
import {
Configuration,
ConfigurationParameters,
StateApi,
StatusApi,
Expand All @@ -9,11 +8,22 @@ import {
import { State } from './subapis/state'
import { Status, Stream } from './subapis'
import { Transaction } from './subapis/transaction'
import { RuntimeConfiguration } from './runtime'

export * from './generated'
export * from './subapis'
export * from './helpers'

export type GatewayApiClientSettings = ConfigurationParameters
export type GatewayApiClientSettings = ConfigurationParameters & {
/**
* Maximum number of addresses that can be queried at once when using /state/entity/details endpoint
*/
maxAddressesCount?: number
/**
* Maximum number of NFT IDs that can be queried at once when using /state/non-fungible/data endpoint
*/
maxNftIdsCount?: number
}

export class GatewayApiClient {
static initialize(settings?: GatewayApiClientSettings) {
Expand All @@ -22,7 +32,9 @@ export class GatewayApiClient {
}

private static constructConfiguration(settings?: GatewayApiClientSettings) {
return new Configuration(settings)
return new RuntimeConfiguration({
...settings,
})
}

state: State
Expand All @@ -37,15 +49,15 @@ export class GatewayApiClient {
transaction: TransactionApi
}

constructor(configuration: Configuration) {
constructor(configuration: RuntimeConfiguration) {
this.lowLevel = {
state: new StateApi(configuration),
stream: new StreamApi(configuration),
status: new StatusApi(configuration),
transaction: new TransactionApi(configuration),
}

this.state = new State(this.lowLevel.state)
this.state = new State(this.lowLevel.state, configuration)
this.stream = new Stream(this.lowLevel.stream)
this.status = new Status(this.lowLevel.status)
this.transaction = new Transaction(this.lowLevel.transaction)
Expand Down
20 changes: 20 additions & 0 deletions sdk/typescript/lib/runtime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { GatewayApiClientSettings } from '.'
import { MAX_ADDRESSES_COUNT, MAX_NFT_IDS_COUNT } from './constants'
import { Configuration as GeneratedConfiguration } from './generated'

export class RuntimeConfiguration extends GeneratedConfiguration {
protected extendedConfiguration: GatewayApiClientSettings

constructor(params: GatewayApiClientSettings) {
super(params)
this.extendedConfiguration = params
}

get maxAddressesCount() {
return this.extendedConfiguration.maxAddressesCount || MAX_ADDRESSES_COUNT
}

get maxNftIdsCount() {
return this.extendedConfiguration.maxNftIdsCount || MAX_NFT_IDS_COUNT
}
}
144 changes: 139 additions & 5 deletions sdk/typescript/lib/subapis/state.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import { chunk } from '../chunk'
import { MAX_ADDRESSES_COUNT } from '../constants'
import { chunk } from '../helpers/chunk'
import { exhaustPagination } from '../helpers/exhaust-pagination'
import {
EntityMetadataItem,
FungibleResourcesCollection,
FungibleResourcesCollectionItemVaultAggregated,
NonFungibleIdsCollection,
NonFungibleResourcesCollection,
NonFungibleResourcesCollectionItemVaultAggregated,
ResourceAggregationLevel,
StateApi,
StateEntityDetailsResponseItem,
StateEntityMetadataPageResponse,
StateNonFungibleDetailsResponseItem,
ValidatorCollection,
ValidatorCollectionItem,
} from '../generated'
import { RuntimeConfiguration } from '../runtime'

export type ReplaceProperty<
ObjectType,
Expand All @@ -34,7 +41,10 @@ export type StateEntityDetailsVaultResponseItem =
}

export class State {
constructor(public innerClient: StateApi) {}
constructor(
public innerClient: StateApi,
public configuration: RuntimeConfiguration
) {}

/**
* Get detailed information about entities together with vault aggregated fungible and non-fungible resources.
Expand Down Expand Up @@ -63,8 +73,8 @@ export class State {
> {
const isArray = Array.isArray(addresses)
if (isArray && addresses.length === 0) return Promise.resolve([])
if (isArray && addresses.length > MAX_ADDRESSES_COUNT) {
const chunks = chunk(addresses, MAX_ADDRESSES_COUNT)
if (isArray && addresses.length > this.configuration.maxAddressesCount) {
const chunks = chunk(addresses, this.configuration.maxAddressesCount)
return Promise.all(
chunks.map((chunk) => this.getEntityDetailsVaultAggregated(chunk))
).then((results) => results.flat())
Expand All @@ -80,4 +90,128 @@ export class State {
? (items as StateEntityDetailsVaultResponseItem[])
: (items[0] as StateEntityDetailsVaultResponseItem)
}

/**
* Get paged list of entity metadata
* @param address
* @param cursor
*/
async getEntityMetadata(
address: string,
cursor?: string
): Promise<StateEntityMetadataPageResponse> {
return this.innerClient.entityMetadataPage({
stateEntityMetadataPageRequest: {
address,
cursor,
},
})
}

/**
* Get list of all metadata items for given entity. This will iterate over returned cursors and aggregate all responses,
* which is why multiple API requests can be made.
*
* @param address - entity address
* @param startCursor - optional cursor to start iteration from
*/
async getAllEntityMetadata(
address: string,
startCursor?: string
): Promise<EntityMetadataItem[]> {
return exhaustPagination(
this.getEntityMetadata.bind(this, address),
startCursor
)
}

/**
* Get paged list of validators
* @param cursor
*/
async getValidators(cursor?: string): Promise<ValidatorCollection> {
return this.innerClient
.stateValidatorsList({
stateValidatorsListRequest: {
cursor: cursor || null,
},
})
.then(({ validators }) => validators)
}

/**
* Get list of all validators. This will iterate over returned cursors and aggregate all responses.
*/
async getAllValidators(start?: string): Promise<ValidatorCollectionItem[]> {
return exhaustPagination(this.getValidators.bind(this), start)
}

/**
* Get paged list of non fungible ids for given non fungible resource address
* @params address - non fungible resource address
* @params cursor - optional cursor used for pagination
*/
async getNonFungibleIds(
address: string,
cursor?: string
): Promise<NonFungibleIdsCollection> {
return this.innerClient
.nonFungibleIds({
stateNonFungibleIdsRequest: {
resource_address: address,
cursor,
},
})
.then(({ non_fungible_ids }) => non_fungible_ids)
}

/**
* Get list of non fungible ids for given non fungible resource address. This will iterate over returned cursors and aggregate all responses.
*
* @params address - non fungible resource address
* @params startCursor - optional cursor to start paging from
*/
async getAllNonFungibleIds(
address: string,
startCursor?: string
): Promise<string[]> {
return exhaustPagination(
this.getNonFungibleIds.bind(this, address),
startCursor
)
}

async getNonFungibleData(
address: string,
ids: string
): Promise<StateNonFungibleDetailsResponseItem>
async getNonFungibleData(
address: string,
ids: string[]
): Promise<StateNonFungibleDetailsResponseItem[]>
async getNonFungibleData(
address: string,
ids: string | string[]
): Promise<
StateNonFungibleDetailsResponseItem | StateNonFungibleDetailsResponseItem[]
> {
const isArray = Array.isArray(ids)
if (isArray && ids.length === 0) return Promise.resolve([])
if (isArray && ids.length > this.configuration.maxNftIdsCount) {
const chunks = chunk(ids, this.configuration.maxNftIdsCount)
return Promise.all(
chunks.map((chunk) => this.getNonFungibleData(address, chunk))
).then((results) => results.flat())
}

const { non_fungible_ids } = await this.innerClient.nonFungibleData({
stateNonFungibleDataRequest: {
resource_address: address,
non_fungible_ids: isArray ? ids : [ids],
},
})
return isArray
? (non_fungible_ids as StateNonFungibleDetailsResponseItem[])
: (non_fungible_ids[0] as StateNonFungibleDetailsResponseItem)
}
}
16 changes: 12 additions & 4 deletions sdk/typescript/lib/subapis/stream.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { StreamApi } from "../generated";
import { StreamApi, StreamTransactionsResponse } from '../generated'

export class Stream {
constructor(public innerClient: StreamApi) {
}
}
constructor(public innerClient: StreamApi) {}

getTransactionsList(affectedEntities?: string[], cursor?: string): Promise<StreamTransactionsResponse> {
return this.innerClient.streamTransactions({
streamTransactionsRequest: {
cursor,
affected_global_entities_filter: affectedEntities,
},
})
}
}
2 changes: 2 additions & 0 deletions sdk/typescript/lib/subapis/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export class Transaction {
receiptEvents: false
receiptFeeSummary: false
receiptStateChanges: false
affectedGlobalEntities: false
}
): Promise<TransactionCommittedDetailsResponse> {
return this.innerClient.transactionCommittedDetails({
Expand All @@ -50,6 +51,7 @@ export class Transaction {
receipt_events: options?.receiptEvents ?? true,
receipt_fee_summary: options?.receiptFeeSummary ?? true,
receipt_state_changes: options?.receiptStateChanges ?? true,
affected_global_entities: options?.affectedGlobalEntities ?? true,
},
},
})
Expand Down
Loading

0 comments on commit 7cc4f4c

Please sign in to comment.