• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

teableio / teable / 12881890017

21 Jan 2025 07:13AM CUT coverage: 80.91%. First build
12881890017

Pull #1263

github

web-flow
Merge 1aab39586 into 2be93a15f
Pull Request #1263: feat: webhooks

6485 of 6849 branches covered (94.69%)

58 of 370 new or added lines in 7 files covered. (15.68%)

30774 of 38035 relevant lines covered (80.91%)

1827.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

22.0
/apps/nestjs-backend/src/features/webhook/webhook.factory.ts
1
/* eslint-disable @typescript-eslint/naming-convention */
2✔
2
import crypto from 'crypto';
3
import { HttpService } from '@nestjs/axios';
4
import { Injectable, Logger } from '@nestjs/common';
5
import type { IWebhookVo } from '@teable/openapi';
6
import { ContentType } from '@teable/openapi';
7
import { filter, from, mergeMap } from 'rxjs';
8
import { match } from 'ts-pattern';
9
import type { CoreEvent } from '../../event-emitter/events';
10
import { WebhookService } from './webhook.service';
11

12
type IWebhook = IWebhookVo & { secret: string | null };
13

14
@Injectable()
15
export class WebhookFactory {
2✔
16
  private logger = new Logger(WebhookFactory.name);
101✔
17

18
  constructor(
101✔
19
    private readonly httpService: HttpService,
101✔
20
    private readonly webHookService: WebhookService
101✔
21
  ) {}
101✔
22

23
  async run(spaceId: string, event: CoreEvent) {
101✔
NEW
24
    const webHookList = await this.webHookService.getWebhookListBySpaceId(spaceId);
×
25

NEW
26
    // 10s
×
NEW
27
    // event.payload
×
28

NEW
29
    from(webHookList).pipe(
×
NEW
30
      filter((webhookContext) => {
×
NEW
31
        return true;
×
NEW
32
      }),
×
NEW
33
      mergeMap((value) => {
×
NEW
34
        return this.sendHttpRequest(value, event);
×
NEW
35
      }, 10)
×
36
    );
NEW
37
  }
×
38

39
  private sendHttpRequest(webhookContext: IWebhook, event: CoreEvent) {
101✔
NEW
40
    const body = match(webhookContext.contentType)
×
NEW
41
      .with(ContentType.Json, () => {
×
NEW
42
        return JSON.stringify(event.payload);
×
NEW
43
      })
×
NEW
44
      .with(ContentType.FormUrlencoded, () => {
×
NEW
45
        return '';
×
NEW
46
      })
×
NEW
47
      .exhaustive();
×
48

NEW
49
    const headers: { [key: string]: string } = {};
×
NEW
50
    headers['User-Agent'] = 'teable/1.0.0';
×
NEW
51
    headers['Content-Type'] = webhookContext.contentType;
×
NEW
52
    headers['X-Event'] = event.name;
×
NEW
53
    headers['X-Hook-ID'] = '';
×
54

NEW
55
    if (webhookContext.secret) {
×
NEW
56
      headers['X-Signature-256'] = this.signature(webhookContext.secret, body);
×
NEW
57
    }
×
58

NEW
59
    return this.httpService
×
NEW
60
      .post(webhookContext.url, body, {
×
NEW
61
        headers: headers,
×
NEW
62
      })
×
NEW
63
      .pipe();
×
NEW
64
  }
×
65

66
  private signature(secret: string, body: unknown): string {
101✔
NEW
67
    const signature = crypto
×
NEW
68
      .createHmac('sha256', secret)
×
NEW
69
      .update(JSON.stringify(body))
×
NEW
70
      .digest('hex');
×
NEW
71
    return `sha256=${signature}`;
×
NEW
72
  }
×
73
}
101✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc