Skip to content

Commit

Permalink
Add a configurable webhook execution delay (#883)
Browse files Browse the repository at this point in the history
Changes:
- Adds a configurable (via the environment variable `WEB_HOOK_EXECUTION_DELAY_MS`) delay before executing web hook. The default value for this is 5 seconds.
- The status code returned by the controller was changed to 202 as it is more accurate given the new behavior.
  • Loading branch information
hectorgomezv committed Nov 28, 2023
1 parent bbb509c commit 31c5486
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 31 deletions.
1 change: 1 addition & 0 deletions src/config/entities/__tests__/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,5 @@ export default (): ReturnType<typeof configuration> => ({
safeTransaction: {
useVpcUrl: false,
},
webHookExecutionDelayMs: faker.number.int({ min: 1, max: 10 }),
});
3 changes: 3 additions & 0 deletions src/config/entities/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,7 @@ export default () => ({
safeTransaction: {
useVpcUrl: process.env.USE_TX_SERVICE_VPC_URL?.toLowerCase() === 'true',
},
webHookExecutionDelayMs: parseInt(
process.env.WEB_HOOK_EXECUTION_DELAY_MS ?? `${5_000}`,
),
});
102 changes: 75 additions & 27 deletions src/routes/cache-hooks/cache-hooks.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ describe('Post Hook Events (Unit)', () => {
let safeConfigUrl;
let fakeCacheService: FakeCacheService;
let networkService;
let webHookExecutionDelayMs;

beforeEach(async () => {
jest.clearAllMocks();
jest.useFakeTimers();

const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule.register(configuration)],
Expand All @@ -47,11 +49,18 @@ describe('Post Hook Events (Unit)', () => {
const configurationService = moduleFixture.get(IConfigurationService);
authToken = configurationService.get('auth.token');
safeConfigUrl = configurationService.get('safeConfig.baseUri');
webHookExecutionDelayMs = configurationService.get(
'webHookExecutionDelayMs',
);
networkService = moduleFixture.get(NetworkService);

await app.init();
});

afterEach(() => {
jest.useRealTimers();
});

afterAll(async () => {
await app.close();
});
Expand Down Expand Up @@ -148,7 +157,7 @@ describe('Post Hook Events (Unit)', () => {
.post(`/hooks/events`)
.set('Authorization', `Basic ${authToken}`)
.send(data)
.expect(200);
.expect(202);
});

it('returns 400 (Bad Request) on unknown payload', async () => {
Expand Down Expand Up @@ -223,9 +232,12 @@ describe('Post Hook Events (Unit)', () => {
.post(`/hooks/events`)
.set('Authorization', `Basic ${authToken}`)
.send(data)
.expect(200);
.expect(202);

await expect(fakeCacheService.get(cacheDir)).resolves.toBeNull();
setTimeout(
() => expect(fakeCacheService.get(cacheDir)).resolves.toBeNull(),
webHookExecutionDelayMs,
);
});

it.each([
Expand Down Expand Up @@ -271,9 +283,12 @@ describe('Post Hook Events (Unit)', () => {
.post(`/hooks/events`)
.set('Authorization', `Basic ${authToken}`)
.send(data)
.expect(200);
.expect(202);

await expect(fakeCacheService.get(cacheDir)).resolves.toBeNull();
setTimeout(
() => expect(fakeCacheService.get(cacheDir)).resolves.toBeNull(),
webHookExecutionDelayMs,
);
});

it.each([
Expand Down Expand Up @@ -319,9 +334,12 @@ describe('Post Hook Events (Unit)', () => {
.post(`/hooks/events`)
.set('Authorization', `Basic ${authToken}`)
.send(data)
.expect(200);
.expect(202);

await expect(fakeCacheService.get(cacheDir)).resolves.toBeNull();
setTimeout(
() => expect(fakeCacheService.get(cacheDir)).resolves.toBeNull(),
webHookExecutionDelayMs,
);
});

it.each([
Expand Down Expand Up @@ -358,9 +376,12 @@ describe('Post Hook Events (Unit)', () => {
.post(`/hooks/events`)
.set('Authorization', `Basic ${authToken}`)
.send(data)
.expect(200);
.expect(202);

await expect(fakeCacheService.get(cacheDir)).resolves.toBeNull();
setTimeout(
() => expect(fakeCacheService.get(cacheDir)).resolves.toBeNull(),
webHookExecutionDelayMs,
);
});

it.each([
Expand Down Expand Up @@ -407,9 +428,12 @@ describe('Post Hook Events (Unit)', () => {
.post(`/hooks/events`)
.set('Authorization', `Basic ${authToken}`)
.send(data)
.expect(200);
.expect(202);

await expect(fakeCacheService.get(cacheDir)).resolves.toBeNull();
setTimeout(
() => expect(fakeCacheService.get(cacheDir)).resolves.toBeNull(),
webHookExecutionDelayMs,
);
});

it.each([
Expand Down Expand Up @@ -456,9 +480,12 @@ describe('Post Hook Events (Unit)', () => {
.post(`/hooks/events`)
.set('Authorization', `Basic ${authToken}`)
.send(data)
.expect(200);
.expect(202);

await expect(fakeCacheService.get(cacheDir)).resolves.toBeNull();
setTimeout(
() => expect(fakeCacheService.get(cacheDir)).resolves.toBeNull(),
webHookExecutionDelayMs,
);
});

it.each([
Expand Down Expand Up @@ -500,9 +527,12 @@ describe('Post Hook Events (Unit)', () => {
.post(`/hooks/events`)
.set('Authorization', `Basic ${authToken}`)
.send(data)
.expect(200);
.expect(202);

await expect(fakeCacheService.get(cacheDir)).resolves.toBeNull();
setTimeout(
() => expect(fakeCacheService.get(cacheDir)).resolves.toBeNull(),
webHookExecutionDelayMs,
);
});

it.each([
Expand Down Expand Up @@ -539,9 +569,12 @@ describe('Post Hook Events (Unit)', () => {
.post(`/hooks/events`)
.set('Authorization', `Basic ${authToken}`)
.send(data)
.expect(200);
.expect(202);

await expect(fakeCacheService.get(cacheDir)).resolves.toBeNull();
setTimeout(
() => expect(fakeCacheService.get(cacheDir)).resolves.toBeNull(),
webHookExecutionDelayMs,
);
});

it.each([
Expand Down Expand Up @@ -603,9 +636,12 @@ describe('Post Hook Events (Unit)', () => {
.post(`/hooks/events`)
.set('Authorization', `Basic ${authToken}`)
.send(data)
.expect(200);
.expect(202);

await expect(fakeCacheService.get(cacheDir)).resolves.toBeNull();
setTimeout(
() => expect(fakeCacheService.get(cacheDir)).resolves.toBeNull(),
webHookExecutionDelayMs,
);
});

it.each([
Expand Down Expand Up @@ -645,9 +681,12 @@ describe('Post Hook Events (Unit)', () => {
.post(`/hooks/events`)
.set('Authorization', `Basic ${authToken}`)
.send(data)
.expect(200);
.expect(202);

await expect(fakeCacheService.get(cacheDir)).resolves.toBeNull();
setTimeout(
() => expect(fakeCacheService.get(cacheDir)).resolves.toBeNull(),
webHookExecutionDelayMs,
);
});

it.each([
Expand All @@ -667,9 +706,12 @@ describe('Post Hook Events (Unit)', () => {
.post(`/hooks/events`)
.set('Authorization', `Basic ${authToken}`)
.send(data)
.expect(200);
.expect(202);

await expect(fakeCacheService.get(cacheDir)).resolves.toBeNull();
setTimeout(
() => expect(fakeCacheService.get(cacheDir)).resolves.toBeNull(),
webHookExecutionDelayMs,
);
});

it.each([
Expand All @@ -689,9 +731,12 @@ describe('Post Hook Events (Unit)', () => {
.post(`/hooks/events`)
.set('Authorization', `Basic ${authToken}`)
.send(data)
.expect(200);
.expect(202);

await expect(fakeCacheService.get(cacheDir)).resolves.toBeNull();
setTimeout(
() => expect(fakeCacheService.get(cacheDir)).resolves.toBeNull(),
webHookExecutionDelayMs,
);
});

it.each([
Expand All @@ -711,8 +756,11 @@ describe('Post Hook Events (Unit)', () => {
.post(`/hooks/events`)
.set('Authorization', `Basic ${authToken}`)
.send(data)
.expect(200);
.expect(202);

await expect(fakeCacheService.get(cacheDir)).resolves.toBeNull();
setTimeout(
() => expect(fakeCacheService.get(cacheDir)).resolves.toBeNull(),
webHookExecutionDelayMs,
);
});
});
29 changes: 25 additions & 4 deletions src/routes/cache-hooks/cache-hooks.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { Body, Controller, HttpCode, Post, UseGuards } from '@nestjs/common';
import {
Body,
Controller,
HttpCode,
Inject,
Post,
UseGuards,
} from '@nestjs/common';
import { ApiExcludeController } from '@nestjs/swagger';
import { CacheHooksService } from '@/routes/cache-hooks/cache-hooks.service';
import { ChainUpdate } from '@/routes/cache-hooks/entities/chain-update.entity';
Expand All @@ -15,18 +22,29 @@ import { PendingTransaction } from '@/routes/cache-hooks/entities/pending-transa
import { SafeAppsUpdate } from '@/routes/cache-hooks/entities/safe-apps-update.entity';
import { EventValidationPipe } from '@/routes/cache-hooks/pipes/event-validation.pipe';
import { BasicAuthGuard } from '@/routes/common/auth/basic-auth.guard';
import { IConfigurationService } from '@/config/configuration.service.interface';

@Controller({
path: '',
version: '1',
})
@ApiExcludeController()
export class CacheHooksController {
constructor(private readonly service: CacheHooksService) {}
private readonly webHookExecutionDelayMs: number;

constructor(
private readonly service: CacheHooksService,
@Inject(IConfigurationService)
private readonly configurationService: IConfigurationService,
) {
this.webHookExecutionDelayMs = this.configurationService.getOrThrow<number>(
'webHookExecutionDelayMs',
);
}

@UseGuards(BasicAuthGuard)
@Post('/hooks/events')
@HttpCode(200)
@HttpCode(202)
async postEvent(
@Body(EventValidationPipe)
eventPayload:
Expand All @@ -43,6 +61,9 @@ export class CacheHooksController {
| PendingTransaction
| SafeAppsUpdate,
): Promise<void> {
await this.service.onEvent(eventPayload);
setTimeout(
() => this.service.onEvent(eventPayload),
this.webHookExecutionDelayMs,
);
}
}

0 comments on commit 31c5486

Please sign in to comment.