diff --git a/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte b/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte
index f3273aa5e..cf3dc8f31 100644
--- a/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte
+++ b/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte
@@ -8,10 +8,13 @@
let name
let selectedTrigger
+ let nameTouched = false
let triggerVal
export let webhookModal
$: instanceId = $database._id
+ $: nameError =
+ nameTouched && !name ? "Please specify a name for the automation." : null
async function createAutomation() {
await automationStore.actions.create({
@@ -51,13 +54,18 @@
confirmText="Save"
size="M"
onConfirm={createAutomation}
- disabled={!selectedTrigger}
+ disabled={!selectedTrigger || !name}
>
Please name your automation, then select a trigger. Every automation must
start with a trigger.
-
+ (nameTouched = true)}
+ bind:error={nameError}
+ label="Name"
+ />
Triggers
diff --git a/packages/server/src/api/controllers/query.js b/packages/server/src/api/controllers/query.js
index 89bf1a8ab..a22cda4eb 100644
--- a/packages/server/src/api/controllers/query.js
+++ b/packages/server/src/api/controllers/query.js
@@ -23,9 +23,6 @@ function formatResponse(resp) {
try {
resp = JSON.parse(resp)
} catch (err) {
- console.error(
- "Error parsing JSON response. Returning string in array instead."
- )
resp = { response: resp }
}
}
diff --git a/packages/server/src/api/controllers/webhook.js b/packages/server/src/api/controllers/webhook.js
index c810f8500..15ee748d8 100644
--- a/packages/server/src/api/controllers/webhook.js
+++ b/packages/server/src/api/controllers/webhook.js
@@ -3,6 +3,7 @@ const { generateWebhookID, getWebhookParams } = require("../../db/utils")
const toJsonSchema = require("to-json-schema")
const validate = require("jsonschema").validate
const triggers = require("../../automations/triggers")
+const { getDeployedAppID } = require("@budibase/auth/db")
const AUTOMATION_DESCRIPTION = "Generated from Webhook Schema"
@@ -76,24 +77,34 @@ exports.buildSchema = async ctx => {
}
exports.trigger = async ctx => {
- const db = new CouchDB(ctx.params.instance)
- const webhook = await db.get(ctx.params.id)
- // validate against the schema
- if (webhook.bodySchema) {
- validate(ctx.request.body, webhook.bodySchema)
- }
- const target = await db.get(webhook.action.target)
- if (webhook.action.type === exports.WebhookType.AUTOMATION) {
- // trigger with both the pure request and then expand it
- // incase the user has produced a schema to bind to
- await triggers.externalTrigger(target, {
- body: ctx.request.body,
- ...ctx.request.body,
- appId: ctx.params.instance,
- })
- }
- ctx.status = 200
- ctx.body = {
- message: "Webhook trigger fired successfully",
+ const prodAppId = getDeployedAppID(ctx.params.instance)
+ try {
+ const db = new CouchDB(prodAppId)
+ const webhook = await db.get(ctx.params.id)
+ // validate against the schema
+ if (webhook.bodySchema) {
+ validate(ctx.request.body, webhook.bodySchema)
+ }
+ const target = await db.get(webhook.action.target)
+ if (webhook.action.type === exports.WebhookType.AUTOMATION) {
+ // trigger with both the pure request and then expand it
+ // incase the user has produced a schema to bind to
+ await triggers.externalTrigger(target, {
+ body: ctx.request.body,
+ ...ctx.request.body,
+ appId: prodAppId,
+ })
+ }
+ 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.",
+ }
+ }
}
}
diff --git a/packages/server/src/automations/steps/outgoingWebhook.js b/packages/server/src/automations/steps/outgoingWebhook.js
index 34299d23b..f0637c335 100644
--- a/packages/server/src/automations/steps/outgoingWebhook.js
+++ b/packages/server/src/automations/steps/outgoingWebhook.js
@@ -85,6 +85,18 @@ exports.run = async function ({ inputs }) {
const request = {
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 (
requestBody &&
requestBody.length !== 0 &&
@@ -95,21 +107,9 @@ exports.run = async function ({ inputs }) {
? requestBody
: JSON.stringify(requestBody)
request.headers = {
+ ...request.headers,
"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 {
@@ -122,7 +122,7 @@ exports.run = async function ({ inputs }) {
return {
httpStatus: status,
response: message,
- success: status === 200,
+ success: status >= 200 && status <= 206,
}
} catch (err) {
/* istanbul ignore next */
diff --git a/packages/server/src/automations/utils.js b/packages/server/src/automations/utils.js
index 4bef91a15..f2d1bf569 100644
--- a/packages/server/src/automations/utils.js
+++ b/packages/server/src/automations/utils.js
@@ -6,6 +6,7 @@ const { queue } = require("./bullboard")
const newid = require("../db/newid")
const { updateEntityMetadata } = require("../utilities")
const { MetadataTypes } = require("../constants")
+const { getDeployedAppID } = require("@budibase/auth/db")
const WH_STEP_ID = definitions.WEBHOOK.stepId
const CRON_STEP_ID = definitions.CRON.stepId
@@ -150,9 +151,13 @@ exports.checkForWebhooks = async ({ appId, oldAuto, newAuto }) => {
await webhooks.save(ctx)
const id = ctx.body.webhook._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 = {
schemaUrl: `api/webhooks/schema/${appId}/${id}`,
- triggerUrl: `api/webhooks/trigger/${appId}/${id}`,
+ triggerUrl: `api/webhooks/trigger/${prodAppId}/${id}`,
}
}
return newAuto
diff --git a/packages/server/src/integrations/rest.ts b/packages/server/src/integrations/rest.ts
index 9c6ece52d..cf234518d 100644
--- a/packages/server/src/integrations/rest.ts
+++ b/packages/server/src/integrations/rest.ts
@@ -142,13 +142,11 @@ module RestModule {
}
async parseResponse(response: any) {
- switch (this.headers.Accept) {
- case "application/json":
- return await response.json()
- case "text/html":
- return await response.text()
- default:
- return await response.json()
+ const contentType = response.headers.get("content-type")
+ if (contentType && contentType.indexOf("application/json") !== -1) {
+ return await response.json()
+ } else {
+ return await response.text()
}
}
@@ -191,7 +189,7 @@ module RestModule {
}
const response = await fetch(this.getUrl(path, queryString), {
- method: "POST",
+ method: "PUT",
headers: this.headers,
body: JSON.stringify(json),
})
diff --git a/packages/server/src/integrations/tests/rest.spec.js b/packages/server/src/integrations/tests/rest.spec.js
index cb69d3c38..7b128a6d1 100644
--- a/packages/server/src/integrations/tests/rest.spec.js
+++ b/packages/server/src/integrations/tests/rest.spec.js
@@ -1,5 +1,11 @@
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 RestIntegration = require("../rest")
diff --git a/packages/server/src/middleware/authorized.js b/packages/server/src/middleware/authorized.js
index bd064f7e6..d91311e16 100644
--- a/packages/server/src/middleware/authorized.js
+++ b/packages/server/src/middleware/authorized.js
@@ -5,20 +5,17 @@ const {
doesHaveBasePermission,
} = require("@budibase/auth/permissions")
const builderMiddleware = require("./builder")
+const { isWebhookEndpoint } = require("./utils")
function hasResource(ctx) {
return ctx.resourceId != null
}
-const WEBHOOK_ENDPOINTS = new RegExp(
- ["webhooks/trigger", "webhooks/schema"].join("|")
-)
-
module.exports =
(permType, permLevel = null) =>
async (ctx, next) => {
// webhooks don't need authentication, each webhook unique
- if (WEBHOOK_ENDPOINTS.test(ctx.request.url)) {
+ if (isWebhookEndpoint(ctx)) {
return next()
}
diff --git a/packages/server/src/middleware/currentapp.js b/packages/server/src/middleware/currentapp.js
index 01b9dcc24..5682a860b 100644
--- a/packages/server/src/middleware/currentapp.js
+++ b/packages/server/src/middleware/currentapp.js
@@ -9,6 +9,7 @@ const { isUserInAppTenant } = require("@budibase/auth/tenancy")
const { getCachedSelf } = require("../utilities/global")
const CouchDB = require("../db")
const env = require("../environment")
+const { isWebhookEndpoint } = require("./utils")
module.exports = async (ctx, next) => {
// try to get the appID from the request
@@ -38,6 +39,7 @@ module.exports = async (ctx, next) => {
// deny access to application preview
if (
isDevAppID(requestAppId) &&
+ !isWebhookEndpoint(ctx) &&
(!ctx.user || !ctx.user.builder || !ctx.user.builder.global)
) {
clearCookie(ctx, Cookies.CurrentApp)
diff --git a/packages/server/src/middleware/utils.js b/packages/server/src/middleware/utils.js
new file mode 100644
index 000000000..b1eea8cd6
--- /dev/null
+++ b/packages/server/src/middleware/utils.js
@@ -0,0 +1,7 @@
+const WEBHOOK_ENDPOINTS = new RegExp(
+ ["webhooks/trigger", "webhooks/schema"].join("|")
+)
+
+exports.isWebhookEndpoint = ctx => {
+ return WEBHOOK_ENDPOINTS.test(ctx.request.url)
+}