Merge pull request #3239 from Budibase/fix/mike-various

Various fixes for recent issues
This commit is contained in:
Michael Drury 2021-11-04 09:13:27 +00:00 committed by GitHub
commit 4d6b63b537
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 84 additions and 53 deletions

View File

@ -8,10 +8,13 @@
let name let name
let selectedTrigger let selectedTrigger
let nameTouched = false
let triggerVal let triggerVal
export let webhookModal export let webhookModal
$: instanceId = $database._id $: instanceId = $database._id
$: nameError =
nameTouched && !name ? "Please specify a name for the automation." : null
async function createAutomation() { async function createAutomation() {
await automationStore.actions.create({ await automationStore.actions.create({
@ -51,13 +54,18 @@
confirmText="Save" confirmText="Save"
size="M" size="M"
onConfirm={createAutomation} onConfirm={createAutomation}
disabled={!selectedTrigger} disabled={!selectedTrigger || !name}
> >
<Body size="XS" <Body size="XS"
>Please name your automation, then select a trigger. Every automation must >Please name your automation, then select a trigger. Every automation must
start with a trigger. start with a trigger.
</Body> </Body>
<Input bind:value={name} label="Name" /> <Input
bind:value={name}
on:change={() => (nameTouched = true)}
bind:error={nameError}
label="Name"
/>
<Layout noPadding> <Layout noPadding>
<Body size="S">Triggers</Body> <Body size="S">Triggers</Body>

View File

@ -23,9 +23,6 @@ function formatResponse(resp) {
try { try {
resp = JSON.parse(resp) resp = JSON.parse(resp)
} catch (err) { } catch (err) {
console.error(
"Error parsing JSON response. Returning string in array instead."
)
resp = { response: resp } resp = { response: resp }
} }
} }

View File

@ -3,6 +3,7 @@ const { generateWebhookID, getWebhookParams } = require("../../db/utils")
const toJsonSchema = require("to-json-schema") const toJsonSchema = require("to-json-schema")
const validate = require("jsonschema").validate const validate = require("jsonschema").validate
const triggers = require("../../automations/triggers") const triggers = require("../../automations/triggers")
const { getDeployedAppID } = require("@budibase/auth/db")
const AUTOMATION_DESCRIPTION = "Generated from Webhook Schema" const AUTOMATION_DESCRIPTION = "Generated from Webhook Schema"
@ -76,24 +77,34 @@ exports.buildSchema = async ctx => {
} }
exports.trigger = async ctx => { exports.trigger = async ctx => {
const db = new CouchDB(ctx.params.instance) const prodAppId = getDeployedAppID(ctx.params.instance)
const webhook = await db.get(ctx.params.id) try {
// validate against the schema const db = new CouchDB(prodAppId)
if (webhook.bodySchema) { const webhook = await db.get(ctx.params.id)
validate(ctx.request.body, webhook.bodySchema) // validate against the schema
} if (webhook.bodySchema) {
const target = await db.get(webhook.action.target) validate(ctx.request.body, webhook.bodySchema)
if (webhook.action.type === exports.WebhookType.AUTOMATION) { }
// trigger with both the pure request and then expand it const target = await db.get(webhook.action.target)
// incase the user has produced a schema to bind to if (webhook.action.type === exports.WebhookType.AUTOMATION) {
await triggers.externalTrigger(target, { // trigger with both the pure request and then expand it
body: ctx.request.body, // incase the user has produced a schema to bind to
...ctx.request.body, await triggers.externalTrigger(target, {
appId: ctx.params.instance, body: ctx.request.body,
}) ...ctx.request.body,
} appId: prodAppId,
ctx.status = 200 })
ctx.body = { }
message: "Webhook trigger fired successfully", ctx.status = 200
ctx.body = {
message: "Webhook trigger fired successfully",
}
} catch (err) {
if (err.status === 404) {
ctx.status = 200
ctx.body = {
message: "Application not deployed yet.",
}
}
} }
} }

View File

@ -85,6 +85,18 @@ exports.run = async function ({ inputs }) {
const request = { const request = {
method: requestMethod, method: requestMethod,
} }
if (headers) {
try {
const customHeaders =
typeof headers === "string" ? JSON.parse(headers) : headers
request.headers = { ...request.headers, ...customHeaders }
} catch (err) {
return {
success: false,
response: "Unable to process headers, must be a JSON object.",
}
}
}
if ( if (
requestBody && requestBody &&
requestBody.length !== 0 && requestBody.length !== 0 &&
@ -95,21 +107,9 @@ exports.run = async function ({ inputs }) {
? requestBody ? requestBody
: JSON.stringify(requestBody) : JSON.stringify(requestBody)
request.headers = { request.headers = {
...request.headers,
"Content-Type": "application/json", "Content-Type": "application/json",
} }
if (headers) {
try {
const customHeaders =
typeof headers === "string" ? JSON.parse(headers) : headers
request.headers = { ...request.headers, ...customHeaders }
} catch (err) {
return {
success: false,
response: "Unable to process headers, must be a JSON object.",
}
}
}
} }
try { try {
@ -122,7 +122,7 @@ exports.run = async function ({ inputs }) {
return { return {
httpStatus: status, httpStatus: status,
response: message, response: message,
success: status === 200, success: status >= 200 && status <= 206,
} }
} catch (err) { } catch (err) {
/* istanbul ignore next */ /* istanbul ignore next */

View File

@ -6,6 +6,7 @@ const { queue } = require("./bullboard")
const newid = require("../db/newid") const newid = require("../db/newid")
const { updateEntityMetadata } = require("../utilities") const { updateEntityMetadata } = require("../utilities")
const { MetadataTypes } = require("../constants") const { MetadataTypes } = require("../constants")
const { getDeployedAppID } = require("@budibase/auth/db")
const WH_STEP_ID = definitions.WEBHOOK.stepId const WH_STEP_ID = definitions.WEBHOOK.stepId
const CRON_STEP_ID = definitions.CRON.stepId const CRON_STEP_ID = definitions.CRON.stepId
@ -150,9 +151,13 @@ exports.checkForWebhooks = async ({ appId, oldAuto, newAuto }) => {
await webhooks.save(ctx) await webhooks.save(ctx)
const id = ctx.body.webhook._id const id = ctx.body.webhook._id
newTrigger.webhookId = id newTrigger.webhookId = id
// the app ID has to be development for this endpoint
// it can only be used when building the app
// but the trigger endpoint will always be used in production
const prodAppId = getDeployedAppID(appId)
newTrigger.inputs = { newTrigger.inputs = {
schemaUrl: `api/webhooks/schema/${appId}/${id}`, schemaUrl: `api/webhooks/schema/${appId}/${id}`,
triggerUrl: `api/webhooks/trigger/${appId}/${id}`, triggerUrl: `api/webhooks/trigger/${prodAppId}/${id}`,
} }
} }
return newAuto return newAuto

View File

@ -142,13 +142,11 @@ module RestModule {
} }
async parseResponse(response: any) { async parseResponse(response: any) {
switch (this.headers.Accept) { const contentType = response.headers.get("content-type")
case "application/json": if (contentType && contentType.indexOf("application/json") !== -1) {
return await response.json() return await response.json()
case "text/html": } else {
return await response.text() return await response.text()
default:
return await response.json()
} }
} }
@ -191,7 +189,7 @@ module RestModule {
} }
const response = await fetch(this.getUrl(path, queryString), { const response = await fetch(this.getUrl(path, queryString), {
method: "POST", method: "PUT",
headers: this.headers, headers: this.headers,
body: JSON.stringify(json), body: JSON.stringify(json),
}) })

View File

@ -1,5 +1,11 @@
jest.mock("node-fetch", () => jest.mock("node-fetch", () =>
jest.fn(() => ({ json: jest.fn(), text: jest.fn() })) jest.fn(() => ({
headers: {
get: () => ["application/json"]
},
json: jest.fn(),
text: jest.fn()
}))
) )
const fetch = require("node-fetch") const fetch = require("node-fetch")
const RestIntegration = require("../rest") const RestIntegration = require("../rest")

View File

@ -5,20 +5,17 @@ const {
doesHaveBasePermission, doesHaveBasePermission,
} = require("@budibase/auth/permissions") } = require("@budibase/auth/permissions")
const builderMiddleware = require("./builder") const builderMiddleware = require("./builder")
const { isWebhookEndpoint } = require("./utils")
function hasResource(ctx) { function hasResource(ctx) {
return ctx.resourceId != null return ctx.resourceId != null
} }
const WEBHOOK_ENDPOINTS = new RegExp(
["webhooks/trigger", "webhooks/schema"].join("|")
)
module.exports = module.exports =
(permType, permLevel = null) => (permType, permLevel = null) =>
async (ctx, next) => { async (ctx, next) => {
// webhooks don't need authentication, each webhook unique // webhooks don't need authentication, each webhook unique
if (WEBHOOK_ENDPOINTS.test(ctx.request.url)) { if (isWebhookEndpoint(ctx)) {
return next() return next()
} }

View File

@ -9,6 +9,7 @@ const { isUserInAppTenant } = require("@budibase/auth/tenancy")
const { getCachedSelf } = require("../utilities/global") const { getCachedSelf } = require("../utilities/global")
const CouchDB = require("../db") const CouchDB = require("../db")
const env = require("../environment") const env = require("../environment")
const { isWebhookEndpoint } = require("./utils")
module.exports = async (ctx, next) => { module.exports = async (ctx, next) => {
// try to get the appID from the request // try to get the appID from the request
@ -38,6 +39,7 @@ module.exports = async (ctx, next) => {
// deny access to application preview // deny access to application preview
if ( if (
isDevAppID(requestAppId) && isDevAppID(requestAppId) &&
!isWebhookEndpoint(ctx) &&
(!ctx.user || !ctx.user.builder || !ctx.user.builder.global) (!ctx.user || !ctx.user.builder || !ctx.user.builder.global)
) { ) {
clearCookie(ctx, Cookies.CurrentApp) clearCookie(ctx, Cookies.CurrentApp)

View File

@ -0,0 +1,7 @@
const WEBHOOK_ENDPOINTS = new RegExp(
["webhooks/trigger", "webhooks/schema"].join("|")
)
exports.isWebhookEndpoint = ctx => {
return WEBHOOK_ENDPOINTS.test(ctx.request.url)
}