Skip to content
94 changes: 94 additions & 0 deletions e2e/service/AccountService.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* 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 { Account } from '../../src/model/account/Account';
import { NetworkType } from '../../src/model/network/NetworkType';
import { Deadline } from '../../src/model/transaction/Deadline';
import { UInt64 } from '../../src/model/UInt64';
import { IntegrationTestHelper } from '../infrastructure/IntegrationTestHelper';
import { AccountService } from '../../src/service/AccountService';
import { NamespaceRegistrationTransaction } from '../../src/model/transaction/NamespaceRegistrationTransaction';
import { NamespaceId } from '../../src/model/namespace/NamespaceId';

describe('AccountService', () => {
const helper = new IntegrationTestHelper();
let generationHash: string;
let account: Account;
let networkType: NetworkType;
let accountService: AccountService;
let namespaceId: NamespaceId;
const name = 'root-test-namespace-' + Math.floor(Math.random() * 10000);

before(() => {
return helper.start().then(() => {
account = helper.account;
generationHash = helper.generationHash;
networkType = helper.networkType;
accountService = new AccountService(helper.repositoryFactory);
});
});
before(() => {
return helper.listener.open();
});

after(() => {
helper.listener.close();
});

/**
* =========================
* Setup test data
* =========================
*/
describe('Create a namespace', () => {
it('Announce NamespaceRegistrationTransaction', () => {
const registerNamespaceTransaction = NamespaceRegistrationTransaction.createRootNamespace(
Deadline.create(),
name,
UInt64.fromUint(300000),
networkType,
helper.maxFee,
);
namespaceId = new NamespaceId(name);
const signedTransaction = registerNamespaceTransaction.signWith(account, generationHash);
return helper.announce(signedTransaction);
});
});

/**
* =========================
* Test
* =========================
*/
describe('call accountInfoWithResolvedMosaic', () => {
it('accountInfoWithResolvedMosaic', async () => {
const info = await accountService.accountInfoWithResolvedMosaic([account.address]).toPromise();
expect(info).to.not.be.undefined;
expect(info[0].resolvedMosaics).to.not.be.undefined;
expect(info[0].resolvedMosaics?.length).to.be.greaterThan(0);
});
});

describe('call accountNamespacesWithName', () => {
it('accountNamespacesWithName', async () => {
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);
});
});
});
23 changes: 23 additions & 0 deletions src/model/account/AccountInfoResolvedMosaic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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 { AccountInfo } from './AccountInfo';
import { ResolvedMosaic } from '../mosaic/ResolvedMosaic';

/**
* Account info with resolved mosaic
*/
export type AccountInfoResolvedMosaic = AccountInfo & { resolvedMosaics?: ResolvedMosaic[] };
3 changes: 3 additions & 0 deletions src/model/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export * from './account/MultisigAccountGraphInfo';
export * from './account/MultisigAccountInfo';
export * from './account/PublicAccount';
export * from './account/AccountNames';
export * from './account/AccountInfoResolvedMosaic';

// Blockchain
export * from './blockchain/BlockchainScore';
Expand All @@ -51,6 +52,7 @@ export * from './mosaic/NetworkCurrencyLocal';
export * from './mosaic/NetworkCurrencyPublic';
export * from './mosaic/NetworkHarvestLocal';
export * from './mosaic/MosaicNames';
export * from './mosaic/ResolvedMosaic';

// Mosaic
export * from './metadata/Metadata';
Expand All @@ -69,6 +71,7 @@ export * from './namespace/NamespaceName';
export * from './namespace/NamespaceRegistrationType';
export * from './namespace/AliasAction';
export * from './namespace/EmptyAlias';
export * from './namespace/NamespaceInfoWithName';

// Network

Expand Down
23 changes: 23 additions & 0 deletions src/model/mosaic/ResolvedMosaic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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 { NamespaceName } from '../namespace/NamespaceName';
import { Mosaic } from './Mosaic';

/**
* Resolved mosaic model with namespace name
*/
export type ResolvedMosaic = Mosaic & { namespaceName?: NamespaceName };
22 changes: 22 additions & 0 deletions src/model/namespace/NamespaceInfoWithName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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 { NamespaceInfo } from './NamespaceInfo';

/**
* Resolved mosaic model with namespace name
*/
export type NamespaceInfoWithName = NamespaceInfo & { namespaceName?: string };
137 changes: 137 additions & 0 deletions src/service/AccountService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* 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 { Observable, of } from 'rxjs';
import { map, withLatestFrom } from 'rxjs/operators';
import { RepositoryFactory } from '../infrastructure/RepositoryFactory';
import { AccountRepository } from '../infrastructure/AccountRepository';
import { NamespaceRepository } from '../infrastructure/NamespaceRepository';
import { Address } from '../model/account/Address';
import { mergeMap } from 'rxjs/operators';
import { DtoMapping } from '../core/utils/DtoMapping';
import { IAccountService } from './interfaces/IAccountService';
import { NamespaceInfoWithName } from '../model/namespace/NamespaceInfoWithName';
import { ResolvedMosaic } from '../model/mosaic/ResolvedMosaic';
import { Mosaic } from '../model/mosaic/Mosaic';
import { MosaicId } from '../model/mosaic/MosaicId';
import { NamespaceId } from '../model/namespace/NamespaceId';
import { AccountInfoResolvedMosaic } from '../model/account/AccountInfoResolvedMosaic';
import { AccountInfo } from '../model/account/AccountInfo';
import { NamespaceName } from '../model/namespace/NamespaceName';
/**
* Account Service
*/
export class AccountService implements IAccountService {
private readonly accountRepository: AccountRepository;

private readonly namespaceRepository: NamespaceRepository;

/**
* Constructor
* @param repositoryFactory
*/
constructor(public readonly repositoryFactory: RepositoryFactory) {
this.accountRepository = repositoryFactory.createAccountRepository();
this.namespaceRepository = repositoryFactory.createNamespaceRepository();
}

/**
* Get account info with resolved mosaic
* @param addresses Array of addresses
*/
public accountInfoWithResolvedMosaic(addresses: Address[]): Observable<AccountInfoResolvedMosaic[]> {
const accountInfoObservable = this.accountRepository.getAccountsInfo(addresses);
const distinctNames = accountInfoObservable.pipe(
mergeMap((info) => {
const namespaceIds = this.getDistinctNamespaceIdFromAccountInfors(info);
if (namespaceIds.length) {
return this.namespaceRepository.getNamespacesName(namespaceIds);
}
return of([]);
}),
);

return accountInfoObservable.pipe(
withLatestFrom(distinctNames),
map(([infos, names]) => {
return infos.map((info) => {
const resolved = this.resolveMosaics(info.mosaics, names);
return DtoMapping.assign(info, { resolvedMosaics: resolved });
});
}),
);
}

/**
* Get namespace info for account with namespace name
* @param addresses Array of addresses
* @returns {Observable<NamespaceInfoWithName[]>}
*/
public accountNamespacesWithName(addresses: Address[]): Observable<NamespaceInfoWithName[]> {
return this.namespaceRepository.getNamespacesFromAccounts(addresses).pipe(
mergeMap((infos) => {
const namespaceIds = infos.map((i) => i.id);
return this.namespaceRepository.getNamespacesName(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 });
});
}),
);
}),
);
}

/**
* Resolve mosaics provides namespace names
* @param mosaics unresolved mosaics
* @return {ResolvedMosaic[]}
*/
private resolveMosaics(mosaics: Mosaic[], names: NamespaceName[]): ResolvedMosaic[] {
return mosaics.map((mosaic) => {
if (mosaic.id instanceof MosaicId) {
return mosaic as ResolvedMosaic;
} else {
const name = names.find((f) => f.namespaceId.equals(mosaic.id));
if (name) {
return DtoMapping.assign(mosaic, { namespaceName: name });
} else {
return mosaic as ResolvedMosaic;
}
}
});
}

/**
* Get distince list of namespacesIds from list of accountInfos
* @param accountInfo List of account info
* @returns {NamespaceId[]}
*/
private getDistinctNamespaceIdFromAccountInfors(accountInfo: AccountInfo[]): NamespaceId[] {
const namespaceIds: NamespaceId[] = [];
accountInfo.forEach((info) => {
info.mosaics.forEach((mosaic) => {
if (mosaic.id instanceof NamespaceId) {
if (!namespaceIds.find((n) => n.equals(mosaic.id))) {
namespaceIds.push(mosaic.id);
}
}
});
});
return namespaceIds;
}
}
2 changes: 1 addition & 1 deletion src/service/BlockService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { UInt64 } from '../model/UInt64';
import { IBlockService } from './interfaces/IBlockService';

/**
* Transaction Service
* Block Service
*/
export class BlockService implements IBlockService {
private readonly blockRepository: BlockRepository;
Expand Down
38 changes: 38 additions & 0 deletions src/service/interfaces/IAccountService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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 { Observable } from 'rxjs';
import { Address } from '../../model/account/Address';
import { AccountInfoResolvedMosaic } from '../../model/account/AccountInfoResolvedMosaic';
import { NamespaceInfoWithName } from '../../model/namespace/NamespaceInfoWithName';

/**
* Block Service Interface
*/
export interface IAccountService {
/**
* Get account info with resolved mosaic
* @param addresses Array of addresses
*/
accountInfoWithResolvedMosaic(addresses: Address[]): Observable<AccountInfoResolvedMosaic[]>;

/**
* Get namespace info for account with namespace name
* @param addresses Array of addresses
* @returns {Observable<NamespaceInfoWithName[]>}
*/
accountNamespacesWithName(addresses: Address[]): Observable<NamespaceInfoWithName[]>;
}
2 changes: 2 additions & 0 deletions src/service/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
* limitations under the License.
*/

export * from './AccountService';
export * from './NamespaceService';
export * from './MosaicService';
export * from './AggregateTransactionService';
export * from './MetadataTransactionService';
export * from './MosaicRestrictionTransactionService';
export * from './TransactionService';
export * from './BlockService';
export * from './interfaces/IAccountService';
export * from './interfaces/IBlockService';
export * from './interfaces/ITransactionService';
Loading