Skip to content

Commit a1429fd

Browse files
Merge pull request #125 from kaleido-io/deactivate
Add /deactivatepool API for deleting listeners
2 parents aba1386 + 58de133 commit a1429fd

File tree

5 files changed

+111
-16
lines changed

5 files changed

+111
-16
lines changed

src/event-stream/event-stream.service.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,22 @@ export class EventStreamService {
321321
);
322322
}
323323

324+
async deleteSubscriptionByName(ctx: Context, streamId: string, name: string) {
325+
const existingSubscriptions = await this.getSubscriptions(ctx);
326+
const sub = existingSubscriptions.find(s => s.name === name && s.stream === streamId);
327+
if (!sub) {
328+
this.logger.log(`No subscription found for ${name}`);
329+
return false;
330+
}
331+
await lastValueFrom(
332+
this.http.delete(
333+
new URL(`/subscriptions/${sub.id}`, this.baseUrl).href,
334+
this.requestOptions(ctx),
335+
),
336+
);
337+
return true;
338+
}
339+
324340
connect(
325341
url: string,
326342
topic: string,

src/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
// See the License for the specific language governing permissions and
1515
// limitations under the License.
1616

17-
import { NestApplicationOptions, ShutdownSignal, ValidationPipe } from '@nestjs/common';
17+
import { ShutdownSignal, ValidationPipe } from '@nestjs/common';
1818
import { ConfigService } from '@nestjs/config';
1919
import { NestFactory } from '@nestjs/core';
2020
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';

src/tokens/tokens.controller.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ import {
2828
TokenMint,
2929
TokenPool,
3030
TokenPoolActivate,
31+
TokenPoolDeactivate,
3132
TokenTransfer,
3233
} from './tokens.interfaces';
3334
import { TokensService } from './tokens.service';
34-
import { RequestContext } from '../request-context/request-context.decorator';
35+
import { Context, RequestContext } from '../request-context/request-context.decorator';
3536

3637
@Controller()
3738
export class TokensController {
@@ -40,7 +41,7 @@ export class TokensController {
4041
@Post('init')
4142
@HttpCode(204)
4243
@ApiOperation({ summary: 'Perform one-time initialization (if not auto-initialized)' })
43-
async init(@RequestContext() ctx) {
44+
async init(@RequestContext() ctx: Context) {
4445
await this.service.init(ctx);
4546
}
4647

@@ -53,21 +54,31 @@ export class TokensController {
5354
})
5455
@ApiBody({ type: TokenPool })
5556
@ApiResponse({ status: 202, type: AsyncResponse })
56-
createPool(@RequestContext() ctx, @Body() dto: TokenPool) {
57+
createPool(@RequestContext() ctx: Context, @Body() dto: TokenPool) {
5758
return this.service.createPool(ctx, dto);
5859
}
5960

6061
@Post('activatepool')
6162
@HttpCode(204)
6263
@ApiOperation({
63-
summary: 'Activate a token pool to begin receiving transfer events',
64+
summary: 'Activate a token pool to begin receiving transfer and approval events',
6465
description: 'Will retrigger the token-pool event for this pool as a side-effect',
6566
})
6667
@ApiBody({ type: TokenPoolActivate })
67-
async activatePool(@RequestContext() ctx, @Body() dto: TokenPoolActivate) {
68+
async activatePool(@RequestContext() ctx: Context, @Body() dto: TokenPoolActivate) {
6869
await this.service.activatePool(ctx, dto);
6970
}
7071

72+
@Post('deactivatepool')
73+
@HttpCode(204)
74+
@ApiOperation({
75+
summary: 'Deactivate a token pool to delete all listeners and stop receiving events',
76+
})
77+
@ApiBody({ type: TokenPoolDeactivate })
78+
async deactivatePool(@RequestContext() ctx: Context, @Body() dto: TokenPoolDeactivate) {
79+
await this.service.deactivatePool(ctx, dto);
80+
}
81+
7182
@Post('mint')
7283
@HttpCode(202)
7384
@ApiOperation({
@@ -77,7 +88,7 @@ export class TokensController {
7788
})
7889
@ApiBody({ type: TokenMint })
7990
@ApiResponse({ status: 202, type: AsyncResponse })
80-
mint(@RequestContext() ctx, @Body() dto: TokenMint) {
91+
mint(@RequestContext() ctx: Context, @Body() dto: TokenMint) {
8192
return this.service.mint(ctx, dto);
8293
}
8394

@@ -99,7 +110,7 @@ export class TokensController {
99110
})
100111
@ApiBody({ type: TokenApproval })
101112
@ApiResponse({ status: 202, type: AsyncResponse })
102-
approve(@RequestContext() ctx, @Body() dto: TokenApproval) {
113+
approve(@RequestContext() ctx: Context, @Body() dto: TokenApproval) {
103114
return this.service.approval(ctx, dto);
104115
}
105116

@@ -112,7 +123,7 @@ export class TokensController {
112123
})
113124
@ApiBody({ type: TokenBurn })
114125
@ApiResponse({ status: 202, type: AsyncResponse })
115-
burn(@RequestContext() ctx, @Body() dto: TokenBurn) {
126+
burn(@RequestContext() ctx: Context, @Body() dto: TokenBurn) {
116127
return this.service.burn(ctx, dto);
117128
}
118129

@@ -125,21 +136,21 @@ export class TokensController {
125136
})
126137
@ApiBody({ type: TokenTransfer })
127138
@ApiResponse({ status: 202, type: AsyncResponse })
128-
transfer(@RequestContext() ctx, @Body() dto: TokenTransfer) {
139+
transfer(@RequestContext() ctx: Context, @Body() dto: TokenTransfer) {
129140
return this.service.transfer(ctx, dto);
130141
}
131142

132143
@Get('balance')
133144
@ApiOperation({ summary: 'Retrieve a token balance' })
134145
@ApiResponse({ status: 200, type: TokenBalance })
135-
balance(@RequestContext() ctx, @Query() query: TokenBalanceQuery) {
146+
balance(@RequestContext() ctx: Context, @Query() query: TokenBalanceQuery) {
136147
return this.service.balance(ctx, query);
137148
}
138149

139150
@Get('receipt/:id')
140151
@ApiOperation({ summary: 'Retrieve the result of an async operation' })
141152
@ApiResponse({ status: 200, type: EventStreamReply })
142-
getReceipt(@RequestContext() ctx, @Param('id') id: string) {
153+
getReceipt(@RequestContext() ctx: Context, @Param('id') id: string) {
143154
return this.blockchain.getReceipt(ctx, id);
144155
}
145156
}

src/tokens/tokens.interfaces.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,17 @@ export class TokenPoolActivate {
187187

188188
@ApiProperty()
189189
@IsOptional()
190-
config?: any;
190+
config?: TokenPoolConfig;
191191

192-
@ApiProperty({ description: requestIdDescription })
192+
@ApiProperty()
193193
@IsOptional()
194-
requestId?: string;
194+
poolData?: string;
195+
}
196+
197+
export class TokenPoolDeactivate {
198+
@ApiProperty()
199+
@IsNotEmpty()
200+
poolLocator: string;
195201

196202
@ApiProperty()
197203
@IsOptional()

src/tokens/tokens.service.ts

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
// See the License for the specific language governing permissions and
1515
// limitations under the License.
1616

17-
import { Injectable, Logger } from '@nestjs/common';
17+
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
1818
import { EventStreamService } from '../event-stream/event-stream.service';
1919
import { EventStream, EventStreamSubscription } from '../event-stream/event-stream.interfaces';
2020
import { EventStreamProxyGateway } from '../eventstream-proxy/eventstream-proxy.gateway';
@@ -33,6 +33,7 @@ import {
3333
TokenMint,
3434
TokenPool,
3535
TokenPoolActivate,
36+
TokenPoolDeactivate,
3637
TokenTransfer,
3738
} from './tokens.interfaces';
3839
import {
@@ -325,6 +326,67 @@ export class TokensService {
325326
await Promise.all(promises);
326327
}
327328

329+
async deactivatePool(ctx: Context, dto: TokenPoolDeactivate) {
330+
const tokenCreateEvent = this.mapper.getCreateEvent();
331+
const stream = await this.getStream(ctx);
332+
333+
const promises: Promise<boolean>[] = [];
334+
if (tokenCreateEvent?.name !== undefined) {
335+
promises.push(
336+
this.eventstream.deleteSubscriptionByName(
337+
ctx,
338+
stream.id,
339+
packSubscriptionName(
340+
this.instancePath,
341+
dto.poolLocator,
342+
tokenCreateEvent.name,
343+
dto.poolData,
344+
),
345+
),
346+
);
347+
}
348+
349+
promises.push(
350+
...[
351+
this.eventstream.deleteSubscriptionByName(
352+
ctx,
353+
stream.id,
354+
packSubscriptionName(
355+
this.instancePath,
356+
dto.poolLocator,
357+
TransferSingle.name,
358+
dto.poolData,
359+
),
360+
),
361+
this.eventstream.deleteSubscriptionByName(
362+
ctx,
363+
stream.id,
364+
packSubscriptionName(
365+
this.instancePath,
366+
dto.poolLocator,
367+
TransferBatch.name,
368+
dto.poolData,
369+
),
370+
),
371+
this.eventstream.deleteSubscriptionByName(
372+
ctx,
373+
stream.id,
374+
packSubscriptionName(
375+
this.instancePath,
376+
dto.poolLocator,
377+
ApprovalForAll.name,
378+
dto.poolData,
379+
),
380+
),
381+
],
382+
);
383+
384+
const results = await Promise.all(promises);
385+
if (results.every(deleted => !deleted)) {
386+
throw new NotFoundException('No listeners found');
387+
}
388+
}
389+
328390
checkInterface(dto: CheckInterfaceRequest): CheckInterfaceResponse {
329391
const wrapMethods = (methods: IAbiMethod[]): TokenInterface => {
330392
return { format: InterfaceFormat.ABI, methods };

0 commit comments

Comments
 (0)