Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,12 @@ export interface StructuredResponse<T = unknown, R = unknown> {
data: T;
raw_data?: R;
}

export interface WebhookPayload {
body?: unknown;
query?: unknown;
params?: unknown;
resp?: unknown;
}

export type WebhookTriggerType = 'request' | 'response';
75 changes: 75 additions & 0 deletions src/plugin/webhook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import {FastifyInstance, FastifyRequest} from 'fastify';
import fp from 'fastify-plugin';

import {WebhookPayload, WebhookTriggerType} from '@/interfaces';

async function makeWebhookCall(
url: string,
payload: WebhookPayload,
logger: FastifyInstance['log'],
): Promise<void> {
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Rocket-Webhook': 'true',
},
body: JSON.stringify(payload),
});

if (!response.ok) {
logger.warn(`Webhook call returned status ${response.status} for ${url}`);
} else {
logger.info(`Webhook call successful for ${url}`);
}
} catch (error) {
logger.error({err: error}, `Webhook call failed for ${url}`);
}
}

export default fp(
async (fastify: FastifyInstance) => {
async function callWebhook(
trigger: WebhookTriggerType,
request: FastifyRequest,
payload: unknown,
): Promise<void> {
const apiIdentifier = request.routeOptions?.config?.apiIdentifier;
if (!apiIdentifier) {
fastify.log.debug(
'No apiIdentifier found on request route options config',
);
return;
}

const webhookConfigs =
fastify.appConfig.apis?.[apiIdentifier]?.webhooks ?? null;
if (!webhookConfigs?.length) return;

const isRequestTrigger = trigger === 'request';

for (const config of webhookConfigs) {
const dataToSend = config.data;
const webhookPayload: WebhookPayload = {
body: dataToSend.includes('body') ? request.body : undefined,
query: dataToSend.includes('query') ? request.query : undefined,
params: dataToSend.includes('params') ? request.params : undefined,
resp: dataToSend.includes('resp') ? payload : undefined,
};
const shouldCall = isRequestTrigger
? config.triggerOnRequest
: config.triggerOnResponse;

if (shouldCall) {
await makeWebhookCall(config.url, webhookPayload, fastify.log);
}
}
}

fastify.decorate('callWebhook', callWebhook);
},
{
name: 'webhook-plugin',
},
);
12 changes: 2 additions & 10 deletions src/routes/aggregate/aggregate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {

import {enforceSSP} from '@/utils/ssp';
import {capitalizeFirstLetter} from '@/utils/string';
import {callWebhook} from '@/utils/webhook';

/**
* Register AGGREGATE routes for fields with supportedAggregation.
Expand Down Expand Up @@ -42,7 +41,6 @@ export function registerAggregateRoutes(
const apiIdentifier = `aggregateAPIs->${model.name}->${field.name}->getAggregation`;

// extract the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];
// calculating the authroization based on auth flag, it can be true
// if the api level auth is enabled, or if the app level auth is enabled
Expand Down Expand Up @@ -87,16 +85,10 @@ export function registerAggregateRoutes(
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
await app.callWebhook('request', request, null);
},
onSend: async (request, _, payload) => {
await callWebhook(
'response',
webhookConfig,
request,
payload,
app.log,
);
await app.callWebhook('response', request, payload);
},
},
async (request: FastifyRequest, reply: FastifyReply) => {
Expand Down
6 changes: 2 additions & 4 deletions src/routes/custom-queries/custom-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
import {AppConfig, DataType} from '@/interfaces/config';

import {enforceSSP} from '@/utils/ssp';
import {callWebhook} from '@/utils/webhook';

type ParamSource = {
body?: Record<string, unknown>;
Expand Down Expand Up @@ -94,7 +93,6 @@ export function registerCustomQueryRoutes(
const apiIdentifier = `customAPIs->customQueries->all->${cq.name}`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];
// calculating the authroization based on auth flag, it can be true
// if the api level auth is enabled, or if the app level auth is enabled
Expand Down Expand Up @@ -255,10 +253,10 @@ export function registerCustomQueryRoutes(
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
await app.callWebhook('request', request, null);
},
onSend: async (request, _, payload) => {
await callWebhook('response', webhookConfig, request, payload, app.log);
await app.callWebhook('response', request, payload);
},
handler: async (request: FastifyRequest, reply: FastifyReply) => {
// extract the body, path, and query parameters from the request
Expand Down
12 changes: 2 additions & 10 deletions src/routes/operations/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {AppConfig, ModelConfig, ModelFieldConfig} from '@/interfaces/config';

import {enforceSSP} from '@/utils/ssp';
import {capitalizeFirstLetter} from '@/utils/string';
import {callWebhook} from '@/utils/webhook';

/**
* Register DELETE routes for deletable fields.
Expand Down Expand Up @@ -41,7 +40,6 @@ export function registerDeleteRoutes(
const apiIdentifier = `modelAPIs->${model.name}->${field.name}->delete`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];

// calculating the authroization based on auth flag, it can be true
Expand Down Expand Up @@ -82,16 +80,10 @@ export function registerDeleteRoutes(
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
await app.callWebhook('request', request, null);
},
onSend: async (request, _, payload) => {
await callWebhook(
'response',
webhookConfig,
request,
payload,
app.log,
);
await app.callWebhook('response', request, payload);
},
},
async (request: FastifyRequest, reply: FastifyReply) => {
Expand Down
22 changes: 4 additions & 18 deletions src/routes/operations/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {AppConfig, ModelBody} from '@/interfaces/config';

import {enforceSSP} from '@/utils/ssp';
import {capitalizeFirstLetter} from '@/utils/string';
import {callWebhook} from '@/utils/webhook';

/**
* Register EDIT routes for editable fields.
Expand Down Expand Up @@ -43,7 +42,6 @@ export function registerEditRoutes(
const apiIdentifier = `modelAPIs->${model.name}->${field.name}->edit`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];

// calculating the authroization based on auth flag, it can be true
Expand Down Expand Up @@ -249,16 +247,10 @@ export function registerEditRoutes(
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
await app.callWebhook('request', request, null);
},
onSend: async (request, _, payload) => {
await callWebhook(
'response',
webhookConfig,
request,
payload,
app.log,
);
await app.callWebhook('response', request, payload);
},
},
handleEditRequest,
Expand Down Expand Up @@ -288,16 +280,10 @@ export function registerEditRoutes(
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
await app.callWebhook('request', request, null);
},
onSend: async (request, _, payload) => {
await callWebhook(
'response',
webhookConfig,
request,
payload,
app.log,
);
await app.callWebhook('response', request, payload);
},
},
handleEditRequest,
Expand Down
12 changes: 2 additions & 10 deletions src/routes/operations/get-all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {AppConfig, ModelConfig} from '@/interfaces/config';

import {enforceSSP} from '@/utils/ssp';
import {capitalizeFirstLetter} from '@/utils/string';
import {callWebhook} from '@/utils/webhook';

/**
* Register GET_ALL routes for listing records (table-level).
Expand All @@ -39,7 +38,6 @@ export function registerGetAllRoutes(
const apiIdentifier = `modelAPIs->${model.name}->all->getAll`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];

// calculating the authroization based on auth flag, it can be true
Expand Down Expand Up @@ -79,16 +77,10 @@ export function registerGetAllRoutes(
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
await app.callWebhook('request', request, null);
},
onSend: async (request, _, payload) => {
await callWebhook(
'response',
webhookConfig,
request,
payload,
app.log,
);
await app.callWebhook('response', request, payload);
},
},
async (request: FastifyRequest, reply: FastifyReply) => {
Expand Down
12 changes: 2 additions & 10 deletions src/routes/operations/index-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {AppConfig, ModelConfig, ModelFieldConfig} from '@/interfaces/config';

import {enforceSSP} from '@/utils/ssp';
import {capitalizeFirstLetter} from '@/utils/string';
import {callWebhook} from '@/utils/webhook';

/**
* Register INDEX routes for indexed fields.
Expand Down Expand Up @@ -51,7 +50,6 @@ export function registerIndexRoutes(
const apiIdentifier = `modelAPIs->${model.name}->${field.name}->index`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];

// calculating the authroization based on auth flag, it can be true
Expand Down Expand Up @@ -90,16 +88,10 @@ export function registerIndexRoutes(
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
await app.callWebhook('request', request, null);
},
onSend: async (request, _, payload) => {
await callWebhook(
'response',
webhookConfig,
request,
payload,
app.log,
);
await app.callWebhook('response', request, payload);
},
},
async (request: FastifyRequest, reply: FastifyReply) => {
Expand Down
12 changes: 2 additions & 10 deletions src/routes/operations/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {AppConfig, ModelBody, ModelConfig} from '@/interfaces/config';

import {enforceSSP} from '@/utils/ssp';
import {capitalizeFirstLetter} from '@/utils/string';
import {callWebhook} from '@/utils/webhook';

/**
* Register POST routes for creating records (table-level).
Expand All @@ -31,7 +30,6 @@ export function registerPostRoutes(
const apiIdentifier = `modelAPIs->${model.name}->all->insert`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];

// calculating the authroization based on auth flag, it can be true
Expand Down Expand Up @@ -74,16 +72,10 @@ export function registerPostRoutes(
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
await app.callWebhook('request', request, null);
},
onSend: async (request, _, payload) => {
await callWebhook(
'response',
webhookConfig,
request,
payload,
app.log,
);
await app.callWebhook('response', request, payload);
},
},
async (
Expand Down
13 changes: 3 additions & 10 deletions src/routes/operations/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {AppConfig, ModelConfig, ModelFieldConfig} from '@/interfaces/config';

import {enforceSSP} from '@/utils/ssp';
import {capitalizeFirstLetter} from '@/utils/string';
import {callWebhook} from '@/utils/webhook';

/**
* Register SEARCH routes for searchable fields.
Expand Down Expand Up @@ -43,7 +42,6 @@ export function registerSearchRoutes(
const apiIdentifier = `modelAPIs->${model.name}->${field.name}->search`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];

// calculating the authroization based on auth flag, it can be true
Expand All @@ -52,6 +50,7 @@ export function registerSearchRoutes(
config.apis?.[apiIdentifier]?.authorization ??
config.auth?.enableAuth ??
false;

// defining the primary search query parameter
// for example if the field is "name", this will create "name_search" query parameter
const schema: Record<string, unknown> = generateSchema(
Expand Down Expand Up @@ -85,16 +84,10 @@ export function registerSearchRoutes(
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
await app.callWebhook('request', request, null);
},
onSend: async (request, _, payload) => {
await callWebhook(
'response',
webhookConfig,
request,
payload,
app.log,
);
await app.callWebhook('response', request, payload);
},
},
async (request: FastifyRequest, reply: FastifyReply) => {
Expand Down
Loading
Loading