From 8bf926b0032a0f44c1c644d28137bf3b2518374d Mon Sep 17 00:00:00 2001 From: Toms Date: Fri, 6 Dec 2024 18:46:15 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20get=20asset=20groups=20f?= =?UTF-8?q?or=20did?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/identities/identities.controller.spec.ts | 26 +++++++++++++- src/identities/identities.controller.ts | 35 +++++++++++++++++-- src/identities/identities.service.spec.ts | 30 +++++++++++++++- src/identities/identities.service.ts | 7 ++++ .../models/asset-with-group.model.ts | 35 +++++++++++++++++++ src/test-utils/service-mocks.ts | 1 + 6 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 src/identities/models/asset-with-group.model.ts diff --git a/src/identities/identities.controller.spec.ts b/src/identities/identities.controller.spec.ts index e69079ad..7a95c3ad 100644 --- a/src/identities/identities.controller.spec.ts +++ b/src/identities/identities.controller.spec.ts @@ -1,12 +1,14 @@ -import { DeepMocked } from '@golevelup/ts-jest'; +import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test } from '@nestjs/testing'; import { BigNumber } from '@polymeshassociation/polymesh-sdk'; import { + Asset, AuthorizationType, CddClaim, ClaimData, ClaimScope, ClaimType, + CustomPermissionGroup, GenericAuthorizationData, ResultSet, } from '@polymeshassociation/polymesh-sdk/types'; @@ -27,6 +29,7 @@ import { RegisterIdentityDto } from '~/identities/dto/register-identity.dto'; import { IdentitiesController } from '~/identities/identities.controller'; import { IdentitiesService } from '~/identities/identities.service'; import { AccountModel } from '~/identities/models/account.model'; +import { AssetWithGroupModel } from '~/identities/models/asset-with-group.model'; import { IdentityModel } from '~/identities/models/identity.model'; import { IdentitySignerModel } from '~/identities/models/identity-signer.model'; import { mockPolymeshLoggerProvider } from '~/logger/mock-polymesh-logger'; @@ -755,4 +758,25 @@ describe('IdentitiesController', () => { ); }); }); + + describe('getAssetPermissions', () => { + it('should return the Assets for which the Identity has permissions', async () => { + const asset = createMock({ + id: '3616b82e-8e10-80ae-dc95-2ea28b9db8b3', + ticker: 'SOME_TICKER', + }); + const assetGroups = [ + { asset, group: createMock({ id: new BigNumber(1), asset }) }, + ]; + mockIdentitiesService.findDidExternalAgentOf.mockResolvedValue(assetGroups); + + const result = await controller.getAssetPermissions({ did }); + + expect(result).toEqual( + new ResultsModel({ + results: assetGroups.map(assetGroup => new AssetWithGroupModel(assetGroup)), + }) + ); + }); + }); }); diff --git a/src/identities/identities.controller.ts b/src/identities/identities.controller.ts index bb77491f..7da56162 100644 --- a/src/identities/identities.controller.ts +++ b/src/identities/identities.controller.ts @@ -16,6 +16,7 @@ import { ClaimType, FungibleAsset, NftCollection, + PermissionGroupType, TickerReservation, Venue, } from '@polymeshassociation/polymesh-sdk/types'; @@ -50,12 +51,12 @@ import { ResultsModel } from '~/common/models/results.model'; import { handleServiceResult, TransactionResponseModel } from '~/common/utils'; import { createDividendDistributionDetailsModel } from '~/corporate-actions/corporate-actions.util'; import { DividendDistributionDetailsModel } from '~/corporate-actions/models/dividend-distribution-details.model'; -import { DeveloperTestingService } from '~/developer-testing/developer-testing.service'; import { AddSecondaryAccountParamsDto } from '~/identities/dto/add-secondary-account-params.dto'; import { RegisterIdentityDto } from '~/identities/dto/register-identity.dto'; import { RotatePrimaryKeyParamsDto } from '~/identities/dto/rotate-primary-key-params.dto'; import { IdentitiesService } from '~/identities/identities.service'; import { createIdentityModel } from '~/identities/identities.util'; +import { AssetWithGroupModel } from '~/identities/models/asset-with-group.model'; import { CreatedIdentityModel } from '~/identities/models/created-identity.model'; import { IdentityModel } from '~/identities/models/identity.model'; import { createIdentityResolver } from '~/identities/models/identity.util'; @@ -75,7 +76,6 @@ export class IdentitiesController { private readonly authorizationsService: AuthorizationsService, private readonly claimsService: ClaimsService, private readonly tickerReservationsService: TickerReservationsService, - private readonly developerTestingService: DeveloperTestingService, private readonly logger: PolymeshLogger ) { logger.setContext(IdentitiesController.name); @@ -765,4 +765,35 @@ export class IdentitiesController { ), }); } + + @ApiTags('assets') + @ApiOperation({ + summary: 'Fetch all Assets for which an Identity has permissions', + }) + @ApiParam({ + name: 'did', + description: 'The DID of the Identity for which the Asset permissions are to be fetched', + type: 'string', + example: '0x0600000000000000000000000000000000000000000000000000000000000000', + }) + @ApiArrayResponse('string', { + description: 'List of Assets for which the Identity has permissions', + paginated: false, + example: [ + { + asset: 'SOME_TICKER', + group: { + type: PermissionGroupType.Full, + assetId: '3616b82e-8e10-80ae-dc95-2ea28b9db8b3', + ticker: 'SOME_TICKER', + }, + }, + ], + }) + @Get(':did/external-agent') + async getAssetPermissions(@Param() { did }: DidDto): Promise> { + const results = await this.identitiesService.findDidExternalAgentOf(did); + + return new ResultsModel({ results: results.map(result => new AssetWithGroupModel(result)) }); + } } diff --git a/src/identities/identities.service.spec.ts b/src/identities/identities.service.spec.ts index 38dfbca1..55060b62 100644 --- a/src/identities/identities.service.spec.ts +++ b/src/identities/identities.service.spec.ts @@ -1,9 +1,15 @@ /* eslint-disable import/first */ const mockIsPolymeshTransaction = jest.fn(); +import { createMock } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { BigNumber } from '@polymeshassociation/polymesh-sdk'; -import { TxTags } from '@polymeshassociation/polymesh-sdk/types'; +import { + Asset, + CustomPermissionGroup, + Identity, + TxTags, +} from '@polymeshassociation/polymesh-sdk/types'; import { AccountsService } from '~/accounts/accounts.service'; import { MockDistributionWithDetails } from '~/corporate-actions/mocks/distribution-with-details.mock'; @@ -308,4 +314,26 @@ describe('IdentitiesService', () => { expect(result).toEqual(mockDistributions); }); }); + + describe('findDidExternalAgentOf', () => { + it('should return the list of AssetsGroups for which the Identity has permissions', async () => { + const asset = createMock({ + id: '3616b82e-8e10-80ae-dc95-2ea28b9db8b3', + ticker: 'SOME_TICKER', + }); + const assetGroups = [ + { asset, group: createMock({ id: new BigNumber(1), asset }) }, + ]; + const mockIdentity = createMock({ + did, + assetPermissions: { get: jest.fn().mockResolvedValue(assetGroups) }, + }); + + const findOneSpy = jest.spyOn(service, 'findOne'); + findOneSpy.mockResolvedValue(mockIdentity); + + const result = await service.findDidExternalAgentOf(did); + expect(result).toEqual(assetGroups); + }); + }); }); diff --git a/src/identities/identities.service.ts b/src/identities/identities.service.ts index 79b31287..b3ff8b80 100644 --- a/src/identities/identities.service.ts +++ b/src/identities/identities.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@nestjs/common'; import { BigNumber } from '@polymeshassociation/polymesh-sdk'; import { + AssetWithGroup, AuthorizationRequest, DistributionWithDetails, FungibleAsset, @@ -159,4 +160,10 @@ export class IdentitiesService { return identity.getPendingDistributions(); } + + public async findDidExternalAgentOf(did: string): Promise { + const identity = await this.findOne(did); + + return identity.assetPermissions.get(); + } } diff --git a/src/identities/models/asset-with-group.model.ts b/src/identities/models/asset-with-group.model.ts new file mode 100644 index 00000000..4d160789 --- /dev/null +++ b/src/identities/models/asset-with-group.model.ts @@ -0,0 +1,35 @@ +/* istanbul ignore file */ + +import { ApiProperty } from '@nestjs/swagger'; +import { + Asset, + CustomPermissionGroup, + KnownPermissionGroup, + PermissionGroupType, +} from '@polymeshassociation/polymesh-sdk/types'; + +import { FromEntity } from '~/common/decorators'; + +export class AssetWithGroupModel { + @ApiProperty({ + description: 'The Asset ID to which the Identity has permissions', + example: '3616b82e-8e10-80ae-dc95-2ea28b9db8b3', + }) + @FromEntity() + readonly asset: Asset; + + @ApiProperty({ + description: 'The assigned group details', + example: { + type: PermissionGroupType.Full, + assetId: '3616b82e-8e10-80ae-dc95-2ea28b9db8b3', + ticker: 'SOME_TICKER', + }, + }) + @FromEntity() + readonly group: KnownPermissionGroup | CustomPermissionGroup; + + constructor(model: AssetWithGroupModel) { + Object.assign(this, model); + } +} diff --git a/src/test-utils/service-mocks.ts b/src/test-utils/service-mocks.ts index 338b499c..d08053df 100644 --- a/src/test-utils/service-mocks.ts +++ b/src/test-utils/service-mocks.ts @@ -158,6 +158,7 @@ export class MockIdentitiesService { isAssetPreApproved = jest.fn(); getPreApprovedAssets = jest.fn(); getPendingDistributions = jest.fn(); + findDidExternalAgentOf = jest.fn(); } export class MockSettlementsService {