diff --git a/src/api/entities/Venue/__tests__/index.ts b/src/api/entities/Venue/__tests__/index.ts index d555d6bafc..0f4c4530ee 100644 --- a/src/api/entities/Venue/__tests__/index.ts +++ b/src/api/entities/Venue/__tests__/index.ts @@ -60,11 +60,50 @@ describe('Venue class', () => { }); }); + describe('method: exists', () => { + afterAll(() => { + sinon.restore(); + }); + + test('should return whether if the venue exists or not', async () => { + const owner = 'someDid'; + + entityMockUtils.configureMocks({ identityOptions: { did: owner } }); + sinon + .stub(utilsConversionModule, 'numberToU64') + .withArgs(id, context) + .returns(rawId); + + dsMockUtils + .createQueryStub('settlement', 'venueInfo') + .withArgs(rawId) + .resolves(dsMockUtils.createMockOption()); + + const result = await venue.exists(); + + expect(result).toEqual(false); + }); + }); + describe('method: details', () => { afterAll(() => { sinon.restore(); }); + test("should throw an error if the venue doesn't exist", async () => { + dsMockUtils + .createQueryStub('settlement', 'venueInfo') + .resolves(dsMockUtils.createMockOption()); + + entityMockUtils.configureMocks({ + numberedPortfolioOptions: { + exists: false, + }, + }); + + return expect(venue.details()).rejects.toThrow("The Venue doesn't exist"); + }); + test('should return the Venue details', async () => { const description = 'someDescription'; const type = VenueType.Other; @@ -106,6 +145,20 @@ describe('Venue class', () => { sinon.restore(); }); + test("should throw an error if the venue doesn't exist", async () => { + dsMockUtils + .createQueryStub('settlement', 'venueInfo') + .resolves(dsMockUtils.createMockOption()); + + entityMockUtils.configureMocks({ + numberedPortfolioOptions: { + exists: false, + }, + }); + + return expect(venue.getPendingInstructions()).rejects.toThrow("The Venue doesn't exist"); + }); + test("should return the Venue's pending instructions", async () => { const description = 'someDescription'; const type = VenueType.Other; diff --git a/src/api/entities/Venue/index.ts b/src/api/entities/Venue/index.ts index 67baa89728..07112c73d6 100644 --- a/src/api/entities/Venue/index.ts +++ b/src/api/entities/Venue/index.ts @@ -8,8 +8,9 @@ import { Entity, Identity, Instruction, + PolymeshError, } from '~/internal'; -import { InstructionStatus } from '~/types'; +import { ErrorCode, InstructionStatus } from '~/types'; import { ProcedureMethod } from '~/types/internal'; import { identityIdToString, @@ -61,6 +62,25 @@ export class Venue extends Entity { ); } + /** + * Retrieve if the venue still exists + */ + public async exists(): Promise { + const { + context: { + polymeshApi: { + query: { settlement }, + }, + }, + id, + context, + } = this; + + const venueInfo = await settlement.venueInfo(numberToU64(id, context)); + + return !venueInfo.isEmpty; + } + /** * Retrieve information specific to this venue */ @@ -75,6 +95,15 @@ export class Venue extends Entity { context, } = this; + const exists = await this.exists(); + + if (!exists) { + throw new PolymeshError({ + code: ErrorCode.ValidationError, + message: "The Venue doesn't exist", + }); + } + const venueInfo = await settlement.venueInfo(numberToU64(id, context)); const { creator, details, venue_type: type } = venueInfo.unwrap(); @@ -100,6 +129,15 @@ export class Venue extends Entity { context, } = this; + const exists = await this.exists(); + + if (!exists) { + throw new PolymeshError({ + code: ErrorCode.ValidationError, + message: "The Venue doesn't exist", + }); + } + const venueInfo = await settlement.venueInfo(numberToU64(id, context)); const { instructions: rawInstructions } = venueInfo.unwrap(); diff --git a/src/api/procedures/__tests__/addInstruction.ts b/src/api/procedures/__tests__/addInstruction.ts index d37dc37e54..b6bcb858c9 100644 --- a/src/api/procedures/__tests__/addInstruction.ts +++ b/src/api/procedures/__tests__/addInstruction.ts @@ -25,6 +25,11 @@ import { PolymeshTx } from '~/types/internal'; import * as utilsConversionModule from '~/utils/conversion'; import * as utilsInternalModule from '~/utils/internal'; +jest.mock( + '~/api/entities/Venue', + require('~/testUtils/mocks/entities').mockVenueModule('~/api/entities/Venue') +); + describe('addInstruction procedure', () => { let mockContext: Mocked; let portfolioIdToMeshPortfolioIdStub: sinon.SinonStub; @@ -231,6 +236,11 @@ describe('addInstruction procedure', () => { test('should throw an error if the end block is in the past', async () => { dsMockUtils.configureMocks({ contextOptions: { latestBlock: new BigNumber(1000) } }); + entityMockUtils.configureMocks({ + venueOptions: { + exists: true, + }, + }); const proc = procedureMockUtils.getInstance(mockContext); let error; @@ -246,6 +256,11 @@ describe('addInstruction procedure', () => { test('should add an add and authorize instruction transaction to the queue', async () => { dsMockUtils.configureMocks({ contextOptions: { did: fromDid } }); + entityMockUtils.configureMocks({ + venueOptions: { + exists: true, + }, + }); getCustodianStub.onCall(1).returns({ did: fromDid }); const proc = procedureMockUtils.getInstance(mockContext); @@ -268,6 +283,11 @@ describe('addInstruction procedure', () => { test('should add an add instruction transaction to the queue', async () => { dsMockUtils.configureMocks({ contextOptions: { did: fromDid } }); + entityMockUtils.configureMocks({ + venueOptions: { + exists: true, + }, + }); getCustodianStub.onCall(0).returns({ did: toDid }); const proc = procedureMockUtils.getInstance(mockContext); @@ -294,6 +314,26 @@ describe('addInstruction procedure', () => { expect(result).toBe(instruction); }); + test("should throw an error if the venue doesn't exist", async () => { + entityMockUtils.configureMocks({ + venueOptions: { + exists: false, + }, + }); + + const proc = procedureMockUtils.getInstance(mockContext); + + let error; + + try { + await prepareAddInstruction.call(proc, { ...args, legs: [] }); + } catch (err) { + error = err; + } + + expect(error.message).toBe("The Venue doesn't exist"); + }); + describe('getAuthorization', () => { test('should return the appropriate roles and permissions', async () => { const proc = procedureMockUtils.getInstance(mockContext); diff --git a/src/api/procedures/addInstruction.ts b/src/api/procedures/addInstruction.ts index 27c1b0dd70..c57a88d44d 100644 --- a/src/api/procedures/addInstruction.ts +++ b/src/api/procedures/addInstruction.ts @@ -5,6 +5,7 @@ import BigNumber from 'bignumber.js'; import P from 'bluebird'; import { PortfolioId, Ticker, TxTag, TxTags } from 'polymesh-types/types'; +import { Venue } from '~/api/entities/Venue'; import { assertPortfolioExists } from '~/api/procedures/utils'; import { Context, @@ -76,6 +77,18 @@ export async function prepareAddInstruction( } = this; const { legs, venueId, endBlock, validFrom } = args; + const venue = new Venue({ id: venueId }, context); + const exists = await venue.exists(); + + console.log(exists); + + if (!exists) { + throw new PolymeshError({ + code: ErrorCode.ValidationError, + message: "The Venue doesn't exist", + }); + } + if (!legs.length) { throw new PolymeshError({ code: ErrorCode.ValidationError, diff --git a/src/testUtils/mocks/entities.ts b/src/testUtils/mocks/entities.ts index 0c5c0b22fc..9dba569c9a 100644 --- a/src/testUtils/mocks/entities.ts +++ b/src/testUtils/mocks/entities.ts @@ -130,6 +130,7 @@ interface CurrentAccountOptions extends AccountOptions { interface VenueOptions { id?: BigNumber; details?: Partial; + exists?: boolean; } interface NumberedPortfolioOptions { @@ -193,6 +194,7 @@ let currentAccountGetIdentityStub: SinonStub; let currentAccountGetTransactionHistoryStub: SinonStub; let tickerReservationDetailsStub: SinonStub; let venueDetailsStub: SinonStub; +let venueExistsStub: SinonStub; let instructionDetailsStub: SinonStub; let instructionGetLegsStub: SinonStub; let numberedPortfolioIsOwnedByStub: SinonStub; @@ -443,6 +445,7 @@ const defaultVenueOptions: VenueOptions = { type: VenueType.Distribution, description: 'someDescription', }, + exists: true, }; let venueOptions = defaultVenueOptions; const defaultNumberedPortfolioOptions: NumberedPortfolioOptions = { @@ -542,6 +545,7 @@ function configureVenue(opts: VenueOptions): void { const venue = ({ id: opts.id, details: venueDetailsStub.resolves(details), + exists: venueExistsStub.resolves(opts.exists), } as unknown) as MockVenue; Object.assign(mockInstanceContainer.venue, venue); @@ -559,6 +563,7 @@ function configureVenue(opts: VenueOptions): void { function initVenue(opts?: VenueOptions): void { venueConstructorStub = sinon.stub(); venueDetailsStub = sinon.stub(); + venueExistsStub = sinon.stub(); venueOptions = { ...defaultVenueOptions, ...opts };