Skip to content

Commit 013d8e4

Browse files
sansanmpastecki
authored andcommitted
feat: 🎸 get account details
1 parent d17eb12 commit 013d8e4

8 files changed

+181
-2
lines changed

‎src/accounts/accounts.controller.spec.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,22 @@ import { Test, TestingModule } from '@nestjs/testing';
44
import { BigNumber } from '@polymeshassociation/polymesh-sdk';
55
import {
66
ExtrinsicsOrderBy,
7+
Identity,
78
PermissionType,
89
TxGroup,
910
TxTags,
1011
} from '@polymeshassociation/polymesh-sdk/types';
1112
import { Response } from 'express';
1213

1314
import { AccountsController } from '~/accounts/accounts.controller';
14-
import { AccountsService } from '~/accounts/accounts.service';
15+
import { AccountDetails, AccountsService } from '~/accounts/accounts.service';
1516
import { PermissionedAccountDto } from '~/accounts/dto/permissioned-account.dto';
1617
import { ExtrinsicModel } from '~/common/models/extrinsic.model';
1718
import { PaginatedResultsModel } from '~/common/models/paginated-results.model';
1819
import { PermissionsLikeDto } from '~/identities/dto/permissions-like.dto';
20+
import * as identityUtil from '~/identities/identities.util';
1921
import { AccountModel } from '~/identities/models/account.model';
22+
import { IdentityModel } from '~/identities/models/identity.model';
2023
import { IdentitySignerModel } from '~/identities/models/identity-signer.model';
2124
import { NetworkService } from '~/network/network.service';
2225
import { SubsidyService } from '~/subsidy/subsidy.service';
@@ -291,4 +294,22 @@ describe('AccountsController', () => {
291294
expect(result).toEqual(new AccountModel({ address: testAccount.address }));
292295
});
293296
});
297+
298+
describe('getDetails', () => {
299+
it('should call the service and return AccountDetailsModel', async () => {
300+
const fakeIdentityModel = 'fakeIdentityModel' as unknown as IdentityModel;
301+
jest.spyOn(identityUtil, 'createIdentityModel').mockResolvedValue(fakeIdentityModel);
302+
303+
const mockResponse: AccountDetails = {
304+
identity: new MockIdentity() as unknown as Identity,
305+
multiSigDetails: null,
306+
};
307+
308+
mockAccountsService.getDetails.mockReturnValue(mockResponse);
309+
310+
const result = await controller.getAccountDetails({ account: '5xdd' });
311+
312+
expect(result).toEqual({ identity: fakeIdentityModel });
313+
});
314+
});
294315
});

‎src/accounts/accounts.controller.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import { ModifyPermissionsDto } from '~/accounts/dto/modify-permissions.dto';
2828
import { RevokePermissionsDto } from '~/accounts/dto/revoke-permissions.dto';
2929
import { TransactionHistoryFiltersDto } from '~/accounts/dto/transaction-history-filters.dto';
3030
import { TransferPolyxDto } from '~/accounts/dto/transfer-polyx.dto';
31+
import { AccountDetailsModel } from '~/accounts/models/account-details.model';
32+
import { MultiSigDetailsModel } from '~/accounts/models/multi-sig-details.model';
3133
import { PermissionsModel } from '~/accounts/models/permissions.model';
3234
import { BalanceModel } from '~/assets/models/balance.model';
3335
import { ApiArrayResponse, ApiTransactionResponse } from '~/common/decorators/swagger';
@@ -36,6 +38,7 @@ import { ExtrinsicModel } from '~/common/models/extrinsic.model';
3638
import { PaginatedResultsModel } from '~/common/models/paginated-results.model';
3739
import { TransactionQueueModel } from '~/common/models/transaction-queue.model';
3840
import { handleServiceResult, TransactionResponseModel } from '~/common/utils';
41+
import { createIdentityModel, createSignerModel } from '~/identities/identities.util';
3942
import { AccountModel } from '~/identities/models/account.model';
4043
import { IdentitySignerModel } from '~/identities/models/identity-signer.model';
4144
import { NetworkService } from '~/network/network.service';
@@ -301,4 +304,43 @@ export class AccountsController {
301304

302305
return new AccountModel({ address });
303306
}
307+
308+
@ApiOperation({
309+
summary: 'Get Account details',
310+
description:
311+
'This endpoint retrieves the Account details for the given Account address. This includes the associated Identity DID, primary account for that Identity and Secondary Accounts with the Permissions and the Subsidy details',
312+
})
313+
@ApiParam({
314+
name: 'account',
315+
description: 'The Account address whose details is to be fetched',
316+
type: 'string',
317+
example: '5GwwYnwCYcJ1Rkop35y7SDHAzbxrCkNUDD4YuCUJRPPXbvyV',
318+
})
319+
@ApiOkResponse({
320+
description: 'Account details',
321+
type: AccountDetailsModel,
322+
})
323+
@ApiNotFoundResponse({
324+
description: 'No Account found for the given address',
325+
})
326+
@Get(':account')
327+
async getAccountDetails(@Param() { account }: AccountParamsDto): Promise<AccountDetailsModel> {
328+
const { identity, multiSigDetails } = await this.accountsService.getDetails(account);
329+
330+
let identityModel;
331+
let multiSigDetailsModel;
332+
333+
if (identity) {
334+
identityModel = await createIdentityModel(identity);
335+
}
336+
337+
if (multiSigDetails) {
338+
multiSigDetailsModel = new MultiSigDetailsModel({
339+
signers: multiSigDetails.signers.map(createSignerModel),
340+
requiredSignatures: multiSigDetails.requiredSignatures,
341+
});
342+
}
343+
344+
return new AccountDetailsModel({ identity: identityModel, multiSig: multiSigDetailsModel });
345+
}
304346
}

‎src/accounts/accounts.service.spec.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const mockIsPolymeshTransaction = jest.fn();
33
import { Test, TestingModule } from '@nestjs/testing';
44
import { BigNumber } from '@polymeshassociation/polymesh-sdk';
55
import { ExtrinsicsOrderBy } from '@polymeshassociation/polymesh-sdk/middleware/types';
6-
import { PermissionType, TxGroup, TxTags } from '@polymeshassociation/polymesh-sdk/types';
6+
import { Account, PermissionType, TxGroup, TxTags } from '@polymeshassociation/polymesh-sdk/types';
77

88
import { AccountsService } from '~/accounts/accounts.service';
99
import { PermissionedAccountDto } from '~/accounts/dto/permissioned-account.dto';
@@ -17,6 +17,7 @@ import {
1717
MockAccount,
1818
MockAsset,
1919
MockIdentity,
20+
MockMultiSig,
2021
MockPolymesh,
2122
MockTransaction,
2223
} from '~/test-utils/mocks';
@@ -357,4 +358,28 @@ describe('AccountsService', () => {
357358
);
358359
});
359360
});
361+
362+
describe('getDetails', () => {
363+
it('should return the Account details', async () => {
364+
const mockAccount = new MockAccount();
365+
const mockIdentity = new MockIdentity();
366+
const mockMultiSig = new MockMultiSig();
367+
368+
const findOneSpy = jest.spyOn(service, 'findOne');
369+
findOneSpy.mockResolvedValue(mockAccount as unknown as Account);
370+
371+
mockMultiSig.details.mockResolvedValue({ signers: [], requiredSignatures: new BigNumber(2) });
372+
mockAccount.getIdentity.mockResolvedValue(mockIdentity);
373+
mockAccount.getMultiSig.mockResolvedValue(mockMultiSig);
374+
375+
const result = await service.getDetails('address');
376+
377+
const fakeResult = {
378+
identity: mockIdentity,
379+
multiSigDetails: { signers: [], requiredSignatures: new BigNumber(2) },
380+
};
381+
382+
expect(result).toStrictEqual(fakeResult);
383+
});
384+
});
360385
});

‎src/accounts/accounts.service.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {
44
AccountBalance,
55
ExtrinsicData,
66
Identity,
7+
MultiSig,
8+
MultiSigDetails,
79
Permissions,
810
ResultSet,
911
} from '@polymeshassociation/polymesh-sdk/types';
@@ -18,6 +20,11 @@ import { PolymeshService } from '~/polymesh/polymesh.service';
1820
import { TransactionsService } from '~/transactions/transactions.service';
1921
import { handleSdkError } from '~/transactions/transactions.util';
2022

23+
export type AccountDetails = {
24+
identity: Identity | null;
25+
multiSigDetails: MultiSigDetails | null;
26+
};
27+
2128
@Injectable()
2229
export class AccountsService {
2330
constructor(
@@ -114,4 +121,25 @@ export class AccountsService {
114121
options
115122
);
116123
}
124+
125+
private async getMultiSigDetails(multiSig: MultiSig | null): Promise<MultiSigDetails | null> {
126+
if (!multiSig) {
127+
return null;
128+
}
129+
130+
return await multiSig.details();
131+
}
132+
133+
public async getDetails(address: string): Promise<AccountDetails> {
134+
const account = await this.findOne(address);
135+
136+
const [identity, multiSig] = await Promise.all([account.getIdentity(), account.getMultiSig()]);
137+
138+
const multiSigDetails = await this.getMultiSigDetails(multiSig);
139+
140+
return {
141+
identity,
142+
multiSigDetails,
143+
};
144+
}
117145
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/* istanbul ignore file */
2+
3+
import { ApiPropertyOptional } from '@nestjs/swagger';
4+
import { Type } from 'class-transformer';
5+
6+
import { MultiSigDetailsModel } from '~/accounts/models/multi-sig-details.model';
7+
import { IdentityModel } from '~/identities/models/identity.model';
8+
9+
export class AccountDetailsModel {
10+
@ApiPropertyOptional({
11+
description: 'Static data (and identifiers) of the newly created Identity',
12+
type: IdentityModel,
13+
})
14+
@Type(() => IdentityModel)
15+
readonly identity?: IdentityModel;
16+
17+
@ApiPropertyOptional({
18+
description: 'MultiSig Account details',
19+
type: MultiSigDetailsModel,
20+
})
21+
@Type(() => MultiSigDetailsModel)
22+
readonly multiSig?: MultiSigDetailsModel;
23+
24+
constructor(model: AccountDetailsModel) {
25+
Object.assign(this, model);
26+
}
27+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* istanbul ignore file */
2+
3+
import { ApiProperty } from '@nestjs/swagger';
4+
import { BigNumber } from '@polymeshassociation/polymesh-sdk';
5+
import { Type } from 'class-transformer';
6+
7+
import { FromBigNumber } from '~/common/decorators/transformation';
8+
import { SignerModel } from '~/identities/models/signer.model';
9+
10+
export class MultiSigDetailsModel {
11+
@ApiProperty({
12+
description: 'Secondary accounts with permissions',
13+
isArray: true,
14+
type: SignerModel,
15+
})
16+
@Type(() => SignerModel)
17+
readonly signers: SignerModel[];
18+
19+
@ApiProperty({
20+
description: 'Required signers',
21+
type: 'string',
22+
example: '2',
23+
})
24+
@FromBigNumber()
25+
readonly requiredSignatures: BigNumber;
26+
27+
constructor(model: MultiSigDetailsModel) {
28+
Object.assign(this, model);
29+
}
30+
}

‎src/test-utils/mocks.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,12 +455,17 @@ export class MockAccount {
455455
getPermissions = jest.fn();
456456
getIdentity = jest.fn();
457457
getSubsidy = jest.fn();
458+
getMultiSig = jest.fn();
458459

459460
constructor(address = 'address') {
460461
this.address = address;
461462
}
462463
}
463464

465+
export class MockMultiSig {
466+
details = jest.fn();
467+
}
468+
464469
export function createMockMetadataEntry(
465470
partial: PartialFuncReturn<MetadataEntry> = {
466471
id: new BigNumber(1),

‎src/test-utils/service-mocks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ export class MockAccountsService {
105105
revokePermissions = jest.fn();
106106
getTreasuryAccount = jest.fn();
107107
getIdentity = jest.fn();
108+
getDetails = jest.fn();
108109
}
109110

110111
export class MockEventsService {

0 commit comments

Comments
 (0)