From 4698dee3ce8fa5a4bf878ff9a7c6137590327864 Mon Sep 17 00:00:00 2001 From: Timur Ramazanov Date: Tue, 2 Apr 2019 14:40:51 +0300 Subject: [PATCH] Effects queries --- src/datasource/horizon.ts | 32 ++++ src/datasource/types.ts | 146 +++++++++++++++ src/model/effect.ts | 140 ++++++++++++++ src/model/factories/asset_factory.ts | 2 +- src/model/factories/effect_factory.ts | 79 ++++++++ src/model/factories/index.ts | 1 + src/model/index.ts | 1 + src/schema/accounts.ts | 1 + src/schema/effects.ts | 256 ++++++++++++++++++++++++++ src/schema/index.ts | 4 +- src/schema/ledgers.ts | 1 + src/schema/operations.ts | 14 ++ src/schema/resolvers/account.ts | 10 +- src/schema/resolvers/effect.ts | 96 ++++++++++ src/schema/resolvers/index.ts | 2 + src/schema/resolvers/ledger.ts | 5 +- src/schema/resolvers/transaction.ts | 5 +- src/schema/resolvers/util.ts | 45 ++++- src/schema/transactions.ts | 1 + src/schema/type_defs.ts | 14 -- 20 files changed, 832 insertions(+), 23 deletions(-) create mode 100644 src/model/effect.ts create mode 100644 src/model/factories/effect_factory.ts create mode 100644 src/schema/effects.ts create mode 100644 src/schema/resolvers/effect.ts diff --git a/src/datasource/horizon.ts b/src/datasource/horizon.ts index 4adbd342..98212354 100644 --- a/src/datasource/horizon.ts +++ b/src/datasource/horizon.ts @@ -4,6 +4,7 @@ import { AccountID, IAssetInput } from "../model"; import { HorizonAssetType, IHorizonAssetData, + IHorizonEffectData, IHorizonOperationData, IHorizonOrderBookData, IHorizonPaymentPathData, @@ -140,6 +141,37 @@ export default class HorizonAPI extends RESTDataSource { }); } + public async getEffects(limit = 10, order: SortOrder = "desc", cursor?: string): Promise { + return this.request("effects", { limit, order, cursor, cacheTtl: 10 }); + } + + public async getTransactionEffects( + transactionId: string, + limit = 10, + order: SortOrder = "desc", + cursor?: string + ): Promise { + return this.request(`transactions/${transactionId}/effects`, { limit, order, cursor, cacheTtl: 10 }); + } + + public async getAccountEffects( + accountId: AccountID, + limit = 10, + order: SortOrder = "desc", + cursor?: string + ): Promise { + return this.request(`accounts/${accountId}/effects`, { limit, order, cursor, cacheTtl: 10 }); + } + + public async getLedgerEffects( + ledgerSeq: number, + limit = 10, + order: SortOrder = "desc", + cursor?: string + ): Promise { + return this.request(`ledgers/${ledgerSeq}/effects`, { limit, order, cursor, cacheTtl: 10 }); + } + private async request( url: string, params: { diff --git a/src/datasource/types.ts b/src/datasource/types.ts index 1e65e69e..eabcdcc4 100644 --- a/src/datasource/types.ts +++ b/src/datasource/types.ts @@ -18,6 +18,32 @@ export type HorizonOpType = | "manage_data" | "bump_sequence"; +type HorizonEffectType = + | "account_created" + | "account_removed" + | "account_credited" + | "account_debited" + | "account_thresholds_updated" + | "account_home_domain_updated" + | "account_flags_updated" + | "account_inflation_destination_updated" + | "signer_created" + | "signer_removed" + | "signer_updated" + | "trustline_created" + | "trustline_removed" + | "trustline_updated" + | "trustline_authorized" + | "trustline_deauthorized" + | "offer_created" + | "offer_removed" + | "offer_updated" + | "trade" + | "data_created" + | "data_removed" + | "data_updated" + | "sequence_bumped"; + interface IBaseOperationData { id: string; paging_token: any; @@ -201,3 +227,123 @@ export interface IHorizonTradeAggregationData { open: string; close: string; } + +interface IBaseEffect { + id: string; + paging_token: string; + account: AccountID; + type: HorizonEffectType; + created_at: string; +} + +interface IAccountCreatedEffect extends IBaseEffect { + starting_balance: string; +} + +interface IAccountCreditedEffect extends IBaseEffect { + amount: string; +} + +interface IAccountDebitedEffect extends IBaseEffect { + amount: string; +} + +interface IAccountThresholdsUpdatedEffect extends IBaseEffect { + low_threshold: number; + med_threshold: number; + high_threshold: number; +} + +interface IAccountHomeDomainUpdatedEffect extends IBaseEffect { + home_domain: string; +} + +interface IAccountFlagsUpdatedEffect extends IBaseEffect { + auth_required_flag?: boolean; + auth_revokable_flag?: boolean; +} + +interface ISequenceBumpedEffect extends IBaseEffect { + new_seq: number; +} + +interface ISignerCreatedEffect extends IBaseEffect { + weight: number; + public_key: string; + key: string; +} + +interface ISignerRemovedEffect extends IBaseEffect { + weight: number; + public_key: string; + key: string; +} + +interface ISignerUpdatedEffect extends IBaseEffect { + weight: number; + public_key: string; + key: string; +} + +interface ITrustlineCreatedEffect extends IBaseEffect { + asset_type: HorizonAssetType; + asset_code: AssetCode; + asset_issuer: AccountID; + limit: string; +} + +interface ITrustlineRemovedEffect extends IBaseEffect { + asset_type: HorizonAssetType; + asset_code: AssetCode; + asset_issuer: AccountID; + limit: string; +} + +interface ITrustlineUpdatedEffect extends IBaseEffect { + asset_type: HorizonAssetType; + asset_code: AssetCode; + asset_issuer: AccountID; + limit: string; +} + +interface ITrustlineAuthorizedEffect extends IBaseEffect { + trustor: AccountID; + asset_type: HorizonAssetType; + asset_code?: AssetCode; +} + +interface ITrustlineDeauthorizedEffect extends IBaseEffect { + trustor: AccountID; + asset_type: HorizonAssetType; + asset_code?: AssetCode; +} + +interface ITradeEffect extends IBaseEffect { + seller: AccountID; + offer_id: number; + sold_amount: string; + sold_asset_type: HorizonAssetType; + sold_asset_code?: AssetCode; + sold_asset_issuer?: AccountID; + bought_amount: string; + bought_asset_type: HorizonAssetType; + bought_asset_code?: AssetCode; + bought_asset_issuer?: AccountID; +} + +export type IHorizonEffectData = IAccountCreatedEffect & + IAccountCreditedEffect & + IAccountDebitedEffect & + IAccountThresholdsUpdatedEffect & + IAccountHomeDomainUpdatedEffect & + IAccountFlagsUpdatedEffect & + ISequenceBumpedEffect & + ISignerCreatedEffect & + ISignerRemovedEffect & + ISignerUpdatedEffect & + ITrustlineCreatedEffect & + ITrustlineRemovedEffect & + ITrustlineUpdatedEffect & + ITrustlineAuthorizedEffect & + ITrustlineDeauthorizedEffect & + ITradeEffect; diff --git a/src/model/effect.ts b/src/model/effect.ts new file mode 100644 index 00000000..a9211ea5 --- /dev/null +++ b/src/model/effect.ts @@ -0,0 +1,140 @@ +import { Asset } from "stellar-sdk"; +import { AccountID } from "./"; + +export enum EffectKinds { + AccountCreated = "accountCreated", + AccountRemoved = "accountRemoved", + AccountCredited = "accountCredited", + AccountDebited = "accountDebited", + AccountThresholdsUpdated = "accountThresholdsUpdated", + AccountHomeDomainUpdated = "accountHomeDomainUpdated", + AccountFlagsUpdated = "accountFlagsUpdated", + AccountInflationDestinationUpdated = "accountInflationDestinationUpdated", + SignerCreated = "signerCreated", + SignerRemoved = "signerRemoved", + SignerUpdated = "signerUpdated", + TrustlineCreated = "trustlineCreated", + TrustlineRemoved = "trustlineRemoved", + TrustlineUpdated = "trustlineUpdated", + TrustlineAuthorized = "trustlineAuthorized", + TrustlineDeauthorized = "trustlineDeauthorized", + OfferCreated = "offerCreated", + OfferRemoved = "offerRemoved", + OfferUpdated = "offerUpdated", + Trade = "trade", + DataCreated = "dataCreated", + DataRemoved = "dataRemoved", + DataUpdated = "dataUpdated", + SequenceBumped = "sequenceBumped" +} + +export interface IBaseEffect { + id: string; + account: AccountID; + kind: EffectKinds; + createdAt: Date; +} + +export interface IAccountCreatedEffect extends IBaseEffect { + startingBalance: string; +} + +export interface IAccountCreditedEffect extends IBaseEffect { + amount: string; +} + +export interface IAccountDebitedEffect extends IBaseEffect { + amount: string; +} + +export interface IAccountThresholdsUpdatedEffect extends IBaseEffect { + lowThreshold: number; + medThreshold: number; + highThreshold: number; +} + +export interface IAccountHomeDomainUpdatedEffect extends IBaseEffect { + homeDomain: string; +} + +export interface IAccountFlagsUpdatedEffect extends IBaseEffect { + authRequiredFlag?: boolean; + authRevokableFlag?: boolean; +} + +export interface ISignerCreatedEffect extends IBaseEffect { + weight: number; + publicKey: AccountID; + key: AccountID; +} + +export interface ISignerRemovedEffect extends IBaseEffect { + weight: number; + publicKey: AccountID; + key: AccountID; +} + +export interface ISignerUpdatedEffect extends IBaseEffect { + weight: number; + publicKey: AccountID; + key: AccountID; +} + +export interface ITrustlineCreatedEffect extends IBaseEffect { + asset: Asset; + limit: string; +} + +export interface ITrustlineRemovedEffect extends IBaseEffect { + asset: Asset; + limit: string; +} + +export interface ITrustlineUpdatedEffect extends IBaseEffect { + asset: Asset; + limit: string; +} + +export interface ITrustlineAuthorizedEffect extends IBaseEffect { + trustor: AccountID; + asset: Asset; +} + +export interface ITrustlineDeauthorizedEffect extends IBaseEffect { + trustor: AccountID; + asset: Asset; +} + +export interface ITradeEffect extends IBaseEffect { + seller: AccountID; + offerId: string; + soldAmount: string; + soldAsset: Asset; + boughtAmount: string; + boughtAsset: Asset; +} + +export interface ISequenceBumpEffect extends IBaseEffect { + newSeq: number; +} + +export type Effect = + | IAccountCreatedEffect + | IAccountCreditedEffect + | IAccountDebitedEffect + | IAccountThresholdsUpdatedEffect + | IAccountThresholdsUpdatedEffect + | IAccountHomeDomainUpdatedEffect + | IAccountFlagsUpdatedEffect + | ISignerCreatedEffect + | ISignerCreatedEffect + | ISignerRemovedEffect + | ISignerUpdatedEffect + | ITrustlineCreatedEffect + | ITrustlineRemovedEffect + | ITrustlineUpdatedEffect + | ITrustlineAuthorizedEffect + | ITrustlineAuthorizedEffect + | ITrustlineDeauthorizedEffect + | ITradeEffect + | ISequenceBumpEffect; diff --git a/src/model/factories/asset_factory.ts b/src/model/factories/asset_factory.ts index fc719b6d..e69c1b8a 100644 --- a/src/model/factories/asset_factory.ts +++ b/src/model/factories/asset_factory.ts @@ -7,7 +7,7 @@ export class AssetFactory { return type === XDR.AssetType.assetTypeNative().value ? Asset.native() : new Asset(code, issuer); } - public static fromHorizonResponse(type: HorizonAssetType, code: string, issuer: string) { + public static fromHorizonResponse(type: HorizonAssetType, code?: string, issuer?: string) { return type === "native" ? Asset.native() : new Asset(code, issuer); } diff --git a/src/model/factories/effect_factory.ts b/src/model/factories/effect_factory.ts new file mode 100644 index 00000000..257c275e --- /dev/null +++ b/src/model/factories/effect_factory.ts @@ -0,0 +1,79 @@ +import { IHorizonEffectData } from "../../datasource/types"; +import { Effect, EffectKinds } from "../effect"; +import { AssetFactory } from "./asset_factory"; + +export class EffectFactory { + public static fromHorizon(data: IHorizonEffectData): Effect { + const baseData = { + id: data.id, + account: data.account, + kind: data.type.replace(/_([a-z])/g, g => g[1].toUpperCase()) as EffectKinds, + createdAt: new Date(data.created_at) + }; + + switch (baseData.kind) { + case EffectKinds.AccountCreated: + return { ...baseData, startingBalance: data.starting_balance }; + case EffectKinds.AccountCredited: + case EffectKinds.AccountDebited: + return { ...baseData, amount: data.amount }; + case EffectKinds.AccountThresholdsUpdated: + return { + ...baseData, + lowThreshold: data.low_threshold, + medThreshold: data.med_threshold, + highThreshold: data.high_threshold + }; + case EffectKinds.AccountHomeDomainUpdated: + return { ...baseData, homeDomain: data.home_domain }; + case EffectKinds.AccountFlagsUpdated: + return { ...baseData, authRequiredFlag: data.auth_required_flag, authRevokableFlag: data.auth_revokable_flag }; + case EffectKinds.SignerCreated: + case EffectKinds.SignerUpdated: + case EffectKinds.SignerRemoved: + return { + ...baseData, + weight: data.weight, + publicKey: data.public_key, + key: data.key + }; + case EffectKinds.TrustlineCreated: + case EffectKinds.TrustlineUpdated: + case EffectKinds.TrustlineRemoved: + return { + ...baseData, + asset: AssetFactory.fromHorizonResponse(data.asset_type, data.asset_code, data.asset_issuer), + limit: data.limit + }; + case EffectKinds.TrustlineAuthorized: + case EffectKinds.TrustlineDeauthorized: + return { + ...baseData, + asset: AssetFactory.fromHorizonResponse(data.asset_type, data.asset_code, data.asset_issuer), + trustor: data.trustor + }; + case EffectKinds.Trade: + return { + ...baseData, + seller: data.seller, + offerId: data.offer_id, + soldAmount: data.sold_amount, + soldAsset: AssetFactory.fromHorizonResponse( + data.sold_asset_type, + data.sold_asset_code, + data.sold_asset_issuer + ), + boughtAmount: data.bought_amount, + boughtAsset: AssetFactory.fromHorizonResponse( + data.bought_asset_type, + data.bought_asset_code, + data.bought_asset_issuer + ) + }; + case EffectKinds.SequenceBumped: + return { ...baseData, newSeq: data.new_seq }; + default: + return baseData; + } + } +} diff --git a/src/model/factories/index.ts b/src/model/factories/index.ts index c4ee0e92..1d6be298 100644 --- a/src/model/factories/index.ts +++ b/src/model/factories/index.ts @@ -5,6 +5,7 @@ export * from "./account_values_factory"; export * from "./asset_factory"; export * from "./data_entry_factory"; export * from "./data_entry_values_factory"; +export * from "./effect_factory"; export * from "./ledger_header_factory"; export * from "./offer_factory"; export * from "./offer_values_factory"; diff --git a/src/model/index.ts b/src/model/index.ts index c61d927d..94e75697 100644 --- a/src/model/index.ts +++ b/src/model/index.ts @@ -8,6 +8,7 @@ export * from "./asset_input"; export * from "./data_entry"; export * from "./data_entry_subscription_payload"; export * from "./data_entry_values"; +export * from "./effect"; export * from "./ledger"; export * from "./ledger_header"; export * from "./mutation_type"; diff --git a/src/schema/accounts.ts b/src/schema/accounts.ts index a3cabb27..cf60d0ba 100644 --- a/src/schema/accounts.ts +++ b/src/schema/accounts.ts @@ -40,6 +40,7 @@ export const typeDefs = gql` data: [DataEntry] trustLines: [TrustLine] operations(first: Int, after: String, last: Int, before: String): OperationConnection + effects(first: Int, after: String, last: Int, before: String): EffectConnection } type OperationConnection { diff --git a/src/schema/effects.ts b/src/schema/effects.ts new file mode 100644 index 00000000..97f66b88 --- /dev/null +++ b/src/schema/effects.ts @@ -0,0 +1,256 @@ +import { gql } from "apollo-server"; + +export const typeDefs = gql` + enum EffectKind { + accountCreated + accountRemoved + accountCredited + accountDebited + accountThresholdsUpdated + accountHomeDomainUpdated + accountFlagsUpdated + accountInflationDestinationUpdated + signerCreated + signerRemoved + signerUpdated + trustlineCreated + trustlineRemoved + trustlineUpdated + trustlineAuthorized + trustlineDeauthorized + offerCreated + offerRemoved + offerUpdated + trade + dataCreated + dataRemoved + dataUpdated + sequenceBumped + } + + interface Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + } + + type AccountCreatedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + startingBalance: String! + } + + type AccountRemovedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + } + + type AccountCreditedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + amount: String! + } + + type AccountDebitedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + amount: String! + } + + type AccountThresholdsUpdatedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + lowThreshold: Int! + medThreshold: Int! + highThreshold: Int! + } + + type AccountHomeDomainUpdatedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + homeDomain: String! + } + + type AccountFlagsUpdatedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + authRequiredFlag: Boolean + authRevokableFlag: Boolean + } + + type AccountInflationDestinationUpdatedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + } + + type SequenceBumpedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + newSeq: Int! + } + + type SignerCreatedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + weight: Int! + publicKey: AccountID! + key: AccountID! + } + + type SignerRemovedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + weight: Int! + publicKey: AccountID! + key: AccountID! + } + + type SignerUpdatedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + weight: Int! + publicKey: AccountID! + key: AccountID! + } + + type TrustlineCreatedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + asset: Asset! + limit: String! + } + + type TrustlineRemovedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + asset: Asset! + limit: String! + } + + type TrustlineUpdatedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + asset: Asset! + limit: String! + } + + type TrustlineAuthorizedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + trustor: AccountID! + asset: Asset! + } + + type TrustlineDeauthorizedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + trustor: AccountID! + asset: Asset! + } + + type OfferCreatedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + } + + type OfferRemovedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + } + + type OfferUpdatedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + } + + type TradeEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + seller: AccountID! + offerId: OfferID! + soldAmount: String! + soldAsset: Asset! + boughtAmount: String! + boughtAsset: Asset! + } + + type DataCreatedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + } + + type DataUpdatedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + } + + type DataRemovedEffect implements Effect { + id: String! + account: Account! + kind: EffectKind! + createdAt: DateTime! + } + + type EffectConnection { + pageInfo: PageInfo! + nodes: [Effect] + edges: [EffectEdge] + } + + type EffectEdge { + cursor: String! + node: Effect + } + + extend type Query { + effects(first: Int, last: Int, after: String, before: String): EffectConnection + } + +`; diff --git a/src/schema/index.ts b/src/schema/index.ts index af85c6c6..1f4afb3f 100644 --- a/src/schema/index.ts +++ b/src/schema/index.ts @@ -4,6 +4,7 @@ import resolvers from "./resolvers"; import { typeDefs as accountsTypeDefs } from "./accounts"; import { typeDefs as assetsTypeDefs } from "./assets"; +import { typeDefs as effectsTypeDefs } from "./effects"; import { typeDefs as ledgerTypeDefs } from "./ledgers"; import { typeDefs as offersTypeDefs } from "./offers"; import { typeDefs as operationsTypeDefs } from "./operations"; @@ -16,12 +17,13 @@ import { typeDefs } from "./type_defs"; const schema = makeExecutableSchema({ typeDefs: [ typeDefs, - operationsTypeDefs, accountsTypeDefs, + effectsTypeDefs, assetsTypeDefs, ledgerTypeDefs, offersTypeDefs, transactionsTypeDefs, + operationsTypeDefs, orderBookTypeDefs, paymentPathTypeDefs, tradeAggregationsTypeDefs diff --git a/src/schema/ledgers.ts b/src/schema/ledgers.ts index bb7f70a2..46bbbb24 100644 --- a/src/schema/ledgers.ts +++ b/src/schema/ledgers.ts @@ -17,6 +17,7 @@ export const typeDefs = gql` header: LedgerHeader transactions(first: Int, last: Int, before: String, after: String): TransactionConnection operations(first: Int, last: Int, before: String, after: String): OperationConnection + effects(first: Int, last: Int, before: String, after: String): EffectConnection } extend type Query { diff --git a/src/schema/operations.ts b/src/schema/operations.ts index 4bb57ab5..e89f7828 100644 --- a/src/schema/operations.ts +++ b/src/schema/operations.ts @@ -1,6 +1,20 @@ import { gql } from "apollo-server"; export const typeDefs = gql` + enum OperationKind { + payment + setOption + accountMerge + allowTrust + bumpSequence + changeTrust + createAccount + manageDatum + manageOffer + createPassiveOffer + pathPayment + } + interface Operation { id: String! kind: OperationKind! diff --git a/src/schema/resolvers/account.ts b/src/schema/resolvers/account.ts index 50e298a5..610f697e 100644 --- a/src/schema/resolvers/account.ts +++ b/src/schema/resolvers/account.ts @@ -9,7 +9,14 @@ import { joinToMap } from "../../util/array"; import { ACCOUNT, pubsub } from "../../pubsub"; -import { accountResolver, createBatchResolver, eventMatches, ledgerResolver, operationsResolver } from "./util"; +import { + accountResolver, + createBatchResolver, + effectsResolver, + eventMatches, + ledgerResolver, + operationsResolver +} from "./util"; const dataEntriesResolver = createBatchResolver((source: any) => db.dataEntries.findAllByAccountIDs(_.map(source, "id")) @@ -55,6 +62,7 @@ export default { trustLines: trustLinesResolver, ledger: ledgerResolver, operations: operationsResolver, + effects: effectsResolver, inflationDestination: accountResolver }, Query: { diff --git a/src/schema/resolvers/effect.ts b/src/schema/resolvers/effect.ts new file mode 100644 index 00000000..8f8a188f --- /dev/null +++ b/src/schema/resolvers/effect.ts @@ -0,0 +1,96 @@ +import { IHorizonEffectData } from "../../datasource/types"; +import { Effect, EffectKinds } from "../../model"; +import { EffectFactory } from "../../model/factories"; +import { accountResolver } from "./util"; + +export default { + Effect: { + __resolveType(effect: Effect) { + switch (effect.kind) { + case EffectKinds.AccountCreated: + return "AccountCreatedEffect"; + case EffectKinds.AccountRemoved: + return "AccountRemovedEffect"; + case EffectKinds.AccountCredited: + return "AccountCreditedEffect"; + case EffectKinds.AccountDebited: + return "AccountDebitedEffect"; + case EffectKinds.AccountThresholdsUpdated: + return "AccountThresholdsUpdatedEffect"; + case EffectKinds.AccountHomeDomainUpdated: + return "AccountHomeDomainUpdatedEffect"; + case EffectKinds.AccountFlagsUpdated: + return "AccountFlagsUpdatedEffect"; + case EffectKinds.AccountInflationDestinationUpdated: + return "AccountInflationDestinationUpdatedEffect"; + case EffectKinds.SignerCreated: + return "SignerCreatedEffect"; + case EffectKinds.SignerRemoved: + return "SignerRemovedEffect"; + case EffectKinds.SignerUpdated: + return "SignerUpdatedEffect"; + case EffectKinds.TrustlineCreated: + return "TrustlineCreatedEffect"; + case EffectKinds.TrustlineRemoved: + return "TrustlineRemovedEffect"; + case EffectKinds.TrustlineUpdated: + return "TrustlineUpdatedEffect"; + case EffectKinds.TrustlineAuthorized: + return "TrustlineAuthorizedEffect"; + case EffectKinds.TrustlineDeauthorized: + return "TrustlineDeauthorizedEffect"; + case EffectKinds.OfferCreated: + return "OfferCreatedEffect"; + case EffectKinds.OfferRemoved: + return "OfferRemovedEffect"; + case EffectKinds.OfferUpdated: + return "OfferUpdatedEffect"; + case EffectKinds.Trade: + return "TradeEffect"; + case EffectKinds.DataCreated: + return "DataCreatedEffect"; + case EffectKinds.DataRemoved: + return "DataRemovedEffect"; + case EffectKinds.DataUpdated: + return "DataUpdatedEffect"; + case EffectKinds.SequenceBumped: + return "SequenceBumpedEffect"; + default: + return null; + } + }, + account: accountResolver + }, + Query: { + async effects(root: any, args: any, ctx: any, info: any) { + const { first, last, after, before } = args; + let records: IHorizonEffectData[] = await ctx.dataSources.horizon.getEffects( + first || last, + last ? "asc" : "desc", + last ? before : after + ); + + // we must keep descending ordering, because Horizon doesn't do it, + // when you request the previous page + if (last) { + records = records.reverse(); + } + + const edges = records.map(record => { + return { + node: EffectFactory.fromHorizon(record), + cursor: record.paging_token + }; + }); + + return { + nodes: edges.map(edge => edge.node), + edges, + pageInfo: { + startCursor: records.length !== 0 ? records[0].paging_token : null, + endCursor: records.length !== 0 ? records[records.length - 1].paging_token : null + } + }; + } + } +}; diff --git a/src/schema/resolvers/index.ts b/src/schema/resolvers/index.ts index f197297e..d84f2ae1 100644 --- a/src/schema/resolvers/index.ts +++ b/src/schema/resolvers/index.ts @@ -1,6 +1,7 @@ import accountResolvers from "./account"; import assetResolvers from "./asset"; import dataEntryResolvers from "./data_entry"; +import effectResolvers from "./effect"; import ledgerResolvers from "./ledger"; import offerResolvers from "./offer"; import operationResolvers from "./operation"; @@ -13,6 +14,7 @@ import trustLineResolvers from "./trust_line"; export default [ accountResolvers, + effectResolvers, assetResolvers, dataEntryResolvers, ledgerResolvers, diff --git a/src/schema/resolvers/ledger.ts b/src/schema/resolvers/ledger.ts index e35f1a37..e85bc9de 100644 --- a/src/schema/resolvers/ledger.ts +++ b/src/schema/resolvers/ledger.ts @@ -1,7 +1,7 @@ import { db } from "../../database"; import { Ledger, LedgerHeader } from "../../model"; import { pubsub } from "../../pubsub"; -import { createBatchResolver, operationsResolver, transactionsResolver } from "./util"; +import { createBatchResolver, effectsResolver, operationsResolver, transactionsResolver } from "./util"; const LEDGER_CREATED = "LEDGER_CREATED"; @@ -13,7 +13,8 @@ export default { Ledger: { header: ledgerHeaderResolver, transactions: transactionsResolver, - operations: operationsResolver + operations: operationsResolver, + effects: effectsResolver }, Query: { ledger(root: any, args: any, ctx: any, info: any) { diff --git a/src/schema/resolvers/transaction.ts b/src/schema/resolvers/transaction.ts index 7ef48e01..eaa67833 100644 --- a/src/schema/resolvers/transaction.ts +++ b/src/schema/resolvers/transaction.ts @@ -2,7 +2,7 @@ import { db } from "../../database"; import { IHorizonTransactionData } from "../../datasource/types"; import { Account, Transaction } from "../../model"; import { TransactionWithXDRFactory } from "../../model/factories"; -import { createBatchResolver, ledgerResolver, memoResolver, operationsResolver } from "./util"; +import { createBatchResolver, effectsResolver, ledgerResolver, memoResolver, operationsResolver } from "./util"; export default { Transaction: { @@ -11,7 +11,8 @@ export default { }), ledger: ledgerResolver, memo: memoResolver, - operations: operationsResolver + operations: operationsResolver, + effects: effectsResolver }, Query: { async transaction(root: any, args: any, ctx: any, info: any) { diff --git a/src/schema/resolvers/util.ts b/src/schema/resolvers/util.ts index edf3ff13..78dffe55 100644 --- a/src/schema/resolvers/util.ts +++ b/src/schema/resolvers/util.ts @@ -3,9 +3,9 @@ import { createBatchResolver as create } from "graphql-resolve-batch"; import { Asset, Memo } from "stellar-sdk"; import { db } from "../../database"; import HorizonAPI from "../../datasource/horizon"; -import { IHorizonOperationData, IHorizonTransactionData } from "../../datasource/types"; +import { IHorizonEffectData, IHorizonOperationData, IHorizonTransactionData } from "../../datasource/types"; import { Account, AccountID, Ledger, MutationType, Transaction } from "../../model"; -import { OperationFactory, TransactionWithXDRFactory } from "../../model/factories"; +import { EffectFactory, OperationFactory, TransactionWithXDRFactory } from "../../model/factories"; export function createBatchResolver(loadFn: any) { return create(async (source: ReadonlyArray, args: any, context: any, info: any) => @@ -109,6 +109,47 @@ export async function operationsResolver(obj: any, args: any, ctx: any) { }; } +export async function effectsResolver(obj: any, args: any, ctx: any) { + let records: IHorizonEffectData[]; + const dataSource: HorizonAPI = ctx.dataSources.horizon; + const { first, after, last, before } = args; + const pagingArgs = [first || last, last ? "asc" : "desc", last ? before : after]; + + if (obj instanceof Transaction) { + records = await dataSource.getTransactionEffects(obj.id, ...pagingArgs); + } else if (obj instanceof Account) { + records = await dataSource.getAccountEffects(obj.id, ...pagingArgs); + } else if (obj instanceof Ledger) { + records = await dataSource.getLedgerEffects(obj.id, ...pagingArgs); + } else if (obj === undefined) { + records = await dataSource.getEffects(...pagingArgs); + } else { + throw new Error(`Cannot fetch effects for ${obj.constructor}`); + } + + // we must keep descending ordering, because Horizon doesn't do it, + // when you request the previous page + if (last) { + records = records.reverse(); + } + + const edges = records.map(record => { + return { + node: EffectFactory.fromHorizon(record), + cursor: record.paging_token + }; + }); + + return { + nodes: edges.map(edge => edge.node), + edges, + pageInfo: { + startCursor: records.length !== 0 ? records[0].paging_token : null, + endCursor: records.length !== 0 ? records[records.length - 1].paging_token : null + } + }; +} + export async function transactionsResolver(obj: any, args: any, ctx: any) { let data: IHorizonTransactionData[]; const dataSource = ctx.dataSources.horizon; diff --git a/src/schema/transactions.ts b/src/schema/transactions.ts index e40cd181..5f2c7c35 100644 --- a/src/schema/transactions.ts +++ b/src/schema/transactions.ts @@ -15,6 +15,7 @@ export const typeDefs = gql` success: Boolean! resultCode: Int! operations(first: Int, after: String, last: Int, before: String): OperationConnection + effects(first: Int, after: String, last: Int, before: String): EffectConnection } type TransactionConnection { diff --git a/src/schema/type_defs.ts b/src/schema/type_defs.ts index 4aff9bcd..f336d19c 100644 --- a/src/schema/type_defs.ts +++ b/src/schema/type_defs.ts @@ -29,20 +29,6 @@ export const typeDefs = gql` REMOVE } - enum OperationKind { - payment - setOption - accountMerge - allowTrust - bumpSequence - changeTrust - createAccount - manageDatum - manageOffer - createPassiveOffer - pathPayment - } - type Memo { value: MemoValue type: MemoType!