From 0ccf62aa8d19f1807ca00955e22448775852e1b2 Mon Sep 17 00:00:00 2001 From: DrMint <29893320+DrMint@users.noreply.github.com> Date: Sat, 15 Jun 2024 17:28:51 +0200 Subject: [PATCH] Send webhook on all operation on all collections and global --- src/collections/Currencies/Currencies.ts | 4 -- src/collections/Languages/Languages.ts | 4 -- src/collections/Wordings/Wordings.ts | 4 -- src/constants.ts | 14 +++++ src/hooks/afterChangeWebhook.ts | 16 +++++- src/hooks/afterOperationWebhook.ts | 66 +++++++++++++++++++----- src/utils/collectionConfig.ts | 5 ++ src/utils/endpoints.ts | 2 +- src/utils/versionedCollectionConfig.ts | 29 +++++------ 9 files changed, 101 insertions(+), 43 deletions(-) diff --git a/src/collections/Currencies/Currencies.ts b/src/collections/Currencies/Currencies.ts index 755b248..0cc658c 100644 --- a/src/collections/Currencies/Currencies.ts +++ b/src/collections/Currencies/Currencies.ts @@ -2,7 +2,6 @@ import { text } from "payload/dist/fields/validations"; import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin"; import { shownOnlyToAdmin } from "../../accesses/collections/shownOnlyToAdmin"; import { CollectionGroups, Collections } from "../../constants"; -import { afterOperationWebhook } from "../../hooks/afterOperationWebhook"; import { buildCollectionConfig } from "../../utils/collectionConfig"; import { getAllEndpoint } from "./endpoints/getAllEndpoint"; import { importFromStrapi } from "./endpoints/importFromStrapi"; @@ -27,9 +26,6 @@ export const Currencies = buildCollectionConfig({ hidden: shownOnlyToAdmin, }, access: { create: mustBeAdmin, update: mustBeAdmin, delete: mustBeAdmin }, - hooks: { - afterOperation: [afterOperationWebhook], - }, endpoints: [importFromStrapi, getAllEndpoint], timestamps: false, fields: [ diff --git a/src/collections/Languages/Languages.ts b/src/collections/Languages/Languages.ts index bc2fad2..786f338 100644 --- a/src/collections/Languages/Languages.ts +++ b/src/collections/Languages/Languages.ts @@ -2,7 +2,6 @@ import { text } from "payload/dist/fields/validations"; import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin"; import { shownOnlyToAdmin } from "../../accesses/collections/shownOnlyToAdmin"; import { CollectionGroups, Collections } from "../../constants"; -import { afterOperationWebhook } from "../../hooks/afterOperationWebhook"; import { buildCollectionConfig } from "../../utils/collectionConfig"; import { getAllEndpoint } from "./endpoints/getAllEndpoint"; import { importFromStrapi } from "./endpoints/importFromStrapi"; @@ -28,9 +27,6 @@ export const Languages = buildCollectionConfig({ hidden: shownOnlyToAdmin, }, access: { create: mustBeAdmin, update: mustBeAdmin, delete: mustBeAdmin }, - hooks: { - afterOperation: [afterOperationWebhook], - }, timestamps: false, endpoints: [importFromStrapi, getAllEndpoint], fields: [ diff --git a/src/collections/Wordings/Wordings.ts b/src/collections/Wordings/Wordings.ts index 9e574c5..b8096f1 100644 --- a/src/collections/Wordings/Wordings.ts +++ b/src/collections/Wordings/Wordings.ts @@ -4,7 +4,6 @@ import { QuickFilters, languageBasedFilters } from "../../components/QuickFilter import { CollectionGroups, Collections } from "../../constants"; import { rowField } from "../../fields/rowField/rowField"; import { translatedFields } from "../../fields/translatedFields/translatedFields"; -import { afterOperationWebhook } from "../../hooks/afterOperationWebhook"; import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo"; import { buildCollectionConfig } from "../../utils/collectionConfig"; import { getAllEndpoint } from "./endpoints/getAllEndpoint"; @@ -43,9 +42,6 @@ export const Wordings: CollectionConfig = buildCollectionConfig({ create: mustBeAdmin, delete: mustBeAdmin, }, - hooks: { - afterOperation: [afterOperationWebhook], - }, endpoints: [getAllEndpoint], fields: [ { diff --git a/src/constants.ts b/src/constants.ts index 80e67a2..69c7422 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -86,6 +86,20 @@ export enum AttributeTypes { Tags = "Tags", } +/* WEB HOOKS */ + +export interface WebHookMessage { + collection: Collections; + operation: WebHookOperationType; + id?: string; +} + +export enum WebHookOperationType { + create = "create", + update = "update", + delete = "delete", +} + /* RICH TEXT */ export type RichTextContent = { diff --git a/src/hooks/afterChangeWebhook.ts b/src/hooks/afterChangeWebhook.ts index 31c2c94..48b9f1b 100644 --- a/src/hooks/afterChangeWebhook.ts +++ b/src/hooks/afterChangeWebhook.ts @@ -1,12 +1,24 @@ import { AfterChangeHook } from "payload/dist/globals/config/types"; +import { Collections, WebHookMessage, WebHookOperationType } from "../constants"; export const afterChangeWebhook: AfterChangeHook = async ({ doc, global }) => { const url = `${process.env.WEB_HOOK_URI}/collection-operation`; - await fetch(url, { + + const message: WebHookMessage = { + collection: global.slug as Collections, + operation: WebHookOperationType.update, + }; + + fetch(url, { headers: { Authorization: `Bearer ${process.env.WEB_HOOK_TOKEN}`, - Collection: global.slug, + "Content-Type": "application/json", }, + body: JSON.stringify(message), + method: "POST", + }).catch((e) => { + console.warn("Error while sending webhook", url, e); }); + return doc; }; diff --git a/src/hooks/afterOperationWebhook.ts b/src/hooks/afterOperationWebhook.ts index fa8939c..b64cd38 100644 --- a/src/hooks/afterOperationWebhook.ts +++ b/src/hooks/afterOperationWebhook.ts @@ -1,18 +1,58 @@ import { AfterOperationHook } from "payload/dist/collections/config/types"; +import { Collections, WebHookMessage, WebHookOperationType } from "../constants"; -export const afterOperationWebhook: AfterOperationHook = async ({ - result, - collection, - operation, -}) => { - if (["create", "delete", "deleteByID", "update", "updateByID"].includes(operation)) { - const url = `${process.env.WEB_HOOK_URI}/collection-operation`; - await fetch(url, { - headers: { - Authorization: `Bearer ${process.env.WEB_HOOK_TOKEN}`, - Collection: collection.slug, - }, - }); +const convertOperationToWebHookOperationType = ( + operation: string +): WebHookOperationType | undefined => { + switch (operation) { + case "create": + return WebHookOperationType.create; + + case "update": + case "updateByID": + return WebHookOperationType.update; + + case "delete": + case "deleteByID": + return WebHookOperationType.delete; + + default: + return undefined; } +}; + +export const afterOperationWebhook: AfterOperationHook = ({ result, collection, operation }) => { + const operationType = convertOperationToWebHookOperationType(operation); + if (!operationType) return result; + + if (operationType === WebHookOperationType.update) { + if ("_status" in result && result._status === "draft") { + return result; + } + } + + if (!("id" in result)) { + return result; + } + + const message: WebHookMessage = { + collection: collection.slug as Collections, + operation: operationType, + id: result.id, + }; + + const url = `${process.env.WEB_HOOK_URI}/collection-operation`; + + fetch(url, { + headers: { + Authorization: `Bearer ${process.env.WEB_HOOK_TOKEN}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(message), + method: "POST", + }).catch((e) => { + console.warn("Error while sending webhook", url, e); + }); + return result; }; diff --git a/src/utils/collectionConfig.ts b/src/utils/collectionConfig.ts index 44a1e70..669f8e6 100644 --- a/src/utils/collectionConfig.ts +++ b/src/utils/collectionConfig.ts @@ -1,5 +1,6 @@ import { GeneratedTypes } from "payload"; import { CollectionConfig } from "payload/types"; +import { afterOperationWebhook } from "../hooks/afterOperationWebhook"; import { formatToPascalCase } from "./string"; type CollectionConfigWithPlugins = CollectionConfig; @@ -15,4 +16,8 @@ export type BuildCollectionConfig = Omit< export const buildCollectionConfig = (config: BuildCollectionConfig): CollectionConfig => ({ ...config, typescript: { interface: formatToPascalCase(config.labels.singular) }, + hooks: { + ...config.hooks, + afterOperation: [...(config.hooks?.afterOperation ?? []), afterOperationWebhook], + }, }); diff --git a/src/utils/endpoints.ts b/src/utils/endpoints.ts index 667171f..db90e15 100644 --- a/src/utils/endpoints.ts +++ b/src/utils/endpoints.ts @@ -14,7 +14,7 @@ import { isNodeUploadNode, isUploadNodeAudioNode, isUploadNodeImageNode, - isUploadNodeVideoNode + isUploadNodeVideoNode, } from "../constants"; import { EndpointAttribute, diff --git a/src/utils/versionedCollectionConfig.ts b/src/utils/versionedCollectionConfig.ts index d4de227..0e067f2 100644 --- a/src/utils/versionedCollectionConfig.ts +++ b/src/utils/versionedCollectionConfig.ts @@ -1,6 +1,6 @@ import { CollectionBeforeChangeHook, CollectionConfig, RelationshipField } from "payload/types"; import { Collections } from "../constants"; -import { BuildCollectionConfig } from "./collectionConfig"; +import { BuildCollectionConfig, buildCollectionConfig } from "./collectionConfig"; const fields = { updatedBy: "updatedBy" }; @@ -19,17 +19,16 @@ const updatedByField = (): RelationshipField => ({ type BuildVersionedCollectionConfig = Omit; -export const buildVersionedCollectionConfig = ({ - hooks: { beforeChange, ...otherHooks } = {}, - fields, - ...otherParams -}: BuildVersionedCollectionConfig): CollectionConfig => ({ - ...otherParams, - timestamps: true, - versions: { drafts: { autosave: true } }, - hooks: { - ...otherHooks, - beforeChange: [...(beforeChange ?? []), beforeChangeUpdatedBy], - }, - fields: [...fields, updatedByField()], -}); +export const buildVersionedCollectionConfig = ( + config: BuildVersionedCollectionConfig +): CollectionConfig => + buildCollectionConfig({ + ...config, + timestamps: true, + versions: { drafts: { autosave: true } }, + hooks: { + ...config.hooks, + beforeChange: [...(config.hooks?.beforeChange ?? []), beforeChangeUpdatedBy], + }, + fields: [...config.fields, updatedByField()], + });