From cf1a66576ca05a7cfa9d173fd3fac5ada1d82093 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Tue, 14 Jul 2020 20:15:57 +0100 Subject: [PATCH 1/5] Fixed #627 - Added account & namespace pagination --- e2e/infrastructure/NamespaceHttp.spec.ts | 35 +++++---- e2e/service/AccountService.spec.ts | 2 +- package-lock.json | 6 +- package.json | 2 +- src/infrastructure/AccountHttp.ts | 22 ++++++ src/infrastructure/AccountRepository.ts | 4 +- src/infrastructure/BlockHttp.ts | 2 +- src/infrastructure/NamespaceHttp.ts | 74 +++++++++---------- src/infrastructure/NamespaceRepository.ts | 23 +----- .../AccountPaginationStreamer.ts | 34 +++++++++ .../NamespacePaginationStreamer.ts | 34 +++++++++ .../searchCriteria/AccountOrderBy.ts | 20 +++++ .../searchCriteria/AccountSearchCriteria.ts | 35 +++++++++ .../searchCriteria/NamespaceSearchCriteria.ts | 47 ++++++++++++ src/service/AccountService.ts | 14 ++-- src/service/NamespaceService.ts | 2 +- src/service/interfaces/IAccountService.ts | 4 +- test/core/utils/EnumMapping.spec.ts | 37 +++++++++- test/infrastructure/AccountHttp.spec.ts | 20 +++++ test/infrastructure/NamespaceHttp.spec.ts | 48 +++++++----- .../AccountPaginationStreamer.spec.ts | 57 ++++++++++++++ .../NamespacePaginationStreamer.spec.ts | 57 ++++++++++++++ test/service/AccountService.spec.ts | 25 +++++-- test/service/NamespaceService.spec.ts | 10 +-- 24 files changed, 492 insertions(+), 122 deletions(-) create mode 100644 src/infrastructure/paginationStreamer/AccountPaginationStreamer.ts create mode 100644 src/infrastructure/paginationStreamer/NamespacePaginationStreamer.ts create mode 100644 src/infrastructure/searchCriteria/AccountOrderBy.ts create mode 100644 src/infrastructure/searchCriteria/AccountSearchCriteria.ts create mode 100644 src/infrastructure/searchCriteria/NamespaceSearchCriteria.ts create mode 100644 test/infrastructure/streamer/AccountPaginationStreamer.spec.ts create mode 100644 test/infrastructure/streamer/NamespacePaginationStreamer.spec.ts diff --git a/e2e/infrastructure/NamespaceHttp.spec.ts b/e2e/infrastructure/NamespaceHttp.spec.ts index 3651621c22..011f309362 100644 --- a/e2e/infrastructure/NamespaceHttp.spec.ts +++ b/e2e/infrastructure/NamespaceHttp.spec.ts @@ -25,6 +25,8 @@ import { Deadline } from '../../src/model/transaction/Deadline'; import { NamespaceRegistrationTransaction } from '../../src/model/transaction/NamespaceRegistrationTransaction'; import { UInt64 } from '../../src/model/UInt64'; import { IntegrationTestHelper } from './IntegrationTestHelper'; +import { NamespacePaginationStreamer } from '../../src/infrastructure/paginationStreamer/NamespacePaginationStreamer'; +import { take, toArray } from 'rxjs/operators'; describe('NamespaceHttp', () => { let defaultNamespaceId: NamespaceId; @@ -90,23 +92,9 @@ describe('NamespaceHttp', () => { }); }); - describe('getNamespacesFromAccount', () => { - it('should return namespace data given publicKeyNemesis', async () => { - const namespaces = await namespaceRepository.getNamespacesFromAccount(account.address).toPromise(); - deepEqual(namespaces[0].ownerAddress, account.address); - }); - }); - - describe('getNamespacesFromAccounts', () => { - it('should return namespaces data given publicKeyNemesis', async () => { - const namespaces = await namespaceRepository.getNamespacesFromAccounts([account.address]).toPromise(); - deepEqual(namespaces[0].ownerAddress, account.address); - }); - }); - describe('getNamespacesName', () => { it('should return namespace name given array of namespaceIds', async () => { - const namespaceNames = await namespaceRepository.getNamespacesName([defaultNamespaceId]).toPromise(); + const namespaceNames = await namespaceRepository.getNamespacesNames([defaultNamespaceId]).toPromise(); expect(namespaceNames[0].name).to.be.equal('currency'); }); }); @@ -124,4 +112,21 @@ describe('NamespaceHttp', () => { expect(address.plain()).to.be.equal(account.address.plain()); }); }); + + describe('searchNamespace', () => { + it('should return namespace info', async () => { + const info = await namespaceRepository.search({}).toPromise(); + expect(info.data.length).to.be.greaterThan(0); + }); + }); + + describe('searchNamespace with streamer', () => { + it('should return namespace info', async () => { + const streamer = new NamespacePaginationStreamer(namespaceRepository); + const infoStreamer = await streamer.search({ pageSize: 20 }).pipe(take(20), toArray()).toPromise(); + const info = await namespaceRepository.search({ pageSize: 20 }).toPromise(); + expect(infoStreamer.length).to.be.greaterThan(0); + deepEqual(infoStreamer, info.data); + }); + }); }); diff --git a/e2e/service/AccountService.spec.ts b/e2e/service/AccountService.spec.ts index d1ba727ca7..4850b419da 100644 --- a/e2e/service/AccountService.spec.ts +++ b/e2e/service/AccountService.spec.ts @@ -85,7 +85,7 @@ describe('AccountService', () => { describe('call accountNamespacesWithName', () => { it('accountNamespacesWithName', async () => { - const info = await accountService.accountNamespacesWithName([account.address]).toPromise(); + const info = await accountService.accountNamespacesWithName(account.address).toPromise(); expect(info).to.not.be.undefined; expect(info.find((i) => i.id.equals(namespaceId))).to.not.be.undefined; expect(info.find((i) => i.id.equals(namespaceId))?.namespaceName).to.be.equal(name); diff --git a/package-lock.json b/package-lock.json index 230c0bbbf6..743d2cad0b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5965,9 +5965,9 @@ } }, "symbol-openapi-typescript-fetch-client": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/symbol-openapi-typescript-fetch-client/-/symbol-openapi-typescript-fetch-client-0.9.4.tgz", - "integrity": "sha512-1HSnSRbxPeKMokQop2+t2tsinkjpfOhxLRXXn0OW3jAusU1r0fKRD0Izscl+cOqOpz08FEZSjUWvbnj9ePd3mQ==" + "version": "0.9.5-SNAPSHOT.202007141039", + "resolved": "https://registry.npmjs.org/symbol-openapi-typescript-fetch-client/-/symbol-openapi-typescript-fetch-client-0.9.5-SNAPSHOT.202007141039.tgz", + "integrity": "sha512-p+mdUNoCbAhYx+h7hixuTWQwfmvW9WON0uAqrkY0O1s3OSXzJCKLZ1YwIhEh6QVbglBKbPtazgIsKxSvJYmD/Q==" }, "table": { "version": "5.4.6", diff --git a/package.json b/package.json index 0ceb74252e..9fd20fcab0 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "ripemd160": "^2.0.2", "rxjs": "^6.5.3", "rxjs-compat": "^6.5.3", - "symbol-openapi-typescript-fetch-client": "0.9.4", + "symbol-openapi-typescript-fetch-client": "0.9.5-SNAPSHOT.202007141039", "tweetnacl": "^1.0.3", "utf8": "^3.0.0", "ws": "^7.2.3" diff --git a/src/infrastructure/AccountHttp.ts b/src/infrastructure/AccountHttp.ts index ae7ce99581..a02e4629ce 100644 --- a/src/infrastructure/AccountHttp.ts +++ b/src/infrastructure/AccountHttp.ts @@ -27,6 +27,9 @@ import { Http } from './Http'; import { SupplementalPublicKeys } from '../model/account/SupplementalPublicKeys'; import { AccountLinkPublicKey } from '../model/account/AccountLinkPublicKey'; import { AccountLinkVotingKey } from '../model/account/AccountLinkVotingKey'; +import { AccountSearchCriteria } from './searchCriteria/AccountSearchCriteria'; +import { DtoMapping } from '../core/utils/DtoMapping'; +import { Page } from './Page'; /** * Account http repository. @@ -71,6 +74,25 @@ export class AccountHttp extends Http implements AccountRepository { return this.call(this.accountRoutesApi.getAccountsInfo(accountIds), (body) => body.map(this.toAccountInfo)); } + /** + * Gets an array of accounts. + * @param criteria - Account search criteria + * @returns Observable + */ + public search(criteria: AccountSearchCriteria): Observable> { + return this.call( + this.accountRoutesApi.searchAccounts( + criteria.pageSize, + criteria.pageNumber, + criteria.offset, + DtoMapping.mapEnum(criteria.order), + DtoMapping.mapEnum(criteria.orderBy), + criteria.mosaicId?.toHex(), + ), + (body) => super.toPage(body.pagination, body.data, this.toAccountInfo), + ); + } + /** * This method maps a AccountInfoDTO from rest to the SDK's AccountInfo model object. * diff --git a/src/infrastructure/AccountRepository.ts b/src/infrastructure/AccountRepository.ts index 9b2e7b47d7..9f3b1a21bb 100644 --- a/src/infrastructure/AccountRepository.ts +++ b/src/infrastructure/AccountRepository.ts @@ -17,13 +17,15 @@ import { Observable } from 'rxjs'; import { AccountInfo } from '../model/account/AccountInfo'; import { Address } from '../model/account/Address'; +import { Searcher } from './paginationStreamer/Searcher'; +import { AccountSearchCriteria } from './searchCriteria/AccountSearchCriteria'; /** * Account interface repository. * * @since 1.0 */ -export interface AccountRepository { +export interface AccountRepository extends Searcher { /** * Gets an AccountInfo for an account. * @param address Address diff --git a/src/infrastructure/BlockHttp.ts b/src/infrastructure/BlockHttp.ts index 45e67b339c..99373f4f2a 100644 --- a/src/infrastructure/BlockHttp.ts +++ b/src/infrastructure/BlockHttp.ts @@ -60,7 +60,7 @@ export class BlockHttp extends Http implements BlockRepository { } /** - * Gets an array of bocks. + * Gets an array of blocks. * @param criteria - Block search criteria * @returns Observable */ diff --git a/src/infrastructure/NamespaceHttp.ts b/src/infrastructure/NamespaceHttp.ts index 8e6486ec0c..31fb323b7e 100644 --- a/src/infrastructure/NamespaceHttp.ts +++ b/src/infrastructure/NamespaceHttp.ts @@ -33,7 +33,9 @@ import { NetworkType } from '../model/network/NetworkType'; import { UInt64 } from '../model/UInt64'; import { Http } from './Http'; import { NamespaceRepository } from './NamespaceRepository'; -import { QueryParams } from './QueryParams'; +import { NamespaceSearchCriteria } from './searchCriteria/NamespaceSearchCriteria'; +import { Page } from './Page'; +import { DtoMapping } from '../core/utils/DtoMapping'; /** * Namespace http repository. @@ -119,44 +121,12 @@ export class NamespaceHttp extends Http implements NamespaceRepository { return this.call(this.namespaceRoutesApi.getNamespace(namespaceId.toHex()), (body) => this.toNamespaceInfo(body)); } - /** - * Gets array of NamespaceInfo for an account - * @param address - Address - * @param queryParams - (Optional) Query params - * @returns Observable - */ - public getNamespacesFromAccount(address: Address, queryParams?: QueryParams): Observable { - return this.call( - this.namespaceRoutesApi.getNamespacesFromAccount( - address.plain(), - this.queryParams(queryParams).pageSize, - this.queryParams(queryParams).id, - ), - (body) => body.namespaces.map((namespaceInfoDTO) => this.toNamespaceInfo(namespaceInfoDTO)), - ); - } - - /** - * Gets array of NamespaceInfo for different account - * @param addresses - Array of Address - * @param queryParams - (Optional) Query params - * @returns Observable - */ - public getNamespacesFromAccounts(addresses: Address[]): Observable { - const publicKeysBody = { - addresses: addresses.map((address) => address.plain()), - }; - return this.call(this.namespaceRoutesApi.getNamespacesFromAccounts(publicKeysBody), (body) => - body.namespaces.map((namespaceInfoDTO) => this.toNamespaceInfo(namespaceInfoDTO)), - ); - } - /** * Gets array of NamespaceName for different namespaceIds * @param namespaceIds - Array of namespace ids * @returns Observable */ - public getNamespacesName(namespaceIds: NamespaceId[]): Observable { + public getNamespacesNames(namespaceIds: NamespaceId[]): Observable { const namespaceIdsBody = { namespaceIds: namespaceIds.map((id) => id.toHex()), }; @@ -171,6 +141,27 @@ export class NamespaceHttp extends Http implements NamespaceRepository { ); } + /** + * Gets an array of namespaces. + * @param criteria - Namespace search criteria + * @returns Observable + */ + public search(criteria: NamespaceSearchCriteria): Observable> { + return this.call( + this.namespaceRoutesApi.searchNamespaces( + criteria.ownerAddress?.plain(), + criteria.registrationType?.valueOf(), + criteria.level0?.toHex(), + criteria.aliasType?.valueOf(), + criteria.pageSize, + criteria.pageNumber, + criteria.offset, + DtoMapping.mapEnum(criteria.order), + ), + (body) => super.toPage(body.pagination, body.data, this.toNamespaceInfo), + ); + } + /** * Gets the MosaicId from a MosaicAlias * @param namespaceId - the namespaceId of the namespace @@ -239,16 +230,22 @@ export class NamespaceHttp extends Http implements NamespaceRepository { dto.meta.id, dto.namespace.registrationType as number, dto.namespace.depth, - this.extractLevels(dto.namespace), + NamespaceHttp.extractLevels(dto.namespace), NamespaceId.createFromEncoded(dto.namespace.parentId), Address.createFromEncoded(dto.namespace.ownerAddress), UInt64.fromNumericString(dto.namespace.startHeight), UInt64.fromNumericString(dto.namespace.endHeight), - this.extractAlias(dto.namespace), + NamespaceHttp.extractAlias(dto.namespace), ); } - private extractLevels(namespace: NamespaceDTO): NamespaceId[] { + /** + * Extract the namespace levels + * + * @internal + * @param namespace + */ + private static extractLevels(namespace: NamespaceDTO): NamespaceId[] { const result: NamespaceId[] = []; if (namespace.level0) { result.push(NamespaceId.createFromEncoded(namespace.level0)); @@ -266,10 +263,9 @@ export class NamespaceHttp extends Http implements NamespaceRepository { * Extract the alias from a namespace * * @internal - * @access private * @param namespace */ - private extractAlias(namespace: NamespaceDTO): Alias { + private static extractAlias(namespace: NamespaceDTO): Alias { if (namespace.alias && namespace.alias.type.valueOf() === AliasType.Mosaic) { return new MosaicAlias(new MosaicId(namespace.alias.mosaicId!)); } else if (namespace.alias && namespace.alias.type.valueOf() === AliasType.Address) { diff --git a/src/infrastructure/NamespaceRepository.ts b/src/infrastructure/NamespaceRepository.ts index 97a8ffc742..ed93366d2e 100644 --- a/src/infrastructure/NamespaceRepository.ts +++ b/src/infrastructure/NamespaceRepository.ts @@ -22,14 +22,15 @@ import { MosaicNames } from '../model/mosaic/MosaicNames'; import { NamespaceId } from '../model/namespace/NamespaceId'; import { NamespaceInfo } from '../model/namespace/NamespaceInfo'; import { NamespaceName } from '../model/namespace/NamespaceName'; -import { QueryParams } from './QueryParams'; +import { Searcher } from './paginationStreamer/Searcher'; +import { NamespaceSearchCriteria } from './searchCriteria/NamespaceSearchCriteria'; /** * Namespace interface repository. * * @since 1.0 */ -export interface NamespaceRepository { +export interface NamespaceRepository extends Searcher { /** * Get readable names for a set of accountIds. * Returns friendly names for accounts. @@ -53,28 +54,12 @@ export interface NamespaceRepository { */ getNamespace(namespaceId: NamespaceId): Observable; - /** - * Gets array of NamespaceInfo for an account - * @param address - Address - * @param queryParams - (Optional) Query params - * @returns Observable - */ - getNamespacesFromAccount(address: Address, queryParams?: QueryParams): Observable; - - /** - * Gets array of NamespaceInfo for different account - * @param addresses - Array of Address - * @param queryParams - (Optional) Query params - * @returns Observable - */ - getNamespacesFromAccounts(addresses: Address[], queryParams?: QueryParams): Observable; - /** * Gets array of NamespaceName for different namespaceIds * @param namespaceIds - Array of namespace ids * @returns Observable */ - getNamespacesName(namespaceIds: NamespaceId[]): Observable; + getNamespacesNames(namespaceIds: NamespaceId[]): Observable; /** * Gets the MosaicId from a MosaicAlias diff --git a/src/infrastructure/paginationStreamer/AccountPaginationStreamer.ts b/src/infrastructure/paginationStreamer/AccountPaginationStreamer.ts new file mode 100644 index 0000000000..ad1e80bff0 --- /dev/null +++ b/src/infrastructure/paginationStreamer/AccountPaginationStreamer.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2020 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { PaginationStreamer } from './PaginationStreamer'; +import { Searcher } from './Searcher'; +import { AccountInfo } from '../../model/account/AccountInfo'; +import { AccountSearchCriteria } from '../searchCriteria/AccountSearchCriteria'; + +/** + * A helper object that streams {@link AccountInfo} using the search. + */ +export class AccountPaginationStreamer extends PaginationStreamer { + /** + * Constructor + * + * @param searcher the account repository that will perform the searches + */ + constructor(searcher: Searcher) { + super(searcher); + } +} diff --git a/src/infrastructure/paginationStreamer/NamespacePaginationStreamer.ts b/src/infrastructure/paginationStreamer/NamespacePaginationStreamer.ts new file mode 100644 index 0000000000..403a87dcf9 --- /dev/null +++ b/src/infrastructure/paginationStreamer/NamespacePaginationStreamer.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2020 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { PaginationStreamer } from './PaginationStreamer'; +import { Searcher } from './Searcher'; +import { NamespaceSearchCriteria } from '../searchCriteria/NamespaceSearchCriteria'; +import { NamespaceInfo } from '../../model/namespace/NamespaceInfo'; + +/** + * A helper object that streams {@link NamespaceInfo} using the search. + */ +export class NamespacePaginationStreamer extends PaginationStreamer { + /** + * Constructor + * + * @param searcher the namespace repository that will perform the searches + */ + constructor(searcher: Searcher) { + super(searcher); + } +} diff --git a/src/infrastructure/searchCriteria/AccountOrderBy.ts b/src/infrastructure/searchCriteria/AccountOrderBy.ts new file mode 100644 index 0000000000..30df7b0053 --- /dev/null +++ b/src/infrastructure/searchCriteria/AccountOrderBy.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2020 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export enum AccountOrderBy { + Id = 'id', + Importance = 'importance', + Balance = 'balance', +} diff --git a/src/infrastructure/searchCriteria/AccountSearchCriteria.ts b/src/infrastructure/searchCriteria/AccountSearchCriteria.ts new file mode 100644 index 0000000000..c4d829496b --- /dev/null +++ b/src/infrastructure/searchCriteria/AccountSearchCriteria.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2020 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SearchCriteria } from './SearchCriteria'; +import { MosaicId } from '../../model/mosaic/MosaicId'; +import { AccountOrderBy } from './AccountOrderBy'; + +/** + * Defines the params used to search blocks. With this criteria, you can sort and filter + * block queries using rest. + */ +export interface AccountSearchCriteria extends SearchCriteria { + /** + * Account order by enum. (optional) + */ + orderBy?: AccountOrderBy; + + /** + * Account mosaic id. (optional) + */ + mosaicId?: MosaicId; +} diff --git a/src/infrastructure/searchCriteria/NamespaceSearchCriteria.ts b/src/infrastructure/searchCriteria/NamespaceSearchCriteria.ts new file mode 100644 index 0000000000..5d33fcc7cc --- /dev/null +++ b/src/infrastructure/searchCriteria/NamespaceSearchCriteria.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2020 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SearchCriteria } from './SearchCriteria'; +import { NamespaceRegistrationType } from '../../model/namespace/NamespaceRegistrationType'; +import { AliasType } from '../../model/namespace/AliasType'; +import { Address } from '../../model/account/Address'; +import { NamespaceId } from '../../model/namespace/NamespaceId'; + +/** + * Defines the params used to search blocks. With this criteria, you can sort and filter + * namespace queries using rest. + */ +export interface NamespaceSearchCriteria extends SearchCriteria { + /** + * Namespace owner's address. (optional) + */ + ownerAddress?: Address; + + /** + * Namespace registration type. (optional) + */ + registrationType?: NamespaceRegistrationType; + + /** + * Namespace root level id. (optional) + */ + level0?: NamespaceId; + + /** + * Namespace alais type. (optional) + */ + aliasType?: AliasType; +} diff --git a/src/service/AccountService.ts b/src/service/AccountService.ts index 52b2e025f4..5d294cd40f 100644 --- a/src/service/AccountService.ts +++ b/src/service/AccountService.ts @@ -15,7 +15,7 @@ */ import { Observable, of } from 'rxjs'; -import { map, withLatestFrom } from 'rxjs/operators'; +import { map, withLatestFrom, toArray } from 'rxjs/operators'; import { RepositoryFactory } from '../infrastructure/RepositoryFactory'; import { AccountRepository } from '../infrastructure/AccountRepository'; import { NamespaceRepository } from '../infrastructure/NamespaceRepository'; @@ -31,6 +31,7 @@ import { NamespaceId } from '../model/namespace/NamespaceId'; import { AccountInfoResolvedMosaic } from '../model/account/AccountInfoResolvedMosaic'; import { AccountInfo } from '../model/account/AccountInfo'; import { NamespaceName } from '../model/namespace/NamespaceName'; +import { NamespacePaginationStreamer } from '../infrastructure/paginationStreamer/NamespacePaginationStreamer'; /** * Account Service */ @@ -58,7 +59,7 @@ export class AccountService implements IAccountService { mergeMap((info) => { const namespaceIds = this.getDistinctNamespaceIdFromAccountInfos(info); if (namespaceIds.length) { - return this.namespaceRepository.getNamespacesName(namespaceIds); + return this.namespaceRepository.getNamespacesNames(namespaceIds); } return of([]); }), @@ -77,14 +78,15 @@ export class AccountService implements IAccountService { /** * Get namespace info for account with namespace name - * @param addresses Array of addresses + * @param addresses Namespace owner address * @returns {Observable} */ - public accountNamespacesWithName(addresses: Address[]): Observable { - return this.namespaceRepository.getNamespacesFromAccounts(addresses).pipe( + public accountNamespacesWithName(address: Address): Observable { + return new NamespacePaginationStreamer(this.namespaceRepository).search({ ownerAddress: address }).pipe( + toArray(), mergeMap((infos) => { const namespaceIds = infos.map((i) => i.id); - return this.namespaceRepository.getNamespacesName(namespaceIds).pipe( + return this.namespaceRepository.getNamespacesNames(namespaceIds).pipe( map((resolved) => { return infos.map((info) => { const name = resolved.find((r) => r.namespaceId.equals(info.id)); diff --git a/src/service/NamespaceService.ts b/src/service/NamespaceService.ts index 679b62b3af..db66350ed8 100644 --- a/src/service/NamespaceService.ts +++ b/src/service/NamespaceService.ts @@ -40,7 +40,7 @@ export class NamespaceService { namespace(id: NamespaceId): Observable { return this.namespaceRepository.getNamespace(id).pipe( mergeMap((namespaceInfo: NamespaceInfo) => - this.namespaceRepository.getNamespacesName(namespaceInfo.levels).pipe( + this.namespaceRepository.getNamespacesNames(namespaceInfo.levels).pipe( map((names) => Object.assign( { diff --git a/src/service/interfaces/IAccountService.ts b/src/service/interfaces/IAccountService.ts index 2d70b21b4c..56abb576ed 100644 --- a/src/service/interfaces/IAccountService.ts +++ b/src/service/interfaces/IAccountService.ts @@ -31,8 +31,8 @@ export interface IAccountService { /** * Get namespace info for account with namespace name - * @param addresses Array of addresses + * @param address Namespace owner address * @returns {Observable} */ - accountNamespacesWithName(addresses: Address[]): Observable; + accountNamespacesWithName(addresses: Address): Observable; } diff --git a/test/core/utils/EnumMapping.spec.ts b/test/core/utils/EnumMapping.spec.ts index 6ce826b402..5f2b17b697 100644 --- a/test/core/utils/EnumMapping.spec.ts +++ b/test/core/utils/EnumMapping.spec.ts @@ -14,11 +14,12 @@ * limitations under the License. */ import { expect } from 'chai'; -import { Order, PositionEnum, BlockOrderByEnum } from 'symbol-openapi-typescript-fetch-client'; +import { Order, PositionEnum, BlockOrderByEnum, AccountOrderByEnum } from 'symbol-openapi-typescript-fetch-client'; import { Order as OrderModel } from '../../../src/infrastructure/searchCriteria/Order'; import { DtoMapping } from '../../../src/core/utils/DtoMapping'; import { BlockOrderBy } from '../../../src/infrastructure/searchCriteria/BlockOrderBy'; import { MerklePosition } from '../../../src/model/blockchain/MerklePosition'; +import { AccountOrderBy } from '../../../src/infrastructure/searchCriteria/AccountOrderBy'; describe('Order', () => { it('should be able to map Order', () => { @@ -78,6 +79,40 @@ describe('BlockOrderBy', () => { }); }); +describe('AccountOrderBy', () => { + it('should be able to map AccountOrderByEnum', () => { + const value1: AccountOrderByEnum = AccountOrderByEnum.Id; + const value2: AccountOrderBy = DtoMapping.mapEnum(value1); + expect(value2).eq(AccountOrderBy.Id); + }); + it('should be able to map AccountOrderBy when undefined', () => { + const value1: AccountOrderByEnum | undefined = undefined; + const value2: AccountOrderBy = DtoMapping.mapEnum(value1); + expect(value2).eq(undefined); + }); + it('should be able to map AccountOrderBy', () => { + const value1: AccountOrderBy = AccountOrderBy.Balance; + const value2: Order = DtoMapping.mapEnum(value1); + expect(value2).eq(AccountOrderByEnum.Balance); + }); + it('should be able to map AccountOrderBy when undefined', () => { + const value1: AccountOrderBy | undefined = undefined; + const value2: AccountOrderByEnum = DtoMapping.mapEnum(value1); + expect(value2).eq(undefined); + }); + it('should be able to map AccountOrderBy', () => { + const value1: AccountOrderBy = AccountOrderBy.Importance; + const value2: Order = DtoMapping.mapEnum(value1); + expect(value2).eq(AccountOrderByEnum.Importance); + }); + it('openAPI sanity check', () => { + for (const item in AccountOrderByEnum) { + const value: AccountOrderBy = DtoMapping.mapEnum(item); + expect(value).not.to.be.undefined; + } + }); +}); + describe('MerklePosition', () => { it('should be able to map MerklePosition', () => { const value1: PositionEnum = PositionEnum.Left; diff --git a/test/infrastructure/AccountHttp.spec.ts b/test/infrastructure/AccountHttp.spec.ts index 7827bc829f..86a6c4d652 100644 --- a/test/infrastructure/AccountHttp.spec.ts +++ b/test/infrastructure/AccountHttp.spec.ts @@ -25,6 +25,8 @@ import { Mosaic, AccountLinkPublicKeyDTO, AccountLinkVotingKeyDTO, + Pagination, + AccountPage, } from 'symbol-openapi-typescript-fetch-client'; import { deepEqual, instance, mock, reset, when } from 'ts-mockito'; import { DtoMapping } from '../../src/core/utils/DtoMapping'; @@ -33,6 +35,7 @@ import { AccountRepository } from '../../src/infrastructure/AccountRepository'; import { AccountInfo } from '../../src/model/account/AccountInfo'; import { AccountType } from '../../src/model/account/AccountType'; import { Address } from '../../src/model/account/Address'; +import { MosaicId } from '../../src/model/mosaic/MosaicId'; describe('AccountHttp', () => { const address = Address.createFromRawAddress('SATNE7Q5BITMUTRRN6IB4I7FLSDRDWZA34I2PMQ'); @@ -121,4 +124,21 @@ describe('AccountHttp', () => { const accountInfos = await accountRepository.getAccountsInfo([address]).toPromise(); assertAccountInfo(accountInfos[0]); }); + + it('searchAccounts', async () => { + const pagination = {} as Pagination; + pagination.pageNumber = 1; + pagination.pageSize = 1; + pagination.totalEntries = 1; + pagination.totalPages = 1; + + const body = {} as AccountPage; + body.data = [accountInfoDto]; + body.pagination = pagination; + when(accountRoutesApi.searchAccounts(undefined, undefined, undefined, undefined, undefined, mosaic.id)).thenReturn( + Promise.resolve(body), + ); + const infos = await accountRepository.search({ mosaicId: new MosaicId(mosaic.id) }).toPromise(); + assertAccountInfo(infos.data[0]); + }); }); diff --git a/test/infrastructure/NamespaceHttp.spec.ts b/test/infrastructure/NamespaceHttp.spec.ts index c8c03f44ba..9e3d5d260d 100644 --- a/test/infrastructure/NamespaceHttp.spec.ts +++ b/test/infrastructure/NamespaceHttp.spec.ts @@ -27,6 +27,8 @@ import { NamespaceMetaDTO, NamespaceNameDTO, NamespaceRoutesApi, + Pagination, + NamespacePage, } from 'symbol-openapi-typescript-fetch-client'; import { deepEqual, instance, mock, reset, when } from 'ts-mockito'; import { DtoMapping } from '../../src/core/utils/DtoMapping'; @@ -162,7 +164,7 @@ describe('NamespaceHttp', () => { when(namespaceRoutesApi.getNamespacesNames(deepEqual({ namespaceIds: [namespaceId.toHex()] }))).thenReturn( Promise.resolve([namespaceNameParent, namespaceNameChild]), ); - const namespace = await namespaceRepository.getNamespacesName([namespaceId]).toPromise(); + const namespace = await namespaceRepository.getNamespacesNames([namespaceId]).toPromise(); expect(namespace.length).to.be.equal(2); expect(namespace[0].name).to.be.equal('parent'); expect(namespace[0].namespaceId.toHex()).to.be.equal(namespaceId.toHex()); @@ -172,24 +174,6 @@ describe('NamespaceHttp', () => { expect(namespace[1].namespaceId.toHex()).to.be.equal(new NamespaceId('child').toHex()); }); - it('getNamespaceFromAccount', async () => { - when(namespaceRoutesApi.getNamespacesFromAccount(deepEqual(address.plain()), undefined, undefined)).thenReturn( - Promise.resolve({ namespaces: [namespaceInfoDto] }), - ); - const namespaces = await namespaceRepository.getNamespacesFromAccount(address).toPromise(); - - assertNamespaceInfo(namespaces[0]); - }); - - it('getNamespaceFromAccounts', async () => { - when(namespaceRoutesApi.getNamespacesFromAccounts(deepEqual(deepEqual({ addresses: [address.plain()] })))).thenReturn( - Promise.resolve({ namespaces: [namespaceInfoDto] }), - ); - const namespaces = await namespaceRepository.getNamespacesFromAccount(address).toPromise(); - - assertNamespaceInfo(namespaces[0]); - }); - it('getLinkedAddress', async () => { when(namespaceRoutesApi.getNamespace(deepEqual(namespaceId.toHex()))).thenReturn(Promise.resolve(namespaceInfoDto)); const namespaces = await namespaceRepository.getLinkedAddress(namespaceId).toPromise(); @@ -219,4 +203,30 @@ describe('NamespaceHttp', () => { .toPromise() .catch((error) => expect(error).not.to.be.undefined); }); + + it('searchNameapsces', async () => { + const pagination = {} as Pagination; + pagination.pageNumber = 1; + pagination.pageSize = 1; + pagination.totalEntries = 1; + pagination.totalPages = 1; + + const body = {} as NamespacePage; + body.data = [namespaceInfoDto]; + body.pagination = pagination; + when( + namespaceRoutesApi.searchNamespaces( + deepEqual(address.plain()), + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + ), + ).thenReturn(Promise.resolve(body)); + const infos = await namespaceRepository.search({ ownerAddress: address }).toPromise(); + assertNamespaceInfo(infos.data[0]); + }); }); diff --git a/test/infrastructure/streamer/AccountPaginationStreamer.spec.ts b/test/infrastructure/streamer/AccountPaginationStreamer.spec.ts new file mode 100644 index 0000000000..66e5603aaa --- /dev/null +++ b/test/infrastructure/streamer/AccountPaginationStreamer.spec.ts @@ -0,0 +1,57 @@ +/* + * Copyright 2020 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { instance, mock } from 'ts-mockito'; +import { AccountPaginationStreamer } from '../../../src/infrastructure/paginationStreamer/AccountPaginationStreamer'; +import { PaginationStreamerTestHelper } from './PaginationStreamerTestHelper'; +import { AccountRepository } from '../../../src/infrastructure/AccountRepository'; + +describe('AccountPaginationStreamer', () => { + it('basicMultiPageTest', () => { + const accountRepositoryMock: AccountRepository = mock(); + const streamer = new AccountPaginationStreamer(instance(accountRepositoryMock)); + const tester = new PaginationStreamerTestHelper(streamer, mock(), accountRepositoryMock, {}); + return tester.basicMultiPageTest(); + }); + + it('basicSinglePageTest', () => { + const accountRepositoryMock: AccountRepository = mock(); + const streamer = new AccountPaginationStreamer(instance(accountRepositoryMock)); + const tester = new PaginationStreamerTestHelper(streamer, mock(), accountRepositoryMock, {}); + return tester.basicSinglePageTest(); + }); + + it('limitToTwoPages', () => { + const accountRepositoryMock: AccountRepository = mock(); + const streamer = new AccountPaginationStreamer(instance(accountRepositoryMock)); + const tester = new PaginationStreamerTestHelper(streamer, mock(), accountRepositoryMock, {}); + return tester.limitToTwoPages(); + }); + + it('multipageWithLimit', () => { + const accountRepositoryMock: AccountRepository = mock(); + const streamer = new AccountPaginationStreamer(instance(accountRepositoryMock)); + const tester = new PaginationStreamerTestHelper(streamer, mock(), accountRepositoryMock, {}); + return tester.multipageWithLimit(); + }); + + it('limitToThreePages', () => { + const accountRepositoryMock: AccountRepository = mock(); + const streamer = new AccountPaginationStreamer(instance(accountRepositoryMock)); + const tester = new PaginationStreamerTestHelper(streamer, mock(), accountRepositoryMock, {}); + return tester.limitToThreePages(); + }); +}); diff --git a/test/infrastructure/streamer/NamespacePaginationStreamer.spec.ts b/test/infrastructure/streamer/NamespacePaginationStreamer.spec.ts new file mode 100644 index 0000000000..8668ffd389 --- /dev/null +++ b/test/infrastructure/streamer/NamespacePaginationStreamer.spec.ts @@ -0,0 +1,57 @@ +/* + * Copyright 2020 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { instance, mock } from 'ts-mockito'; +import { NamespacePaginationStreamer } from '../../../src/infrastructure/paginationStreamer/NamespacePaginationStreamer'; +import { PaginationStreamerTestHelper } from './PaginationStreamerTestHelper'; +import { NamespaceRepository } from '../../../src/infrastructure/NamespaceRepository'; + +describe('NamespacePaginationStreamer', () => { + it('basicMultiPageTest', () => { + const namespaceRepositoryMock: NamespaceRepository = mock(); + const streamer = new NamespacePaginationStreamer(instance(namespaceRepositoryMock)); + const tester = new PaginationStreamerTestHelper(streamer, mock(), namespaceRepositoryMock, {}); + return tester.basicMultiPageTest(); + }); + + it('basicSinglePageTest', () => { + const namespaceRepositoryMock: NamespaceRepository = mock(); + const streamer = new NamespacePaginationStreamer(instance(namespaceRepositoryMock)); + const tester = new PaginationStreamerTestHelper(streamer, mock(), namespaceRepositoryMock, {}); + return tester.basicSinglePageTest(); + }); + + it('limitToTwoPages', () => { + const namespaceRepositoryMock: NamespaceRepository = mock(); + const streamer = new NamespacePaginationStreamer(instance(namespaceRepositoryMock)); + const tester = new PaginationStreamerTestHelper(streamer, mock(), namespaceRepositoryMock, {}); + return tester.limitToTwoPages(); + }); + + it('multipageWithLimit', () => { + const namespaceRepositoryMock: NamespaceRepository = mock(); + const streamer = new NamespacePaginationStreamer(instance(namespaceRepositoryMock)); + const tester = new PaginationStreamerTestHelper(streamer, mock(), namespaceRepositoryMock, {}); + return tester.multipageWithLimit(); + }); + + it('limitToThreePages', () => { + const namespaceRepositoryMock: NamespaceRepository = mock(); + const streamer = new NamespacePaginationStreamer(instance(namespaceRepositoryMock)); + const tester = new PaginationStreamerTestHelper(streamer, mock(), namespaceRepositoryMock, {}); + return tester.limitToThreePages(); + }); +}); diff --git a/test/service/AccountService.spec.ts b/test/service/AccountService.spec.ts index f6ca06b726..97f130fc05 100644 --- a/test/service/AccountService.spec.ts +++ b/test/service/AccountService.spec.ts @@ -37,6 +37,7 @@ import { MosaicId } from '../../src/model/mosaic/MosaicId'; import { NamespaceName } from '../../src/model/namespace/NamespaceName'; import { Mosaic } from '../../src/model/mosaic/Mosaic'; import { SupplementalPublicKeys } from '../../src/model/account/SupplementalPublicKeys'; +import { Page } from '../../src/infrastructure/Page'; describe('AccountService', () => { let accountService: AccountService; @@ -125,23 +126,31 @@ describe('AccountService', () => { when(mockAccountRepository.getAccountsInfo(deepEqual([account.address]))).thenReturn(observableOf(mockAccountInfo())); - when(mockNamespaceRepository.getNamespacesFromAccounts(deepEqual([account.address]))).thenReturn(observableOf(mockNamespaceInfo())); - when(mockNamespaceRepository.getNamespacesFromAccounts(deepEqual([account2.address]))).thenReturn( - observableOf(mockNamespaceInfo()), + when( + mockNamespaceRepository.search( + deepEqual({ + ownerAddress: account.address, + pageNumber: 1, + }), + ), + ).thenReturn(observableOf(new Page(mockNamespaceInfo(), 1, 20, 3, 1))); + + when(mockNamespaceRepository.search(deepEqual({ ownerAddress: account.address, pageNumber: 1 }))).thenReturn( + observableOf(new Page(mockNamespaceInfo(), 1, 20, 3, 1)), ); - when(mockNamespaceRepository.getNamespacesName(deepEqual([NetworkCurrencyLocal.NAMESPACE_ID]))).thenReturn( + when(mockNamespaceRepository.getNamespacesNames(deepEqual([NetworkCurrencyLocal.NAMESPACE_ID]))).thenReturn( observableOf([mockNamespaceName(NetworkCurrencyLocal.NAMESPACE_ID, 'catapult.currency')]), ); - when(mockNamespaceRepository.getNamespacesName(deepEqual([NetworkCurrencyPublic.NAMESPACE_ID]))).thenReturn( + when(mockNamespaceRepository.getNamespacesNames(deepEqual([NetworkCurrencyPublic.NAMESPACE_ID]))).thenReturn( observableOf([mockNamespaceName(NetworkCurrencyPublic.NAMESPACE_ID, 'symbol.xym')]), ); - when(mockNamespaceRepository.getNamespacesName(deepEqual([NetworkHarvestLocal.NAMESPACE_ID]))).thenReturn( + when(mockNamespaceRepository.getNamespacesNames(deepEqual([NetworkHarvestLocal.NAMESPACE_ID]))).thenReturn( observableOf([mockNamespaceName(NetworkHarvestLocal.NAMESPACE_ID, 'catapult.harvest')]), ); when( - mockNamespaceRepository.getNamespacesName( + mockNamespaceRepository.getNamespacesNames( deepEqual([NetworkCurrencyLocal.NAMESPACE_ID, NetworkCurrencyPublic.NAMESPACE_ID, NetworkHarvestLocal.NAMESPACE_ID]), ), ).thenReturn( @@ -181,7 +190,7 @@ describe('AccountService', () => { }); it('should return namespaceInfo with resolved name', async () => { - const result = await accountService.accountNamespacesWithName([account.address]).toPromise(); + const result = await accountService.accountNamespacesWithName(account.address).toPromise(); expect(result).to.not.be.undefined; expect(result.length).to.be.greaterThan(0); expect(result![0].namespaceName).to.be.equal('catapult.currency'); diff --git a/test/service/NamespaceService.spec.ts b/test/service/NamespaceService.spec.ts index c104d19b13..c22802d248 100644 --- a/test/service/NamespaceService.spec.ts +++ b/test/service/NamespaceService.spec.ts @@ -66,10 +66,10 @@ describe('NamespaceService', () => { const subnamespace = givenSubnamespace(); when(mockedNamespaceRepository.getNamespace(rootNamespace.id)).thenReturn(observableOf(rootNamespace)); when(mockedNamespaceRepository.getNamespace(subnamespace.id)).thenReturn(observableOf(subnamespace)); - when(mockedNamespaceRepository.getNamespacesName(deepEqual([rootNamespace.id]))).thenReturn( + when(mockedNamespaceRepository.getNamespacesNames(deepEqual([rootNamespace.id]))).thenReturn( observableOf([new NamespaceName(new NamespaceId([3316183705, 3829351378]), 'symboltests')]), ); - when(mockedNamespaceRepository.getNamespacesName(deepEqual([rootNamespace.id, subnamespace.id]))).thenReturn( + when(mockedNamespaceRepository.getNamespacesNames(deepEqual([rootNamespace.id, subnamespace.id]))).thenReturn( observableOf([ new NamespaceName(new NamespaceId([3316183705, 3829351378]), 'symboltests'), new NamespaceName(new NamespaceId([1781696705, 4157485863]), 'level2'), @@ -88,13 +88,13 @@ describe('NamespaceService', () => { const subnamespace = givenSubnamespace(); when(mockedNamespaceRepository.getNamespace(rootNamespace.id)).thenReturn(observableOf(rootNamespace)); when(mockedNamespaceRepository.getNamespace(subnamespace.id)).thenReturn(observableOf(subnamespace)); - when(mockedNamespaceRepository.getNamespacesName(deepEqual([rootNamespace.id]))).thenReturn( + when(mockedNamespaceRepository.getNamespacesNames(deepEqual([rootNamespace.id]))).thenReturn( observableOf([new NamespaceName(new NamespaceId([3316183705, 3829351378]), 'symboltests')]), ); - when(mockedNamespaceRepository.getNamespacesName(deepEqual([subnamespace.id]))).thenReturn( + when(mockedNamespaceRepository.getNamespacesNames(deepEqual([subnamespace.id]))).thenReturn( observableOf([new NamespaceName(new NamespaceId([1781696705, 4157485863]), 'level2')]), ); - when(mockedNamespaceRepository.getNamespacesName(deepEqual([rootNamespace.id, subnamespace.id]))).thenReturn( + when(mockedNamespaceRepository.getNamespacesNames(deepEqual([rootNamespace.id, subnamespace.id]))).thenReturn( observableOf([ new NamespaceName(new NamespaceId([3316183705, 3829351378]), 'symboltests'), new NamespaceName(new NamespaceId([1781696705, 4157485863]), 'level2'), From 6f5261ef4d4ff405a1b48a1cedf8ad665448bae6 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 15 Jul 2020 10:06:19 +0100 Subject: [PATCH 2/5] Updated e2e tests --- e2e/infrastructure/AccountHttp.spec.ts | 30 ++++++++++++++++++++---- e2e/infrastructure/NamespaceHttp.spec.ts | 12 ++++++---- src/service/AccountService.ts | 30 +++++++++++++----------- 3 files changed, 49 insertions(+), 23 deletions(-) diff --git a/e2e/infrastructure/AccountHttp.spec.ts b/e2e/infrastructure/AccountHttp.spec.ts index 4cdb323659..2425d60fae 100644 --- a/e2e/infrastructure/AccountHttp.spec.ts +++ b/e2e/infrastructure/AccountHttp.spec.ts @@ -33,6 +33,9 @@ import { NamespaceRegistrationTransaction } from '../../src/model/transaction/Na import { TransferTransaction } from '../../src/model/transaction/TransferTransaction'; import { UInt64 } from '../../src/model/UInt64'; import { IntegrationTestHelper } from './IntegrationTestHelper'; +import { AccountPaginationStreamer } from '../../src/infrastructure/paginationStreamer/AccountPaginationStreamer'; +import { toArray, take } from 'rxjs/operators'; +import { deepEqual } from 'assert'; describe('AccountHttp', () => { const helper = new IntegrationTestHelper(); @@ -158,11 +161,11 @@ describe('AccountHttp', () => { }); }); - /** - * ========================= - * Tests - * ========================= - */ + // /** + // * ========================= + // * Tests + // * ========================= + // */ describe('getAccountInfo', () => { it('should return account data given a NEM Address', async () => { @@ -178,6 +181,23 @@ describe('AccountHttp', () => { }); }); + describe('searchAccount', () => { + it('should return account info', async () => { + const info = await accountRepository.search({}).toPromise(); + expect(info.data.length).to.be.greaterThan(0); + }); + }); + + describe('searchAccount with streamer', () => { + it('should return account info', async () => { + const streamer = new AccountPaginationStreamer(accountRepository); + const infoStreamer = await streamer.search({ pageSize: 20 }).pipe(take(20), toArray()).toPromise(); + const info = await namespaceRepository.search({ pageSize: 20 }).toPromise(); + expect(infoStreamer.length).to.be.greaterThan(0); + deepEqual(infoStreamer, info.data); + }); + }); + describe('transactions', () => { it('should not return accounts when account does not exist', () => { return accountRepository diff --git a/e2e/infrastructure/NamespaceHttp.spec.ts b/e2e/infrastructure/NamespaceHttp.spec.ts index 011f309362..e95ab28897 100644 --- a/e2e/infrastructure/NamespaceHttp.spec.ts +++ b/e2e/infrastructure/NamespaceHttp.spec.ts @@ -27,6 +27,7 @@ import { UInt64 } from '../../src/model/UInt64'; import { IntegrationTestHelper } from './IntegrationTestHelper'; import { NamespacePaginationStreamer } from '../../src/infrastructure/paginationStreamer/NamespacePaginationStreamer'; import { take, toArray } from 'rxjs/operators'; +import { Order } from '../../src/infrastructure/infrastructure'; describe('NamespaceHttp', () => { let defaultNamespaceId: NamespaceId; @@ -115,7 +116,7 @@ describe('NamespaceHttp', () => { describe('searchNamespace', () => { it('should return namespace info', async () => { - const info = await namespaceRepository.search({}).toPromise(); + const info = await namespaceRepository.search({ ownerAddress: account.address }).toPromise(); expect(info.data.length).to.be.greaterThan(0); }); }); @@ -123,10 +124,13 @@ describe('NamespaceHttp', () => { describe('searchNamespace with streamer', () => { it('should return namespace info', async () => { const streamer = new NamespacePaginationStreamer(namespaceRepository); - const infoStreamer = await streamer.search({ pageSize: 20 }).pipe(take(20), toArray()).toPromise(); - const info = await namespaceRepository.search({ pageSize: 20 }).toPromise(); + const infoStreamer = await streamer + .search({ ownerAddress: account.address, pageSize: 20, order: Order.Desc }) + .pipe(take(20), toArray()) + .toPromise(); + const info = await namespaceRepository.search({ pageSize: 20, order: Order.Desc }).toPromise(); expect(infoStreamer.length).to.be.greaterThan(0); - deepEqual(infoStreamer, info.data); + deepEqual(infoStreamer[0], info.data[0]); }); }); }); diff --git a/src/service/AccountService.ts b/src/service/AccountService.ts index 5d294cd40f..c055b9aa15 100644 --- a/src/service/AccountService.ts +++ b/src/service/AccountService.ts @@ -82,20 +82,22 @@ export class AccountService implements IAccountService { * @returns {Observable} */ public accountNamespacesWithName(address: Address): Observable { - return new NamespacePaginationStreamer(this.namespaceRepository).search({ ownerAddress: address }).pipe( - toArray(), - mergeMap((infos) => { - const namespaceIds = infos.map((i) => i.id); - return this.namespaceRepository.getNamespacesNames(namespaceIds).pipe( - map((resolved) => { - return infos.map((info) => { - const name = resolved.find((r) => r.namespaceId.equals(info.id)); - return DtoMapping.assign(info, { namespaceName: name?.name }); - }); - }), - ); - }), - ); + return new NamespacePaginationStreamer(this.namespaceRepository) + .search({ ownerAddress: address }) + .pipe(toArray()) + .pipe( + mergeMap((infos) => { + const namespaceIds = infos.map((i) => i.id); + return this.namespaceRepository.getNamespacesNames(namespaceIds).pipe( + map((resolved) => { + return infos.map((info) => { + const name = resolved.find((r) => r.namespaceId.equals(info.id)); + return DtoMapping.assign(info, { namespaceName: name?.name }); + }); + }), + ); + }), + ); } /** From 74780efbda95de4059af254ef7b24414064fbd00 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 15 Jul 2020 16:59:55 +0100 Subject: [PATCH 3/5] removed importance in account order by --- src/infrastructure/searchCriteria/AccountOrderBy.ts | 1 - test/core/utils/EnumMapping.spec.ts | 5 ----- 2 files changed, 6 deletions(-) diff --git a/src/infrastructure/searchCriteria/AccountOrderBy.ts b/src/infrastructure/searchCriteria/AccountOrderBy.ts index 30df7b0053..6097111ada 100644 --- a/src/infrastructure/searchCriteria/AccountOrderBy.ts +++ b/src/infrastructure/searchCriteria/AccountOrderBy.ts @@ -15,6 +15,5 @@ */ export enum AccountOrderBy { Id = 'id', - Importance = 'importance', Balance = 'balance', } diff --git a/test/core/utils/EnumMapping.spec.ts b/test/core/utils/EnumMapping.spec.ts index 5f2b17b697..2b7e2139eb 100644 --- a/test/core/utils/EnumMapping.spec.ts +++ b/test/core/utils/EnumMapping.spec.ts @@ -100,11 +100,6 @@ describe('AccountOrderBy', () => { const value2: AccountOrderByEnum = DtoMapping.mapEnum(value1); expect(value2).eq(undefined); }); - it('should be able to map AccountOrderBy', () => { - const value1: AccountOrderBy = AccountOrderBy.Importance; - const value2: Order = DtoMapping.mapEnum(value1); - expect(value2).eq(AccountOrderByEnum.Importance); - }); it('openAPI sanity check', () => { for (const item in AccountOrderByEnum) { const value: AccountOrderBy = DtoMapping.mapEnum(item); From 1ac458120b9d113707780b8415c86c40d16c6b59 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 15 Jul 2020 17:44:13 +0100 Subject: [PATCH 4/5] Fixed e2e tests --- e2e/infrastructure/AccountHttp.spec.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/e2e/infrastructure/AccountHttp.spec.ts b/e2e/infrastructure/AccountHttp.spec.ts index 2425d60fae..a4c18282c3 100644 --- a/e2e/infrastructure/AccountHttp.spec.ts +++ b/e2e/infrastructure/AccountHttp.spec.ts @@ -36,6 +36,8 @@ import { IntegrationTestHelper } from './IntegrationTestHelper'; import { AccountPaginationStreamer } from '../../src/infrastructure/paginationStreamer/AccountPaginationStreamer'; import { toArray, take } from 'rxjs/operators'; import { deepEqual } from 'assert'; +import { Order } from '../../src/infrastructure/infrastructure'; +import { AccountOrderBy } from '../../src/infrastructure/searchCriteria/AccountOrderBy'; describe('AccountHttp', () => { const helper = new IntegrationTestHelper(); @@ -191,10 +193,13 @@ describe('AccountHttp', () => { describe('searchAccount with streamer', () => { it('should return account info', async () => { const streamer = new AccountPaginationStreamer(accountRepository); - const infoStreamer = await streamer.search({ pageSize: 20 }).pipe(take(20), toArray()).toPromise(); - const info = await namespaceRepository.search({ pageSize: 20 }).toPromise(); + const infoStreamer = await streamer + .search({ pageSize: 20, order: Order.Asc, orderBy: AccountOrderBy.Id }) + .pipe(take(20), toArray()) + .toPromise(); + const info = await accountRepository.search({ pageSize: 20, order: Order.Asc, orderBy: AccountOrderBy.Id }).toPromise(); expect(infoStreamer.length).to.be.greaterThan(0); - deepEqual(infoStreamer, info.data); + deepEqual(infoStreamer[0], info.data[0]); }); }); From bc558e3cb341effd6a7d906a5d9fe0dda6d0ff20 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 15 Jul 2020 17:50:54 +0100 Subject: [PATCH 5/5] Fixed comments --- e2e/infrastructure/AccountHttp.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/e2e/infrastructure/AccountHttp.spec.ts b/e2e/infrastructure/AccountHttp.spec.ts index a4c18282c3..908c1990fc 100644 --- a/e2e/infrastructure/AccountHttp.spec.ts +++ b/e2e/infrastructure/AccountHttp.spec.ts @@ -163,11 +163,11 @@ describe('AccountHttp', () => { }); }); - // /** - // * ========================= - // * Tests - // * ========================= - // */ + /** + * ========================= + * Tests + * ========================= + */ describe('getAccountInfo', () => { it('should return account data given a NEM Address', async () => {