Webhook API typing.

This commit is contained in:
mike12345567 2024-12-04 16:14:54 +00:00
parent aa2d6779cd
commit bd27d1755f
3 changed files with 91 additions and 53 deletions

View File

@ -4,9 +4,17 @@ import { db as dbCore, context } from "@budibase/backend-core"
import { import {
Webhook, Webhook,
WebhookActionType, WebhookActionType,
BBContext, Ctx,
Automation, Automation,
AutomationActionStepId, AutomationActionStepId,
FetchWebhooksResponse,
SaveWebhookResponse,
SaveWebhookRequest,
DeleteWebhookResponse,
BuildWebhookSchemaRequest,
BuildWebhookSchemaResponse,
TriggerWebhookRequest,
TriggerWebhookResponse,
} from "@budibase/types" } from "@budibase/types"
import sdk from "../../sdk" import sdk from "../../sdk"
import * as pro from "@budibase/pro" import * as pro from "@budibase/pro"
@ -16,17 +24,17 @@ const validate = require("jsonschema").validate
const AUTOMATION_DESCRIPTION = "Generated from Webhook Schema" const AUTOMATION_DESCRIPTION = "Generated from Webhook Schema"
export async function fetch(ctx: BBContext) { export async function fetch(ctx: Ctx<void, FetchWebhooksResponse>) {
const db = context.getAppDB() const db = context.getAppDB()
const response = await db.allDocs( const response = await db.allDocs<Webhook>(
getWebhookParams(null, { getWebhookParams(null, {
include_docs: true, include_docs: true,
}) })
) )
ctx.body = response.rows.map((row: any) => row.doc) ctx.body = response.rows.filter(row => row.doc).map(row => row.doc!)
} }
export async function save(ctx: BBContext) { export async function save(ctx: Ctx<SaveWebhookRequest, SaveWebhookResponse>) {
const webhook = await sdk.automations.webhook.save(ctx.request.body) const webhook = await sdk.automations.webhook.save(ctx.request.body)
ctx.body = { ctx.body = {
message: "Webhook created successfully", message: "Webhook created successfully",
@ -34,21 +42,23 @@ export async function save(ctx: BBContext) {
} }
} }
export async function destroy(ctx: BBContext) { export async function destroy(ctx: Ctx<void, DeleteWebhookResponse>) {
ctx.body = await sdk.automations.webhook.destroy( ctx.body = await sdk.automations.webhook.destroy(
ctx.params.id, ctx.params.id,
ctx.params.rev ctx.params.rev
) )
} }
export async function buildSchema(ctx: BBContext) { export async function buildSchema(
ctx: Ctx<BuildWebhookSchemaRequest, BuildWebhookSchemaResponse>
) {
await context.doInAppContext(ctx.params.instance, async () => { await context.doInAppContext(ctx.params.instance, async () => {
const db = context.getAppDB() const db = context.getAppDB()
const webhook = (await db.get(ctx.params.id)) as Webhook const webhook = await db.get<Webhook>(ctx.params.id)
webhook.bodySchema = toJsonSchema(ctx.request.body) webhook.bodySchema = toJsonSchema(ctx.request.body)
// update the automation outputs // update the automation outputs
if (webhook.action.type === WebhookActionType.AUTOMATION) { if (webhook.action.type === WebhookActionType.AUTOMATION) {
let automation = (await db.get(webhook.action.target)) as Automation let automation = await db.get<Automation>(webhook.action.target)
const autoOutputs = automation.definition.trigger.schema.outputs const autoOutputs = automation.definition.trigger.schema.outputs
let properties = webhook.bodySchema.properties let properties = webhook.bodySchema.properties
// reset webhook outputs // reset webhook outputs
@ -67,60 +77,66 @@ export async function buildSchema(ctx: BBContext) {
}) })
} }
export async function trigger(ctx: BBContext) { export async function trigger(
ctx: Ctx<TriggerWebhookRequest, TriggerWebhookResponse>
) {
const prodAppId = dbCore.getProdAppID(ctx.params.instance) const prodAppId = dbCore.getProdAppID(ctx.params.instance)
const appNotDeployed = () => {
ctx.body = {
message: "Application not deployed yet.",
}
}
await context.doInAppContext(prodAppId, async () => { await context.doInAppContext(prodAppId, async () => {
try { const db = context.getAppDB()
const db = context.getAppDB() const webhook = await db.tryGet<Webhook>(ctx.params.id)
const webhook = (await db.get(ctx.params.id)) as Webhook if (!webhook) {
// validate against the schema return appNotDeployed()
if (webhook.bodySchema) { }
validate(ctx.request.body, webhook.bodySchema) // validate against the schema
} if (webhook.bodySchema) {
const target = await db.get<Automation>(webhook.action.target) validate(ctx.request.body, webhook.bodySchema)
if (webhook.action.type === WebhookActionType.AUTOMATION) { }
// trigger with both the pure request and then expand it const target = await db.tryGet<Automation>(webhook.action.target)
// incase the user has produced a schema to bind to if (!target) {
let hasCollectStep = sdk.automations.utils.checkForCollectStep(target) return appNotDeployed()
}
if (webhook.action.type === WebhookActionType.AUTOMATION) {
// trigger with both the pure request and then expand it
// incase the user has produced a schema to bind to
let hasCollectStep = sdk.automations.utils.checkForCollectStep(target)
if (hasCollectStep && (await pro.features.isSyncAutomationsEnabled())) { if (hasCollectStep && (await pro.features.isSyncAutomationsEnabled())) {
const response = await triggers.externalTrigger( const response = await triggers.externalTrigger(
target, target,
{ {
body: ctx.request.body, fields: {
...ctx.request.body, ...ctx.request.body,
appId: prodAppId, body: ctx.request.body,
}, },
{ getResponses: true } appId: prodAppId,
},
{ getResponses: true }
)
if (triggers.isAutomationResults(response)) {
let collectedValue = response.steps.find(
(step: any) => step.stepId === AutomationActionStepId.COLLECT
) )
if (triggers.isAutomationResults(response)) { ctx.body = collectedValue?.outputs
let collectedValue = response.steps.find(
(step: any) => step.stepId === AutomationActionStepId.COLLECT
)
ctx.status = 200
ctx.body = collectedValue?.outputs
} else {
ctx.throw(400, "Automation did not have a collect block.")
}
} else { } else {
await triggers.externalTrigger(target, { ctx.throw(400, "Automation did not have a collect block.")
body: ctx.request.body,
...ctx.request.body,
appId: prodAppId,
})
ctx.status = 200
ctx.body = {
message: "Webhook trigger fired successfully",
}
} }
} } else {
} catch (err: any) { await triggers.externalTrigger(target, {
if (err.status === 404) { fields: {
ctx.status = 200 ...ctx.request.body,
body: ctx.request.body,
},
appId: prodAppId,
})
ctx.body = { ctx.body = {
message: "Application not deployed yet.", message: "Webhook trigger fired successfully",
} }
} }
} }

View File

@ -17,3 +17,4 @@ export * from "./application"
export * from "./layout" export * from "./layout"
export * from "./deployment" export * from "./deployment"
export * from "./role" export * from "./role"
export * from "./webhook"

View File

@ -0,0 +1,21 @@
import { Webhook } from "../../../documents"
import { DocumentDestroyResponse, DocumentInsertResponse } from "@budibase/nano"
export type FetchWebhooksResponse = Webhook[]
export interface SaveWebhookRequest extends Webhook {}
export interface SaveWebhookResponse {
message: string
webhook: Webhook
}
export interface DeleteWebhookResponse extends DocumentDestroyResponse {}
export interface BuildWebhookSchemaRequest extends Record<string, any> {}
export interface BuildWebhookSchemaResponse extends DocumentInsertResponse {}
export interface TriggerWebhookRequest {}
export type TriggerWebhookResponse =
| Record<string, any>
| { message: string }
| undefined