diff --git a/src/infrastructure/MetadataHttp.ts b/src/infrastructure/MetadataHttp.ts index c7ea12fcf9..23c1aa0b67 100644 --- a/src/infrastructure/MetadataHttp.ts +++ b/src/infrastructure/MetadataHttp.ts @@ -14,8 +14,7 @@ * limitations under the License. */ -import { from as observableFrom, Observable, throwError } from 'rxjs'; -import { catchError, map } from 'rxjs/operators'; +import { Observable } from 'rxjs'; import { MetadataDTO, MetadataRoutesApi } from 'symbol-openapi-typescript-node-client'; import { Convert } from '../core/format/Convert'; import { Address } from '../model/account/Address'; @@ -43,8 +42,7 @@ export class MetadataHttp extends Http implements MetadataRepository { /** * Constructor - * @param url - * @param networkType + * @param url the url. */ constructor(url: string) { super(url); @@ -59,15 +57,12 @@ export class MetadataHttp extends Http implements MetadataRepository { * @returns Observable */ public getAccountMetadata(address: Address, queryParams?: QueryParams): Observable { - return observableFrom( + return this.call( this.metadataRoutesApi.getAccountMetadata(address.plain(), - this.queryParams(queryParams).pageSize, - this.queryParams(queryParams).id, - this.queryParams(queryParams).ordering)).pipe( - map(({body}) => body.metadataEntries.map((metadataEntry) => { - return this.buildMetadata(metadataEntry); - })), - catchError((error) => throwError(this.errorHandling(error))), + this.queryParams(queryParams).pageSize, + this.queryParams(queryParams).id, + this.queryParams(queryParams).ordering), + (body) => body.metadataEntries.map((metadataEntry) => this.buildMetadata(metadataEntry)) ); } @@ -78,12 +73,9 @@ export class MetadataHttp extends Http implements MetadataRepository { * @returns Observable */ getAccountMetadataByKey(address: Address, key: string): Observable { - return observableFrom( - this.metadataRoutesApi.getAccountMetadataByKey(address.plain(), key)).pipe( - map(({body}) => body.metadataEntries.map((metadataEntry) => { - return this.buildMetadata(metadataEntry); - })), - catchError((error) => throwError(this.errorHandling(error))), + return this.call( + this.metadataRoutesApi.getAccountMetadataByKey(address.plain(), key), + (body) => body.metadataEntries.map((metadataEntry) => this.buildMetadata(metadataEntry)) ); } @@ -95,10 +87,9 @@ export class MetadataHttp extends Http implements MetadataRepository { * @returns Observable */ getAccountMetadataByKeyAndSender(address: Address, key: string, publicKey: string): Observable { - return observableFrom( - this.metadataRoutesApi.getAccountMetadataByKeyAndSender(address.plain(), key, publicKey)).pipe( - map(({body}) => this.buildMetadata(body)), - catchError((error) => throwError(this.errorHandling(error))), + return this.call( + this.metadataRoutesApi.getAccountMetadataByKeyAndSender(address.plain(), key, publicKey), + (body) => this.buildMetadata(body) ); } @@ -109,15 +100,12 @@ export class MetadataHttp extends Http implements MetadataRepository { * @returns Observable */ getMosaicMetadata(mosaicId: MosaicId, queryParams?: QueryParams): Observable { - return observableFrom( + return this.call( this.metadataRoutesApi.getMosaicMetadata(mosaicId.toHex(), - this.queryParams(queryParams).pageSize, - this.queryParams(queryParams).id, - this.queryParams(queryParams).ordering)).pipe( - map(({body}) => body.metadataEntries.map((metadataEntry) => { - return this.buildMetadata(metadataEntry); - })), - catchError((error) => throwError(this.errorHandling(error))), + this.queryParams(queryParams).pageSize, + this.queryParams(queryParams).id, + this.queryParams(queryParams).ordering), + (body) => body.metadataEntries.map((metadataEntry) => this.buildMetadata(metadataEntry)) ); } @@ -128,12 +116,9 @@ export class MetadataHttp extends Http implements MetadataRepository { * @returns Observable */ getMosaicMetadataByKey(mosaicId: MosaicId, key: string): Observable { - return observableFrom( - this.metadataRoutesApi.getMosaicMetadataByKey(mosaicId.toHex(), key)).pipe( - map(({body}) => body.metadataEntries.map((metadataEntry) => { - return this.buildMetadata(metadataEntry); - })), - catchError((error) => throwError(this.errorHandling(error))), + return this.call( + this.metadataRoutesApi.getMosaicMetadataByKey(mosaicId.toHex(), key), + (body) => body.metadataEntries.map((metadataEntry) => this.buildMetadata(metadataEntry)) ); } @@ -145,10 +130,8 @@ export class MetadataHttp extends Http implements MetadataRepository { * @returns Observable */ getMosaicMetadataByKeyAndSender(mosaicId: MosaicId, key: string, publicKey: string): Observable { - return observableFrom( - this.metadataRoutesApi.getMosaicMetadataByKeyAndSender(mosaicId.toHex(), key, publicKey)).pipe( - map(({body}) => this.buildMetadata(body)), - catchError((error) => throwError(this.errorHandling(error))), + return this.call( + this.metadataRoutesApi.getMosaicMetadataByKeyAndSender(mosaicId.toHex(), key, publicKey), this.buildMetadata ); } @@ -159,15 +142,11 @@ export class MetadataHttp extends Http implements MetadataRepository { * @returns Observable */ public getNamespaceMetadata(namespaceId: NamespaceId, queryParams?: QueryParams): Observable { - return observableFrom( + return this.call( this.metadataRoutesApi.getNamespaceMetadata(namespaceId.toHex(), - this.queryParams(queryParams).pageSize, - this.queryParams(queryParams).id, - this.queryParams(queryParams).ordering)).pipe( - map(({body}) => body.metadataEntries.map((metadataEntry) => { - return this.buildMetadata(metadataEntry); - })), - catchError((error) => throwError(this.errorHandling(error))), + this.queryParams(queryParams).pageSize, + this.queryParams(queryParams).id, + this.queryParams(queryParams).ordering), body => body.metadataEntries.map(this.buildMetadata) ); } @@ -178,12 +157,9 @@ export class MetadataHttp extends Http implements MetadataRepository { * @returns Observable */ public getNamespaceMetadataByKey(namespaceId: NamespaceId, key: string): Observable { - return observableFrom( - this.metadataRoutesApi.getNamespaceMetadataByKey(namespaceId.toHex(), key)).pipe( - map(({body}) => body.metadataEntries.map((metadataEntry) => { - return this.buildMetadata(metadataEntry); - })), - catchError((error) => throwError(this.errorHandling(error))), + return this.call( + this.metadataRoutesApi.getNamespaceMetadataByKey(namespaceId.toHex(), key), + (body) => body.metadataEntries.map(this.buildMetadata) ); } @@ -195,18 +171,14 @@ export class MetadataHttp extends Http implements MetadataRepository { * @returns Observable */ public getNamespaceMetadataByKeyAndSender(namespaceId: NamespaceId, key: string, publicKey: string): Observable { - return observableFrom( - this.metadataRoutesApi.getNamespaceMetadataByKeyAndSender(namespaceId.toHex(), key, publicKey)).pipe( - map(({body}) => this.buildMetadata(body)), - catchError((error) => throwError(this.errorHandling(error))), - ); + return this.call( + this.metadataRoutesApi.getNamespaceMetadataByKeyAndSender(namespaceId.toHex(), key, publicKey), this.buildMetadata); } /** - * Returns the mosaic metadata given a mosaic id. - * @param namespaceId - Namespace identifier. - * @param queryParams - Optional query parameters - * @returns Observable + * It maps MetadataDTO into a Metadata + * @param metadata - the dto + * @returns the model Metadata. */ private buildMetadata(metadata: MetadataDTO): Metadata { const metadataEntry = metadata.metadataEntry; diff --git a/test/infrastructure/MetadataHttp.spec.ts b/test/infrastructure/MetadataHttp.spec.ts new file mode 100644 index 0000000000..7ad4c82437 --- /dev/null +++ b/test/infrastructure/MetadataHttp.spec.ts @@ -0,0 +1,216 @@ +/* + * 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 { expect } from 'chai'; +import * as http from 'http'; +import { + MetadataDTO, + MetadataEntriesDTO, + MetadataEntryDTO, + MetadataRoutesApi, + MetadataTypeEnum, +} from 'symbol-openapi-typescript-node-client'; +import { instance, mock, reset, when } from 'ts-mockito'; +import { DtoMapping } from '../../src/core/utils/DtoMapping'; +import { MetadataHttp } from '../../src/infrastructure/MetadataHttp'; +import { MetadataRepository } from '../../src/infrastructure/MetadataRepository'; +import { QueryParams } from '../../src/infrastructure/QueryParams'; +import { Address } from '../../src/model/account/Address'; +import { Metadata } from '../../src/model/metadata/Metadata'; +import { MetadataType } from '../../src/model/metadata/MetadataType'; +import { MosaicId } from '../../src/model/mosaic/MosaicId'; +import { NamespaceId } from '../../src/model/namespace/NamespaceId'; + +describe('MetadataHttp', () => { + + const address = Address.createFromRawAddress('MCTVW23D2MN5VE4AQ4TZIDZENGNOZXPRPR72DYSX'); + const mosaicId = new MosaicId('941299B2B7E1291C'); + const namespaceId = new NamespaceId('some.address'); + + const metadataDTOMosaic = new MetadataDTO(); + metadataDTOMosaic.id = 'aaa'; + + const metadataEntryDtoMosaic = new MetadataEntryDTO(); + metadataEntryDtoMosaic.compositeHash = '1'; + metadataEntryDtoMosaic.metadataType = MetadataTypeEnum.NUMBER_1; + metadataEntryDtoMosaic.scopedMetadataKey = '123451234512345A'; + metadataEntryDtoMosaic.senderPublicKey = 'aSenderPublicKey1'; + metadataEntryDtoMosaic.targetPublicKey = 'aTargetPublicKey1'; + metadataEntryDtoMosaic.value = 'value1'; + metadataEntryDtoMosaic.targetId = '941299B2B7E1291C' as any; + metadataDTOMosaic.metadataEntry = metadataEntryDtoMosaic; + + const metadataDTOAddress = new MetadataDTO(); + metadataDTOAddress.id = 'bbb'; + + const metadataEntryDtoAddress = new MetadataEntryDTO(); + metadataEntryDtoAddress.compositeHash = '2'; + metadataEntryDtoAddress.metadataType = MetadataTypeEnum.NUMBER_0; + metadataEntryDtoAddress.scopedMetadataKey = '123451234512345B'; + metadataEntryDtoAddress.senderPublicKey = 'aSenderPublicKey2'; + metadataEntryDtoAddress.targetPublicKey = 'aTargetPublicKey2'; + metadataEntryDtoAddress.value = 'value1'; + metadataEntryDtoAddress.targetId = '941299B2B7E1291D' as any; + metadataDTOAddress.metadataEntry = metadataEntryDtoAddress; + + const metadataDTONamespace = new MetadataDTO(); + metadataDTONamespace.id = 'ccc'; + + const metadataEntryDtoNamespace = new MetadataEntryDTO(); + metadataEntryDtoNamespace.compositeHash = '3'; + metadataEntryDtoNamespace.metadataType = MetadataTypeEnum.NUMBER_2; + metadataEntryDtoNamespace.scopedMetadataKey = '123451234512345C'; + metadataEntryDtoNamespace.senderPublicKey = 'aSenderPublicKey3'; + metadataEntryDtoNamespace.targetPublicKey = 'aTargetPublicKey3'; + metadataEntryDtoNamespace.value = 'value1'; + metadataEntryDtoNamespace.targetId = '941299B2B7E1291E' as any; + metadataDTONamespace.metadataEntry = metadataEntryDtoNamespace; + + const metadataEntriesDTO = new MetadataEntriesDTO(); + metadataEntriesDTO.metadataEntries = [metadataDTOMosaic, metadataDTOAddress, metadataDTONamespace]; + + const url = 'http://someHost'; + const response: http.IncomingMessage = mock(); + const metadataRoutesApi: MetadataRoutesApi = mock(); + const metadataRepository: MetadataRepository = DtoMapping.assign( + new MetadataHttp(url), {metadataRoutesApi: instance(metadataRoutesApi)}); + before(() => { + reset(response); + reset(metadataRoutesApi); + }); + + function assertMetadataInfo(metadataInfo: Metadata, dto: MetadataDTO) { + expect(metadataInfo).to.be.not.null; + expect(metadataInfo.id).to.be.equals(dto.id); + if (metadataInfo.metadataEntry.metadataType === MetadataType.Mosaic) { + expect(metadataInfo.metadataEntry.targetId!.toHex()).to.be.equals(dto.metadataEntry.targetId); + } + if (metadataInfo.metadataEntry.metadataType === MetadataType.Account) { + expect(metadataInfo.metadataEntry.targetId).to.be.undefined; + } + if (metadataInfo.metadataEntry.metadataType === MetadataType.Namespace) { + expect(metadataInfo.metadataEntry.targetId!.toHex()).to.be.equals(dto.metadataEntry.targetId); + } + expect(metadataInfo.metadataEntry.targetPublicKey).to.be.equals(dto.metadataEntry.targetPublicKey); + expect(metadataInfo.metadataEntry.scopedMetadataKey.toHex()).to.be.equals(dto.metadataEntry.scopedMetadataKey); + expect(metadataInfo.metadataEntry.compositeHash).to.be.equals(dto.metadataEntry.compositeHash); + } + + it('getAccountMetadata', async () => { + when(metadataRoutesApi.getAccountMetadata(address.plain(), 1, 'a', '-id')).thenReturn(Promise.resolve({ + response, + body: metadataEntriesDTO, + })); + const metadatas = await metadataRepository.getAccountMetadata(address, new QueryParams({ + pageSize: 1, + id: 'a', + })).toPromise(); + expect(metadatas.length).to.be.equals(3); + assertMetadataInfo(metadatas[0], metadataDTOMosaic); + assertMetadataInfo(metadatas[1], metadataDTOAddress); + assertMetadataInfo(metadatas[2], metadataDTONamespace); + }); + + it('getAccountMetadataByKey', async () => { + when(metadataRoutesApi.getAccountMetadataByKey(address.plain(), 'aaa')).thenReturn(Promise.resolve({ + response, + body: metadataEntriesDTO, + })); + const metadatas = await metadataRepository.getAccountMetadataByKey(address, 'aaa').toPromise(); + assertMetadataInfo(metadatas[0], metadataDTOMosaic); + assertMetadataInfo(metadatas[1], metadataDTOAddress); + assertMetadataInfo(metadatas[2], metadataDTONamespace); + }); + + it('getAccountMetadataByKeyAndSender', async () => { + when(metadataRoutesApi.getAccountMetadataByKeyAndSender(address.plain(), 'aaa', 'sender')).thenReturn(Promise.resolve({ + response, + body: metadataDTOMosaic, + })); + const metadata = await metadataRepository.getAccountMetadataByKeyAndSender(address, 'aaa', 'sender').toPromise(); + assertMetadataInfo(metadata, metadataDTOMosaic); + }); + + it('getMosaicMetadata', async () => { + when(metadataRoutesApi.getMosaicMetadata(mosaicId.toHex(), 1, 'a', '-id')).thenReturn(Promise.resolve({ + response, + body: metadataEntriesDTO, + })); + const metadatas = await metadataRepository.getMosaicMetadata(mosaicId, new QueryParams({ + pageSize: 1, + id: 'a', + })).toPromise(); + expect(metadatas.length).to.be.equals(3); + assertMetadataInfo(metadatas[0], metadataDTOMosaic); + assertMetadataInfo(metadatas[1], metadataDTOAddress); + assertMetadataInfo(metadatas[2], metadataDTONamespace); + }); + + it('getMosaicMetadataByKey', async () => { + when(metadataRoutesApi.getMosaicMetadataByKey(mosaicId.toHex(), 'aaa')).thenReturn(Promise.resolve({ + response, + body: metadataEntriesDTO, + })); + const metadatas = await metadataRepository.getMosaicMetadataByKey(mosaicId, 'aaa').toPromise(); + assertMetadataInfo(metadatas[0], metadataDTOMosaic); + assertMetadataInfo(metadatas[1], metadataDTOAddress); + assertMetadataInfo(metadatas[2], metadataDTONamespace); + }); + + it('getMosaicMetadataByKeyAndSender', async () => { + when(metadataRoutesApi.getMosaicMetadataByKeyAndSender(mosaicId.toHex(), 'aaa', 'sender')).thenReturn(Promise.resolve({ + response, + body: metadataDTOMosaic, + })); + const metadata = await metadataRepository.getMosaicMetadataByKeyAndSender(mosaicId, 'aaa', 'sender').toPromise(); + assertMetadataInfo(metadata, metadataDTOMosaic); + }); + + it('getNamespaceMetadata', async () => { + when(metadataRoutesApi.getNamespaceMetadata(namespaceId.toHex(), 2, 'a', '-id')).thenReturn(Promise.resolve({ + response, + body: metadataEntriesDTO, + })); + const metadatas = await metadataRepository.getNamespaceMetadata(namespaceId, new QueryParams({ + pageSize: 2, + id: 'a', + })).toPromise(); + expect(metadatas.length).to.be.equals(3); + assertMetadataInfo(metadatas[0], metadataDTOMosaic); + assertMetadataInfo(metadatas[1], metadataDTOAddress); + assertMetadataInfo(metadatas[2], metadataDTONamespace); + }); + + it('getNamespaceMetadataByKey', async () => { + when(metadataRoutesApi.getNamespaceMetadataByKey(namespaceId.toHex(), 'bbb')).thenReturn(Promise.resolve({ + response, + body: metadataEntriesDTO, + })); + const metadatas = await metadataRepository.getNamespaceMetadataByKey(namespaceId, 'bbb').toPromise(); + assertMetadataInfo(metadatas[0], metadataDTOMosaic); + assertMetadataInfo(metadatas[1], metadataDTOAddress); + assertMetadataInfo(metadatas[2], metadataDTONamespace); + }); + + it('getNamespaceMetadataByKeyAndSender', async () => { + when(metadataRoutesApi.getNamespaceMetadataByKeyAndSender(namespaceId.toHex(), 'cccc', 'sender1')).thenReturn(Promise.resolve({ + response, + body: metadataDTOMosaic, + })); + const metadata = await metadataRepository.getNamespaceMetadataByKeyAndSender(namespaceId, 'cccc', 'sender1').toPromise(); + assertMetadataInfo(metadata, metadataDTOMosaic); + }); + +});