@pinelab/vendure-plugin-webhook 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # Vendure Webhook plugin
2
+
3
+ ![Vendure version](https://img.shields.io/npm/dependency-version/vendure-plugin-webhook/dev/@vendure/core)
4
+
5
+ Triggers an outgoing webhook based on configured events. Events are specified in `vendure-config` and webhooks are configured per
6
+ channel via the admin UI.
7
+
8
+ YOu can use this plugin for example to trigger builds when ProductEvents or CollectionEvents occur, or send notifications to external
9
+ platforms when orders are placed by subscribing to OrderPlacedEvents!
10
+
11
+ ## Breaking changes since v7.x
12
+
13
+ :warning: V7 of this plugin allows you to create multiple webhooks per channel for multiple different events. You have to manually recreate your webhooks after migration! (Don't forget your DB migration):
14
+
15
+ - Check what URL is triggered for what event in your current environment, and note it down somewhere.
16
+ - Install the new version, migrate, start the server, and go to `Settings > Webhook` in the Admin UI.
17
+ - Create the hook. You can leave the `Transformer` field blank: the plugin will send an empty post without a transfomer.
18
+
19
+ ## Getting started
20
+
21
+ 1. `yarn add vendure-plugin-webhook`
22
+ 2. Add the `WebhookPlugin` to your plugins in your `vendure-config.ts`:
23
+
24
+ ```ts
25
+ import { WebhookPlugin } from 'vendure-plugin-webhook';
26
+
27
+ plugins: [
28
+ WebhookPlugin.init({
29
+ /**
30
+ * Optional: 'delay' waits and deduplicates events for 3000ms.
31
+ * If 4 events were fired for the same channel within 3 seconds,
32
+ * only 1 webhook call will be sent
33
+ */
34
+ delay: 3000,
35
+ events: [ProductEvent, ProductVariantEvent],
36
+ /**
37
+ * Optional: A requestTransformer allows you to send custom headers
38
+ * and a custom body with your webhook call.
39
+ * If no transformers are specified
40
+ */
41
+ requestTransformers: [],
42
+ }),
43
+ AdminUiPlugin.init({
44
+ port: 3002,
45
+ route: 'admin',
46
+ app: compileUiExtensions({
47
+ outputPath: path.join(__dirname, '__admin-ui'),
48
+ // Add the WebhookPlugin's UI to the admin
49
+ extensions: [WebhookPlugin.ui],
50
+ }),
51
+ }),
52
+ ];
53
+ ```
54
+
55
+ 3. Run a DB migration to create the custom entities.
56
+ 4. Start the server and assign the permission `SetWebhook` to administrators who should be able to configure webhooks.
57
+ 5. Go to `settings > webhook` to configure webhooks
58
+
59
+ ### Custom transformers
60
+
61
+ Request transformers are used to create a custom POST body and custom headers for your outgoing webhooks. The example below stringifies the contents of a ProductEvent.
62
+
63
+ ```ts
64
+ import { Logger, ProductEvent } from '@vendure/core';
65
+ import { RequestTransformer } from 'vendure-plugin-webhook';
66
+
67
+ export const stringifyProductTransformer = new RequestTransformer({
68
+ name: 'Stringify Product events',
69
+ supportedEvents: [ProductEvent],
70
+ transform: (event, injector) => {
71
+ if (event instanceof ProductEvent) {
72
+ return {
73
+ body: JSON.stringify(event),
74
+ headers: {
75
+ 'x-custom-header': 'custom-example-header',
76
+ 'content-type': 'application/json',
77
+ },
78
+ };
79
+ } else {
80
+ throw Error(`This transformer is only for ProductEvents!`);
81
+ }
82
+ },
83
+ });
84
+
85
+ // In your vendure-config's plugin array:
86
+ WebhookPlugin.init({
87
+ events: [ProductEvent],
88
+ requestTransformers: [stringifyProductTransformer],
89
+ });
90
+ ```
@@ -0,0 +1 @@
1
+ export declare const adminSchema: import("graphql").DocumentNode;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.adminSchema = void 0;
7
+ const graphql_tag_1 = __importDefault(require("graphql-tag"));
8
+ exports.adminSchema = (0, graphql_tag_1.default) `
9
+ input WebhookInput {
10
+ event: String!
11
+ url: String!
12
+ transformerName: String
13
+ }
14
+
15
+ type Webhook {
16
+ id: ID!
17
+ event: String!
18
+ url: String!
19
+ requestTransformer: WebhookRequestTransformer
20
+ }
21
+
22
+ type WebhookRequestTransformer {
23
+ name: String!
24
+ supportedEvents: [String!]!
25
+ }
26
+
27
+ extend type Mutation {
28
+ """
29
+ Set all webhooks for the current channel. This will overwrite any existing webhooks.
30
+ """
31
+ setWebhooks(webhooks: [WebhookInput!]!): [Webhook!]!
32
+ }
33
+
34
+ extend type Query {
35
+ """
36
+ Get all webhooks for the current channel
37
+ """
38
+ webhooks: [Webhook!]!
39
+ """
40
+ Get all available Vendure events that can be used to trigger webhooks
41
+ """
42
+ availableWebhookEvents: [String!]!
43
+ """
44
+ "
45
+ Get all available webhook request transformers
46
+ """
47
+ availableWebhookRequestTransformers: [WebhookRequestTransformer!]!
48
+ }
49
+ `;
@@ -0,0 +1,23 @@
1
+ import { Injector, RequestContext, Type, VendureEvent } from '@vendure/core';
2
+ export type TransformFn<T extends EventWithContext> = (event: T, injector: Injector) => WebhookRequest | Promise<WebhookRequest>;
3
+ export type EventWithContext = VendureEvent & {
4
+ ctx: RequestContext;
5
+ };
6
+ export interface WebhookRequest {
7
+ body?: ArrayBuffer | ArrayBufferView | string;
8
+ headers?: Record<string, string>;
9
+ }
10
+ export declare class RequestTransformer<T extends Array<Type<EventWithContext>>> {
11
+ private readonly options;
12
+ readonly name: string;
13
+ readonly supportedEvents: T;
14
+ readonly transform: TransformFn<EventWithContext>;
15
+ constructor(options: {
16
+ name: string;
17
+ /**
18
+ * The events that this transformer supports
19
+ */
20
+ supportedEvents: T;
21
+ transform: TransformFn<EventWithContext>;
22
+ });
23
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RequestTransformer = void 0;
4
+ class RequestTransformer {
5
+ constructor(options) {
6
+ this.options = options;
7
+ this.name = options.name;
8
+ this.supportedEvents = options.supportedEvents;
9
+ this.transform = options.transform;
10
+ }
11
+ }
12
+ exports.RequestTransformer = RequestTransformer;
@@ -0,0 +1,12 @@
1
+ import { DeepPartial, VendureEntity } from '@vendure/core';
2
+ /**
3
+ * Each `Webhook` entity represents 1 webhook call for the specified
4
+ * Event with the specified Url and Transformer
5
+ */
6
+ export declare class Webhook extends VendureEntity {
7
+ constructor(input?: DeepPartial<Webhook>);
8
+ channelId: string;
9
+ url: string;
10
+ event: string;
11
+ transformerName?: string;
12
+ }
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.Webhook = void 0;
13
+ const typeorm_1 = require("typeorm");
14
+ const core_1 = require("@vendure/core");
15
+ /**
16
+ * Each `Webhook` entity represents 1 webhook call for the specified
17
+ * Event with the specified Url and Transformer
18
+ */
19
+ let Webhook = class Webhook extends core_1.VendureEntity {
20
+ constructor(input) {
21
+ super(input);
22
+ }
23
+ };
24
+ __decorate([
25
+ (0, typeorm_1.Column)(),
26
+ __metadata("design:type", String)
27
+ ], Webhook.prototype, "channelId", void 0);
28
+ __decorate([
29
+ (0, typeorm_1.Column)(),
30
+ __metadata("design:type", String)
31
+ ], Webhook.prototype, "url", void 0);
32
+ __decorate([
33
+ (0, typeorm_1.Column)(),
34
+ __metadata("design:type", String)
35
+ ], Webhook.prototype, "event", void 0);
36
+ __decorate([
37
+ (0, typeorm_1.Column)({ nullable: true }),
38
+ __metadata("design:type", String)
39
+ ], Webhook.prototype, "transformerName", void 0);
40
+ Webhook = __decorate([
41
+ (0, typeorm_1.Entity)(),
42
+ __metadata("design:paramtypes", [Object])
43
+ ], Webhook);
44
+ exports.Webhook = Webhook;
@@ -0,0 +1,26 @@
1
+ import { PermissionDefinition, RequestContext } from '@vendure/core';
2
+ import { WebhookService } from './webhook.service';
3
+ import { Webhook, WebhookInput, WebhookRequestTransformer } from '../generated/graphql-types';
4
+ import { Webhook as WebhookEntity } from './webhook.entity';
5
+ import { RequestTransformer } from './request-transformer';
6
+ export declare const webhookPermission: PermissionDefinition;
7
+ /**
8
+ * Graphql resolvers for retrieving and updating webhook for channel
9
+ */
10
+ export declare class WebhookResolver {
11
+ private webhookService;
12
+ constructor(webhookService: WebhookService);
13
+ setWebhooks(ctx: RequestContext, webhooks: WebhookInput[]): Promise<Webhook[]>;
14
+ webhooks(ctx: RequestContext): Promise<Webhook[]>;
15
+ availableWebhookEvents(): Promise<string[]>;
16
+ availableWebhookRequestTransformers(): Promise<WebhookRequestTransformer[]>;
17
+ }
18
+ export declare class WebhookRequestTransformerResolver {
19
+ private webhookService;
20
+ constructor(webhookService: WebhookService);
21
+ /**
22
+ * Resolve `webhook.transformerName` to the actual RequestTransformer object
23
+ */
24
+ requestTransformer(ctx: RequestContext, webhook: WebhookEntity): Promise<WebhookRequestTransformer | undefined>;
25
+ }
26
+ export declare function mapToGraphqlTransformer(transformer: RequestTransformer<any>): WebhookRequestTransformer;
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.mapToGraphqlTransformer = exports.WebhookRequestTransformerResolver = exports.WebhookResolver = exports.webhookPermission = void 0;
16
+ const graphql_1 = require("@nestjs/graphql");
17
+ const core_1 = require("@vendure/core");
18
+ const webhook_service_1 = require("./webhook.service");
19
+ const webhook_entity_1 = require("./webhook.entity");
20
+ // Permission needs to be defined first
21
+ exports.webhookPermission = new core_1.PermissionDefinition({
22
+ name: 'SetWebhook',
23
+ description: 'Allows setting a webhook URL',
24
+ });
25
+ /**
26
+ * Graphql resolvers for retrieving and updating webhook for channel
27
+ */
28
+ let WebhookResolver = class WebhookResolver {
29
+ constructor(webhookService) {
30
+ this.webhookService = webhookService;
31
+ }
32
+ async setWebhooks(ctx, webhooks) {
33
+ return this.webhookService.saveWebhooks(ctx, webhooks);
34
+ }
35
+ async webhooks(ctx) {
36
+ return this.webhookService.getAllWebhooks(ctx);
37
+ }
38
+ async availableWebhookEvents() {
39
+ return this.webhookService.getAvailableEvents();
40
+ }
41
+ async availableWebhookRequestTransformers() {
42
+ const transformers = this.webhookService.getAvailableTransformers();
43
+ return transformers.map(mapToGraphqlTransformer);
44
+ }
45
+ };
46
+ __decorate([
47
+ (0, graphql_1.Mutation)(),
48
+ (0, core_1.Allow)(exports.webhookPermission.Permission),
49
+ __param(0, (0, core_1.Ctx)()),
50
+ __param(1, (0, graphql_1.Args)('webhooks')),
51
+ __metadata("design:type", Function),
52
+ __metadata("design:paramtypes", [core_1.RequestContext, Array]),
53
+ __metadata("design:returntype", Promise)
54
+ ], WebhookResolver.prototype, "setWebhooks", null);
55
+ __decorate([
56
+ (0, graphql_1.Query)(),
57
+ (0, core_1.Allow)(exports.webhookPermission.Permission),
58
+ __param(0, (0, core_1.Ctx)()),
59
+ __metadata("design:type", Function),
60
+ __metadata("design:paramtypes", [core_1.RequestContext]),
61
+ __metadata("design:returntype", Promise)
62
+ ], WebhookResolver.prototype, "webhooks", null);
63
+ __decorate([
64
+ (0, graphql_1.Query)(),
65
+ (0, core_1.Allow)(exports.webhookPermission.Permission),
66
+ __metadata("design:type", Function),
67
+ __metadata("design:paramtypes", []),
68
+ __metadata("design:returntype", Promise)
69
+ ], WebhookResolver.prototype, "availableWebhookEvents", null);
70
+ __decorate([
71
+ (0, graphql_1.Query)(),
72
+ (0, core_1.Allow)(exports.webhookPermission.Permission),
73
+ __metadata("design:type", Function),
74
+ __metadata("design:paramtypes", []),
75
+ __metadata("design:returntype", Promise)
76
+ ], WebhookResolver.prototype, "availableWebhookRequestTransformers", null);
77
+ WebhookResolver = __decorate([
78
+ (0, graphql_1.Resolver)(),
79
+ __metadata("design:paramtypes", [webhook_service_1.WebhookService])
80
+ ], WebhookResolver);
81
+ exports.WebhookResolver = WebhookResolver;
82
+ let WebhookRequestTransformerResolver = class WebhookRequestTransformerResolver {
83
+ constructor(webhookService) {
84
+ this.webhookService = webhookService;
85
+ }
86
+ /**
87
+ * Resolve `webhook.transformerName` to the actual RequestTransformer object
88
+ */
89
+ async requestTransformer(ctx, webhook) {
90
+ if (!webhook.transformerName) {
91
+ return;
92
+ }
93
+ const transformers = this.webhookService.getAvailableTransformers();
94
+ const transformer = transformers.find((t) => t.name === webhook.transformerName);
95
+ if (!transformer) {
96
+ return;
97
+ }
98
+ return mapToGraphqlTransformer(transformer);
99
+ }
100
+ };
101
+ __decorate([
102
+ (0, graphql_1.ResolveField)(),
103
+ __param(0, (0, core_1.Ctx)()),
104
+ __param(1, (0, graphql_1.Parent)()),
105
+ __metadata("design:type", Function),
106
+ __metadata("design:paramtypes", [core_1.RequestContext,
107
+ webhook_entity_1.Webhook]),
108
+ __metadata("design:returntype", Promise)
109
+ ], WebhookRequestTransformerResolver.prototype, "requestTransformer", null);
110
+ WebhookRequestTransformerResolver = __decorate([
111
+ (0, graphql_1.Resolver)('Webhook'),
112
+ __metadata("design:paramtypes", [webhook_service_1.WebhookService])
113
+ ], WebhookRequestTransformerResolver);
114
+ exports.WebhookRequestTransformerResolver = WebhookRequestTransformerResolver;
115
+ function mapToGraphqlTransformer(transformer) {
116
+ return {
117
+ name: transformer.name,
118
+ supportedEvents: transformer.supportedEvents.map((event) => event.name),
119
+ };
120
+ }
121
+ exports.mapToGraphqlTransformer = mapToGraphqlTransformer;
@@ -0,0 +1,61 @@
1
+ import { OnApplicationBootstrap } from '@nestjs/common';
2
+ import { ModuleRef } from '@nestjs/core';
3
+ import { EventBus, ID, RequestContext, TransactionalConnection } from '@vendure/core';
4
+ import { Webhook } from './webhook.entity';
5
+ import { EventWithContext, RequestTransformer } from './request-transformer';
6
+ import { WebhookPluginOptions } from '../webhook.plugin';
7
+ import { WebhookInput } from '../generated/graphql-types';
8
+ /**
9
+ * Service for updating and retrieving webhooks from db
10
+ */
11
+ export declare class WebhookService implements OnApplicationBootstrap {
12
+ private eventBus;
13
+ private connection;
14
+ private moduleRef;
15
+ private options;
16
+ /**
17
+ * A queue of unique webhooks to call. This is used to prevent
18
+ * multiple calls to the same webhook for the same
19
+ * event within the configured `delay` time
20
+ */
21
+ webhookQueue: Map<ID, Webhook>;
22
+ constructor(eventBus: EventBus, connection: TransactionalConnection, moduleRef: ModuleRef, options: WebhookPluginOptions);
23
+ /**
24
+ * Subscribe to events specified in config
25
+ */
26
+ onApplicationBootstrap(): Promise<void>;
27
+ /**
28
+ * Get the plugin's configured Events.
29
+ */
30
+ getAvailableEvents(): string[];
31
+ /**
32
+ * Get the plugin's configured Request Transformers.
33
+ */
34
+ getAvailableTransformers(): RequestTransformer<any>[];
35
+ /**
36
+ * Get all configured webhooks for current channel
37
+ */
38
+ getAllWebhooks(ctx: RequestContext): Promise<Webhook[]>;
39
+ /**
40
+ * Get configured webhooks for given Event
41
+ */
42
+ getWebhooksForEvent<T extends EventWithContext>(event: T): Promise<Webhook[]>;
43
+ /**
44
+ * Save set of webhooks for current channel.
45
+ * Overrides any previously set hooks
46
+ */
47
+ saveWebhooks(ctx: RequestContext, inputs: WebhookInput[]): Promise<Webhook[]>;
48
+ /**
49
+ * Push the webhooks for the given Event to the queue,
50
+ * so they can be processed in batch
51
+ */
52
+ addWebhookToQueue(event: EventWithContext): Promise<void>;
53
+ /**
54
+ * Process all webhooks currently in the queue
55
+ */
56
+ processQueue(event: EventWithContext): Promise<void>;
57
+ /**
58
+ * Call the actual webhook with the configured Transformer for given Event
59
+ */
60
+ callWebhook(webhook: Webhook, event: EventWithContext): Promise<void>;
61
+ }
@@ -0,0 +1,182 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var __importDefault = (this && this.__importDefault) || function (mod) {
15
+ return (mod && mod.__esModule) ? mod : { "default": mod };
16
+ };
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.WebhookService = void 0;
19
+ const common_1 = require("@nestjs/common");
20
+ const core_1 = require("@nestjs/core");
21
+ const core_2 = require("@vendure/core");
22
+ const webhook_entity_1 = require("./webhook.entity");
23
+ const node_fetch_1 = __importDefault(require("node-fetch"));
24
+ const constants_1 = require("../constants");
25
+ /**
26
+ * Service for updating and retrieving webhooks from db
27
+ */
28
+ let WebhookService = class WebhookService {
29
+ constructor(eventBus, connection, moduleRef, options) {
30
+ this.eventBus = eventBus;
31
+ this.connection = connection;
32
+ this.moduleRef = moduleRef;
33
+ this.options = options;
34
+ /**
35
+ * A queue of unique webhooks to call. This is used to prevent
36
+ * multiple calls to the same webhook for the same
37
+ * event within the configured `delay` time
38
+ */
39
+ this.webhookQueue = new Map();
40
+ }
41
+ /**
42
+ * Subscribe to events specified in config
43
+ */
44
+ async onApplicationBootstrap() {
45
+ if (!this.options.events || this.options.events.length === 0) {
46
+ throw Error(`Please specify VendureEvents with Webhook.init() to use this plugin.`);
47
+ }
48
+ if (this.options.disabled) {
49
+ core_2.Logger.info(`Webhook plugin disabled,not listening for events`, constants_1.loggerCtx);
50
+ return;
51
+ }
52
+ // Subscribe to all configured events
53
+ this.options.events.forEach((configuredEvent) => {
54
+ this.eventBus.ofType(configuredEvent).subscribe(async (event) => {
55
+ try {
56
+ await this.addWebhookToQueue(event);
57
+ // Start processing after the given delay, because
58
+ // we might get multiple of the same events within the specified delay
59
+ await new Promise((resolve) => setTimeout(resolve, this.options.delay));
60
+ await this.processQueue(event);
61
+ }
62
+ catch (e) {
63
+ core_2.Logger.error(`Failed to call webhook for event ${event.constructor.name} for channel ${event.ctx.channelId}: ${e}`, constants_1.loggerCtx);
64
+ }
65
+ });
66
+ core_2.Logger.info(`Listening for ${configuredEvent.name}`, constants_1.loggerCtx);
67
+ });
68
+ }
69
+ /**
70
+ * Get the plugin's configured Events.
71
+ */
72
+ getAvailableEvents() {
73
+ return this.options.events.map((eventType) => eventType.name);
74
+ }
75
+ /**
76
+ * Get the plugin's configured Request Transformers.
77
+ */
78
+ getAvailableTransformers() {
79
+ return this.options.requestTransformers || [];
80
+ }
81
+ /**
82
+ * Get all configured webhooks for current channel
83
+ */
84
+ async getAllWebhooks(ctx) {
85
+ return this.connection
86
+ .getRepository(ctx, webhook_entity_1.Webhook)
87
+ .find({ where: { channelId: String(ctx.channelId) } });
88
+ }
89
+ /**
90
+ * Get configured webhooks for given Event
91
+ */
92
+ async getWebhooksForEvent(event) {
93
+ const eventName = event.constructor.name;
94
+ return this.connection.getRepository(event.ctx, webhook_entity_1.Webhook).find({
95
+ where: { channelId: String(event.ctx.channelId), event: eventName },
96
+ });
97
+ }
98
+ /**
99
+ * Save set of webhooks for current channel.
100
+ * Overrides any previously set hooks
101
+ */
102
+ async saveWebhooks(ctx, inputs) {
103
+ const repository = this.connection.getRepository(ctx, webhook_entity_1.Webhook);
104
+ // Delete all current hooks
105
+ await repository.delete({ channelId: String(ctx.channelId) });
106
+ // Recreate all hooks
107
+ const webhooks = inputs.map((input) => ({
108
+ channelId: String(ctx.channelId),
109
+ url: input.url,
110
+ event: input.event,
111
+ transformerName: input.transformerName ?? undefined,
112
+ }));
113
+ await repository.save(webhooks);
114
+ return this.getAllWebhooks(ctx);
115
+ }
116
+ /**
117
+ * Push the webhooks for the given Event to the queue,
118
+ * so they can be processed in batch
119
+ */
120
+ async addWebhookToQueue(event) {
121
+ const webhooks = await this.getWebhooksForEvent(event);
122
+ webhooks.map((webhook) => {
123
+ this.webhookQueue.set(webhook.id, webhook);
124
+ });
125
+ if (webhooks.length > 0) {
126
+ core_2.Logger.info(`Added ${webhooks.length} webhooks to the webhook queue for ${event.constructor.name}`, constants_1.loggerCtx);
127
+ }
128
+ }
129
+ /**
130
+ * Process all webhooks currently in the queue
131
+ */
132
+ async processQueue(event) {
133
+ // Check if queue already handled
134
+ if (this.webhookQueue.size === 0) {
135
+ return;
136
+ }
137
+ // Copy queue, and empty original
138
+ const webhooks = [];
139
+ this.webhookQueue.forEach((webhook) => webhooks.push(webhook));
140
+ this.webhookQueue.clear();
141
+ // Start calling the webhooks
142
+ await Promise.all(webhooks.map(async (webhook) => {
143
+ try {
144
+ await this.callWebhook(webhook, event);
145
+ }
146
+ catch (e) {
147
+ core_2.Logger.error(`Failed to call webhook for event ${webhook.event} channel ${webhook.channelId}: ${e}`, constants_1.loggerCtx);
148
+ }
149
+ }));
150
+ }
151
+ /**
152
+ * Call the actual webhook with the configured Transformer for given Event
153
+ */
154
+ async callWebhook(webhook, event) {
155
+ if (!webhook.transformerName) {
156
+ // No transformer, just call webhook without body
157
+ await (0, node_fetch_1.default)(webhook.url, { method: 'POST' });
158
+ return core_2.Logger.info(`Successfully triggered webhook for event ${webhook.event} for channel ${webhook.channelId} without transformer`, constants_1.loggerCtx);
159
+ }
160
+ // Have the configured transformer construct the request
161
+ const transformer = this.getAvailableTransformers().find((transformer) => transformer.name === webhook.transformerName);
162
+ if (!transformer) {
163
+ throw Error(`Could not find transformer ${webhook.transformerName}`);
164
+ }
165
+ const request = await transformer.transform(event, new core_2.Injector(this.moduleRef));
166
+ // Call the webhook with the constructed request
167
+ await (0, node_fetch_1.default)(webhook.url, {
168
+ method: 'POST',
169
+ headers: request.headers,
170
+ body: request.body,
171
+ });
172
+ core_2.Logger.info(`Successfully triggered webhook for event ${event.constructor.name} for channel ${webhook.channelId} with transformer "${webhook.transformerName}"`, constants_1.loggerCtx);
173
+ }
174
+ };
175
+ WebhookService = __decorate([
176
+ (0, common_1.Injectable)(),
177
+ __param(3, (0, common_1.Inject)(constants_1.PLUGIN_INIT_OPTIONS)),
178
+ __metadata("design:paramtypes", [core_2.EventBus,
179
+ core_2.TransactionalConnection,
180
+ core_1.ModuleRef, Object])
181
+ ], WebhookService);
182
+ exports.WebhookService = WebhookService;
@@ -0,0 +1,2 @@
1
+ export declare const loggerCtx = "WebhookPlugin";
2
+ export declare const PLUGIN_INIT_OPTIONS: unique symbol;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PLUGIN_INIT_OPTIONS = exports.loggerCtx = void 0;
4
+ exports.loggerCtx = 'WebhookPlugin';
5
+ exports.PLUGIN_INIT_OPTIONS = Symbol('PLUGIN_INIT_OPTIONS');