UNPKG

@directus/api

Version:

Directus is a real-time API and App dashboard for managing SQL database content

101 lines (99 loc) 4.52 kB
import { parseJSON, toArray } from "@directus/utils"; import { randomUUID } from "node:crypto"; //#region src/database/migrations/20240311A-deprecate-webhooks.ts const TABLE_WEBHOOKS = "directus_webhooks"; const TABLE_FLOWS = "directus_flows"; const TABLE_OPERATIONS = "directus_operations"; const NEW_COLUMN_WAS_ACTIVE = "was_active_before_deprecation"; const NEW_COLUMN_FLOW = "migrated_flow"; /** * 0. Identify and persist which webhooks were active before deprecation * 1. Migrate existing webhooks over to identically behaving Flows * 2. Disable existing webhooks * 3. Dont drop webhooks yet */ async function up(knex) { await knex.schema.alterTable(TABLE_WEBHOOKS, (table) => { table.boolean(NEW_COLUMN_WAS_ACTIVE).notNullable().defaultTo(false); table.uuid(NEW_COLUMN_FLOW).nullable(); table.foreign(NEW_COLUMN_FLOW).references(`${TABLE_FLOWS}.id`).onDelete("SET NULL"); }); await knex(TABLE_WEBHOOKS).update({ [NEW_COLUMN_WAS_ACTIVE]: true }).where({ status: "active" }); const webhooks = await knex.select("*").from(TABLE_WEBHOOKS); await knex.transaction(async (trx) => { for (const webhook of webhooks) { const flowId = randomUUID(); const operationIdRunScript = randomUUID(); const operationIdWebhook = randomUUID(); const newFlow = { id: flowId, name: webhook.name, icon: "webhook", description: "Auto-generated by Directus Migration\n\nWebhooks were deprecated and have been moved into Flows for you automatically.", status: webhook.status, trigger: "event", options: { type: "action", scope: toArray(webhook.actions).filter((action) => action.trim() !== "").map((scope) => `items.${scope}`), collections: toArray(webhook.collections).filter((collection) => collection.trim() !== "") }, operation: null }; const operationRunScript = { id: operationIdRunScript, name: "Transforming Payload for Webhook", key: "payload-transform", type: "exec", position_x: 19, position_y: 1, options: { code: "/**\n * Webhook data slightly differs from Flow trigger data.\n * This flow converts the new Flow format into the older Webhook shape\n * in order to not break existing logic of consumers.\n */ \nmodule.exports = async function(data) {\n const crudOperation = data.$trigger.event.split(\".\").at(-1);\n const keyOrKeys = crudOperation === \"create\" ? \"key\" : \"keys\";\n return {\n event: `items.${crudOperation}`,\n accountability: { user: data.$accountability.user, role: data.$accountability.role },\n payload: data.$trigger.payload,\n [keyOrKeys]: data.$trigger?.[keyOrKeys],\n collection: data.$trigger.collection,\n };\n}" }, resolve: operationIdWebhook, reject: null, flow: flowId }; const operationWebhook = { id: operationIdWebhook, name: "Webhook", key: "webhook", type: "request", position_x: 37, position_y: 1, options: { url: webhook.url, body: webhook.data ? "{{$last}}" : void 0, method: webhook.method, headers: typeof webhook.headers === "string" ? parseJSON(webhook.headers) : webhook.headers }, resolve: null, reject: null, flow: flowId }; await trx(TABLE_FLOWS).insert(newFlow); if (webhook.data && webhook.method !== "GET") { await trx(TABLE_OPERATIONS).insert(operationWebhook); await trx(TABLE_OPERATIONS).insert(operationRunScript); await trx(TABLE_FLOWS).update({ operation: operationIdRunScript }).where({ id: flowId }); } else { operationWebhook.position_x = 19; await trx(TABLE_OPERATIONS).insert(operationWebhook); await trx(TABLE_FLOWS).update({ operation: operationIdWebhook }).where({ id: flowId }); } await trx(TABLE_WEBHOOKS).update({ [NEW_COLUMN_FLOW]: flowId }).where({ id: webhook.id }); await trx(TABLE_WEBHOOKS).update({ status: "inactive" }); } }); } /** * Dont completely drop Webhooks yet. * Lets preserve the data and drop them in the next release to be extra safe with users data. */ async function down(knex) { const migratedFlowIds = (await knex(TABLE_WEBHOOKS).select(NEW_COLUMN_FLOW).whereNotNull(NEW_COLUMN_FLOW)).map((col) => col[NEW_COLUMN_FLOW]); await knex(TABLE_FLOWS).delete().whereIn("id", migratedFlowIds); await knex(TABLE_WEBHOOKS).update({ status: "active" }).where({ [NEW_COLUMN_WAS_ACTIVE]: true }); await knex.schema.alterTable(TABLE_WEBHOOKS, (table) => { table.dropColumns(NEW_COLUMN_WAS_ACTIVE, NEW_COLUMN_FLOW); }); } //#endregion export { down, up };