diff --git a/src/infrastructure/ApostilleHttp.ts b/src/infrastructure/ApostilleHttp.ts index b98e282..000e85b 100644 --- a/src/infrastructure/ApostilleHttp.ts +++ b/src/infrastructure/ApostilleHttp.ts @@ -1,12 +1,16 @@ -import { Listener, PublicAccount, SignedTransaction, TransactionHttp } from 'nem2-sdk'; +import { sortBy } from 'lodash'; +import { AccountHttp, Address, AggregateTransaction, Listener, PublicAccount, QueryParams, SignedTransaction, Transaction, TransactionHttp, TransactionInfo, TransferTransaction } from 'nem2-sdk'; import { filter, mergeMap } from 'rxjs/operators'; +import { Errors } from '../types/Errors'; class ApostilleHttp { private transactionHttp: TransactionHttp; + private accountHttp: AccountHttp; - private constructor(url: string) { + public constructor(url: string) { this.transactionHttp = new TransactionHttp(url); + this.accountHttp = new AccountHttp(url); } public announce(signedTransaction: SignedTransaction): Promise { @@ -40,6 +44,188 @@ class ApostilleHttp { (err) => console.error(err)); }); } + + /** + * @description - cheks on chain if there are any transactions announced + * @returns {Promise} + * @memberof ApostilleAccount + */ + public async isCreated(publicAccount: PublicAccount): Promise { + try { + const unconfirmedTransactions = await this._unconfirmedTransactions(publicAccount); + if (unconfirmedTransactions.length) { + // the apostille has been sent to the network + return true; + } else { + // then check transactions + const transactions = await this._transactions(publicAccount); + if (transactions.length) { + return true; + } else { + return true; + } + } + } catch (err) { + throw new Error(err); + } + } + + /** + * @description - get cosignatories of the account + * @returns {Promise} + * @memberof ApostilleHttp + */ + public getCosignatories(address: Address): Promise { + return new Promise(async (resolve, reject) => { + this.accountHttp.getMultisigAccountInfo(address).subscribe( + (multisigAccountInfo) => { + resolve(multisigAccountInfo.cosignatories); + }, + (err) => reject(err), + ); + }); + } + + /** + * @description - get first transaction + * @static + * @returns {Promise} + * @memberof ApostilleHttp + */ + public async isOwned(address: Address): Promise { + try { + const cossignatories = await this.getCosignatories(address); + if (cossignatories.length > 0) { + return true; + } else { + return false; + } + } catch (error) { + const errorText = JSON.parse(error.response.text); + if (errorText.code === 'ResourceNotFound') { + return false; + } else { + throw new Error(error); + } + } + } + + /** + * @description - get creationTransaction Info + * @param {string} urls + * @returns {Promise} + * @memberof ApostilleAccount + */ + public async getCreationTransactionInfo(publicAccount: PublicAccount): Promise { + try { + const transaction: TransferTransaction = await this.getCreationTransaction(publicAccount); + if (transaction.transactionInfo instanceof TransactionInfo) { + return transaction.transactionInfo; + } else { + throw new Error(Errors[Errors.COULD_NOT_FOUND_TRANSACTION_INFO]); + } + } catch (error) { + throw new Error(error); + } + } + + /** + * @description - get first transaction + * @param {string} urls + * @returns {Promise} + * @memberof ApostilleAccount + */ + public getCreationTransaction(publicAccount: PublicAccount): Promise { + return new Promise((resolve, reject) => { + this._fetchAllTransactions(publicAccount).then((transactions: Transaction[]) => { + if (transactions.length > 0) { + const firstTransaction = transactions[transactions.length - 1]; + if (firstTransaction instanceof TransferTransaction) { + resolve (firstTransaction); + } else if (firstTransaction instanceof AggregateTransaction) { + // if the smallest index is aggregate transaction, then sort it by index + const innerTransactions = firstTransaction.innerTransactions; + const sortedInnerTransactions = sortBy( + innerTransactions, ['transactionInfo.index']); + const firstInnerTransaction = sortedInnerTransactions[0]; + if (firstInnerTransaction instanceof TransferTransaction) { + resolve (firstInnerTransaction); + } else { + reject (Errors[Errors.CREATION_TRANSACTIONS_NOT_FOUND]); + } + } else { + reject (Errors[Errors.CREATION_TRANSACTIONS_NOT_FOUND]); + } + } else { + reject (Errors[Errors.CREATION_TRANSACTIONS_NOT_FOUND]); + } + }); + }); + } + + /** + * Fetch all transactions pertaining to this publicAccount + * + * @returns {Promise} + * @memberof CertificateHistory + */ + public _fetchAllTransactions(publicAccount: PublicAccount): Promise { + return new Promise(async (resolve, reject) => { + let nextId: string = ''; + const pageSize: number = 100; + let lastPageSize: number = 100; + const allTransactions: Transaction[] = []; + while (lastPageSize === pageSize) { + const queryParams = new QueryParams(pageSize, nextId !== '' ? nextId : undefined); + await this._transactions(publicAccount, queryParams).then((transactions) => { + lastPageSize = transactions.length; + if (lastPageSize < 1) { return; } + nextId = transactions[transactions.length - 1].transactionInfo!.id; + allTransactions.push(...transactions); + }).catch((err) => { + reject(err); + }); + } + resolve(allTransactions); + }); + } + + private _unconfirmedTransactions( + publicAccount: PublicAccount, + queryParams?: QueryParams | undefined): Promise { + + return new Promise((resolve, reject) => { + this.accountHttp.unconfirmedTransactions( + publicAccount, + queryParams, + ).subscribe((unconfirmedTransactions) => { + resolve(unconfirmedTransactions); + }, (err) => { + // network or comunication problem + resolve(err); + }); + }); + + } + + private _transactions( + publicAccount: PublicAccount, + queryParams?: QueryParams | undefined): Promise { + + return new Promise((resolve, reject) => { + this.accountHttp.transactions( + publicAccount, + queryParams, + ).subscribe((transactions) => { + resolve(transactions); + }, (err) => { + // network or comunication problem + resolve(err); + }); + }); + + } + } export { ApostilleHttp }; diff --git a/src/model/apostille/ApostillePublicAccount.ts b/src/model/apostille/ApostillePublicAccount.ts index d74caed..d1e0d69 100644 --- a/src/model/apostille/ApostillePublicAccount.ts +++ b/src/model/apostille/ApostillePublicAccount.ts @@ -1,5 +1,5 @@ import { drop } from 'lodash'; -import { Account, AccountHttp, AggregateTransaction, Deadline, LockFundsTransaction, ModifyMultisigAccountTransaction, Mosaic, MultisigCosignatoryModification, MultisigCosignatoryModificationType, PlainMessage, PublicAccount, QueryParams, SignedTransaction, TransferTransaction, UInt64, XEM } from 'nem2-sdk'; +import { Account, AggregateTransaction, Deadline, LockFundsTransaction, ModifyMultisigAccountTransaction, Mosaic, MultisigCosignatoryModification, MultisigCosignatoryModificationType, PlainMessage, PublicAccount, SignedTransaction, TransferTransaction, UInt64, XEM } from 'nem2-sdk'; import { HashFunction } from '../../hash/HashFunction'; import { Errors } from '../../types/Errors'; @@ -187,242 +187,6 @@ export class ApostillePublicAccount { return signedLock; } - /** - * @description - cheks on chain if there are any transactions announced - * @returns {Promise} - * @memberof ApostilleAccount - */ - public isCreated(url: string): Promise { - // check if the apostille account has any transaction - const accountHttp = new AccountHttp(url); - return new Promise(async (resolve, reject) => { - // we need to check unconfirmed transactions first - await accountHttp.unconfirmedTransactions( - this.publicAccount, - new QueryParams(10), - ).subscribe( - async (unconfirmedTransactions) => { - if (unconfirmedTransactions.length) { - // the apostille has been sent to the network - resolve(true); - } else { - // then check transactions - await accountHttp.transactions( - this.publicAccount, - new QueryParams(10), - ).subscribe((transactions) => { - // if there is then the apostille has been created - if (transactions.length) { - resolve(true); - } else { - resolve(false); - } - }, - (err) => { - // network or comunication problem - throw new Error(err.message); - }); - } - }, - (err) => { - // network or comunication problem - throw new Error(err.message); - }, - ); - }); - } - - /** - * @description - get first transaction - * @static - * @returns {Promise} - * @memberof ApostilleAccount - */ - public async isOwned(url: string): Promise { - try { - const cossignatories = await this.getCosignatories(url); - if (cossignatories.length > 0) { - return true; - } else { - return false; - } - } catch (error) { - const errorText = JSON.parse(error.response.text); - if (errorText.code === 'ResourceNotFound') { - return false; - } else { - throw new Error(error); - } - } - } - - /** - * @description - get cosignatories of the account - * @returns {Promise} - * @memberof ApostillePublicAccount - */ - public getCosignatories(url: string): Promise { - const accountHttp = new AccountHttp(url); - - return new Promise(async (resolve, reject) => { - accountHttp.getMultisigAccountInfo(this.publicAccount.address).subscribe( - (multisigAccountInfo) => { - resolve(multisigAccountInfo.cosignatories); - }, - (err) => reject(err), - ); - }); - } - - // /** - // * @description - get Transaction by ID - // * @param {string} transactionID - // * @returns {Observable} - // * @memberof ApostilleAccount - // */ - // public getTransactionById(transactionID: string, urls?: string): Promise { - // let transactionHttp: TransactionHttp; - // if (urls) { - // transactionHttp = new TransactionHttp(urls); - // } else { - // if (this.publicAccount.address.networkType === NetworkType.MIJIN) { - // throw new Error(Errors[Errors.MIJIN_ENDPOINT_NEEDED]); - // } - // transactionHttp = new TransactionHttp(HistoricalEndpoints[this.publicAccount.address.networkType]); - // } - - // return new Promise((resolve, reject) => { - // transactionHttp.getTransaction(transactionID) - // .subscribe((transaction: Transaction) => { - // resolve(transaction); - // }, (err) => { - // reject(err); - // }); - // }); - // } - - // /** - // * @description - get creationTransaction Info - // * @param {string} urls - // * @returns {Promise} - // * @memberof ApostilleAccount - // */ - // public async getCreationTransactionInfo(urls?: string): Promise { - // const transaction: TransferTransaction = await this.getCreationTransaction(urls); - // if (transaction.transactionInfo instanceof TransactionInfo) { - // return transaction.transactionInfo; - // } - // throw new Error(Errors[Errors.COULD_NOT_FOUND_TRANSACTION_INFO]); - // } - - // /** - // * @description - get first transaction - // * @param {string} urls - // * @returns {Promise} - // * @memberof ApostilleAccount - // */ - // public getCreationTransaction(urls?: string): Promise { - // let fixUrl: string; - - // if (urls) { - // fixUrl = urls; - // } else { - // fixUrl = HistoricalEndpoints[this.publicAccount.address.networkType]; - // } - - // return new Promise((resolve, reject) => { - // this.fetchAllIncomingTransactions(fixUrl).then((transactions: Transaction[]) => { - // if (transactions.length > 0) { - // const firstTransaction = transactions[transactions.length - 1]; - // if (firstTransaction instanceof TransferTransaction) { - // resolve (firstTransaction); - // } else if (firstTransaction instanceof AggregateTransaction) { - // // if the smallest index is aggregate transaction, then sort it by index - // const innerTransactions = firstTransaction.innerTransactions; - // const sortedInnerTransactions = sortBy( - // innerTransactions, ['transactionInfo.index']); - // const firstInnerTransaction = sortedInnerTransactions[0]; - // if (firstInnerTransaction instanceof TransferTransaction) { - // resolve (firstInnerTransaction); - // } else { - // reject (Errors[Errors.CREATION_TRANSACTIONS_NOT_FOUND]); - // } - // } else { - // reject (Errors[Errors.CREATION_TRANSACTIONS_NOT_FOUND]); - // } - // } else { - // reject (Errors[Errors.CREATION_TRANSACTIONS_NOT_FOUND]); - // } - // }); - // }); - // } - - // /** - // * Fetch all incoming transactions pertaining to this publicAccount - // * - // * @returns {Promise} - // * @memberof CertificateHistory - // */ - // public fetchAllIncomingTransactions(urls?: string): Promise { - // let fixUrls: string; - - // if (urls) { - // fixUrls = urls; - // } else { - // fixUrls = HistoricalEndpoints[this.publicAccount.address.networkType]; - // } - // return new Promise(async (resolve, reject) => { - // let nextId: string = ''; - // const pageSize: number = 100; - // let lastPageSize: number = 100; - // const allTransactions: Transaction[] = []; - // while (lastPageSize === pageSize) { - // const queryParams = new QueryParams(pageSize, nextId !== '' ? nextId : undefined); - // await this.fetchTransactions(queryParams, fixUrls).then((transactions) => { - // lastPageSize = transactions.length; - // if (lastPageSize < 1) { return; } - // nextId = transactions[transactions.length - 1].transactionInfo!.id; - // allTransactions.push(...transactions); - // }).catch((err) => { - // reject(err); - // }); - // } - // resolve(allTransactions); - // }); - // } - - // /** - // * Fetch transaction with query params - // * - // * @private - // * @param {QueryParams} queryParams - // * @returns {Promise} - // * @memberof CertificateHistory - // */ - // public async fetchTransactions( - // queryParams: QueryParams, - // urls?: string, - // ): Promise { - // let fixUrls: string; - - // if (urls) { - // fixUrls = urls; - // } else { - // fixUrls = HistoricalEndpoints[this.publicAccount.address.networkType]; - // } - // const accountHttp = new AccountHttp(fixUrls); - // return await new Promise((resolve, reject) => { - // accountHttp.transactions( - // this.publicAccount, - // queryParams) - // .subscribe((transactions: Transaction[]) => { - // resolve(transactions); - // }, (err) => { - // reject(err); - // }); - // }); - // } - private _signAggregate(aggregateTransaction: AggregateTransaction, signers: Account[]): SignedTransaction { // fetch the first signer const mainSigner = signers[0]; diff --git a/tests/unit/infrastructure/ApostilleHttp.spec.ts b/tests/unit/infrastructure/ApostilleHttp.spec.ts new file mode 100644 index 0000000..e40c247 --- /dev/null +++ b/tests/unit/infrastructure/ApostilleHttp.spec.ts @@ -0,0 +1,89 @@ +import { NetworkType, PublicAccount, TransactionInfo, TransferTransaction } from 'nem2-sdk'; +import { ApostilleHttp } from '../../../src/infrastructure/ApostilleHttp'; +import { ApostillePublicAccount } from '../../../src/model/apostille/ApostillePublicAccount'; +import { HistoricalEndpoints } from '../../../src/model/repository/HistoricalEndpoints'; +import { Errors } from '../../../src/types/Errors'; + +const getPublicApostilleAccount = ((publicKey: string, networkType: NetworkType): ApostillePublicAccount => { + // Account 1 + const publicAccount = PublicAccount.createFromPublicKey( + publicKey, + networkType, + ); + return new ApostillePublicAccount(publicAccount); +}); + +describe('apostille public account non transaction methods should work properly', () => { + const apostilleHttp = new ApostilleHttp(HistoricalEndpoints[NetworkType.MIJIN_TEST]); + // Apostille Public Account1 1 + const apostillePublicAccount1 = getPublicApostilleAccount( + 'E15CAB00A5A34216A8A29034F950A18DFC6F4F27BCCFBF9779DC6886653B7E56', + NetworkType.MIJIN_TEST); + + // Apostille Public Account1 2 + const apostillePublicAccount2 = getPublicApostilleAccount( + '67FD8C18BAACED8777EBF483B596D6BE0F93EDB2084FA39968DF8D2D96400E08', + NetworkType.MIJIN_TEST); + + // Apostille Public Account 3 + const apostillePublicAccount3 = getPublicApostilleAccount( + '901C9D46840BB74F4649EF3AF65A910A9F162DFA0FD5AD7E2739E5B82C2579F0', + NetworkType.MIJIN_TEST); + + // Apostille Public Account 4 + const apostillePublicAccount4 = getPublicApostilleAccount( + '95361ED8C94048BD5B0BDB229C19DF817DB7D66B59F4162E3F3A1D0D813B2AB9', + NetworkType.MIJIN_TEST); + + it(' should return 2 cosignataries of the accounts', () => { + return apostilleHttp.getCosignatories(apostillePublicAccount1.publicAccount.address).then((data) => { + expect(data.length).toEqual(2); + }); + }); + + it('Should return true if the account is claimed', () => { + return apostilleHttp.isOwned(apostillePublicAccount1.publicAccount.address).then((data) => { + expect(data).toBeTruthy(); + }); + }); + + it('Should return false if the account is not claimed', () => { + return apostilleHttp.isOwned(apostillePublicAccount2.publicAccount.address).then((data) => { + expect(data).toBeFalsy(); + }); + }); + + // TODO: Move this test to luxtag SDK + it('Should return creation transaction when it is transfer transaction', () => { + return apostilleHttp.getCreationTransaction( + apostillePublicAccount3.publicAccount).then((data: TransferTransaction) => { + expect(data.message.payload).toEqual(''); + }); + }); + + // TODO: Move this test to luxtag SDK + it('Should return creation transaction when the it is an aggregate complete transaction', () => { + return apostilleHttp.getCreationTransaction( + apostillePublicAccount1.publicAccount).then((data: TransferTransaction) => { + expect(data.message.payload).toEqual('I am really really awesomeee'); + }); + }); + + // TODO: Move this test to luxtag SDK + it('Throws error if there is no first transactions', () => { + return apostilleHttp.getCreationTransaction( + apostillePublicAccount4.publicAccount).then((data: TransferTransaction) => { + console.log(TransferTransaction); + }).catch((err) => { + expect(err).toEqual(Errors[Errors.CREATION_TRANSACTIONS_NOT_FOUND]); + }); + }); + + it('Should return creation transaction info', () => { + return apostilleHttp.getCreationTransactionInfo( + apostillePublicAccount1.publicAccount).then((transactionInfo: TransactionInfo) => { + expect(transactionInfo.id).toEqual('5B160E18C60E680001790BA2'); + }); + }); + +}); diff --git a/tests/unit/model/apostille/ApostillePublicAccount.spec.ts b/tests/unit/model/apostille/ApostillePublicAccount.spec.ts index 2eef543..7e820c8 100644 --- a/tests/unit/model/apostille/ApostillePublicAccount.spec.ts +++ b/tests/unit/model/apostille/ApostillePublicAccount.spec.ts @@ -1,7 +1,6 @@ import { Account, ModifyMultisigAccountTransaction, NetworkType, PublicAccount, SignedTransaction, TransferTransaction } from 'nem2-sdk'; import { SHA256 } from '../../../../src/hash/sha256'; import { ApostillePublicAccount } from '../../../../src/model/apostille/ApostillePublicAccount'; -import { HistoricalEndpoints } from '../../../../src/model/repository/HistoricalEndpoints'; const getPublicApostilleAccount = ((publicKey: string, networkType: NetworkType): ApostillePublicAccount => { // Account 1 @@ -151,82 +150,3 @@ describe('apostille public account transaction methods should work properly', () expect(signedLockFundsTransaction.signer).toMatch(signer.publicKey); }); }); - -describe('apostille public account non transaction methods should work properly', () => { - // Apostille Public Account1 1 - const apostillePublicAccount1 = getPublicApostilleAccount( - 'E15CAB00A5A34216A8A29034F950A18DFC6F4F27BCCFBF9779DC6886653B7E56', - NetworkType.MIJIN_TEST); - - // Apostille Public Account1 2 - const apostillePublicAccount2 = getPublicApostilleAccount( - '67FD8C18BAACED8777EBF483B596D6BE0F93EDB2084FA39968DF8D2D96400E08', - NetworkType.MIJIN_TEST); - - // Apostille Public Account1 3 - // const apostillePublicAccount3 = getPublicApostilleAccount( - // '901C9D46840BB74F4649EF3AF65A910A9F162DFA0FD5AD7E2739E5B82C2579F0', - // NetworkType.MIJIN_TEST); - - // Apostille Public Account1 4 - // const apostillePublicAccount4 = getPublicApostilleAccount( - // '95361ED8C94048BD5B0BDB229C19DF817DB7D66B59F4162E3F3A1D0D813B2AB9', - // NetworkType.MIJIN_TEST); - - it(' should return 2 cosignataries of the accounts', () => { - return apostillePublicAccount1.getCosignatories(HistoricalEndpoints[NetworkType.MIJIN_TEST]).then((data) => { - expect(data.length).toEqual(2); - }); - }); - - it('Should return true if the account is claimed', () => { - return apostillePublicAccount1.isOwned(HistoricalEndpoints[NetworkType.MIJIN_TEST]).then((data) => { - expect(data).toBeTruthy(); - }); - }); - - it('Should return false if the account is not claimed', () => { - return apostillePublicAccount2.isOwned(HistoricalEndpoints[NetworkType.MIJIN_TEST]).then((data) => { - expect(data).toBeFalsy(); - }); - }); - - // TODO: Move this test to luxtag SDK - // it('Should return creation transaction when it is transfer transaction', () => { - // return apostillePublicAccount3.getCreationTransaction().then((data: TransferTransaction) => { - // expect(data.message.payload).toEqual(''); - // }); - // }); - - // TODO: Move this test to luxtag SDK - // it('Should return creation transaction when the it is an aggregate complete transaction', () => { - // return apostillePublicAccount1.getCreationTransaction().then((data: TransferTransaction) => { - // expect(data.message.payload).toEqual('I am really really awesomeee'); - // }); - // }); - - // TODO: Move this test to luxtag SDK - // it('Throws error if there is no first transactions', () => { - // return apostillePublicAccount4.getCreationTransaction().then((data: TransferTransaction) => { - // console.log(TransferTransaction); - // }).catch((err) => { - // expect(err).toEqual(Errors[Errors.CREATION_TRANSACTIONS_NOT_FOUND]); - // }); - // }); - - // it('Should return creation transaction info', () => { - // return apostillePublicAccount1.getCreationTransactionInfo().then((transactionInfo: TransactionInfo) => { - // expect(transactionInfo.id).toEqual('5B160E18C60E680001790BA2'); - // }); - // }); - - // Skipped because weird error by nem2 SDK - // it.skip('returns correct transaction by ID', async () => { - // const transaction = await apostillePublicAccount1.getTransactionById(transactionID); - - // if (transaction.transactionInfo) { - // expect(transaction.transactionInfo.id).toEqual(transactionID); - // } - // }); - -});