diff --git a/lerna.json b/lerna.json
index 18f9328afd..2b7b35e6fd 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "0.9.176-alpha.3",
+ "version": "0.9.180-alpha.0",
"npmClient": "yarn",
"packages": [
"packages/*"
diff --git a/packages/auth/package.json b/packages/auth/package.json
index b5f27ca059..91305f3cb9 100644
--- a/packages/auth/package.json
+++ b/packages/auth/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/auth",
- "version": "0.9.176-alpha.3",
+ "version": "0.9.180-alpha.0",
"description": "Authentication middlewares for budibase builder and apps",
"main": "src/index.js",
"author": "Budibase",
diff --git a/packages/auth/src/constants.js b/packages/auth/src/constants.js
index 4b4aef5a42..9892275bec 100644
--- a/packages/auth/src/constants.js
+++ b/packages/auth/src/constants.js
@@ -6,6 +6,7 @@ exports.UserStatus = {
exports.Cookies = {
CurrentApp: "budibase:currentapp",
Auth: "budibase:auth",
+ Init: "budibase:init",
OIDC_CONFIG: "budibase:oidc:config",
}
diff --git a/packages/bbui/package.json b/packages/bbui/package.json
index 0d1c5c15bc..ba325bcd0b 100644
--- a/packages/bbui/package.json
+++ b/packages/bbui/package.json
@@ -1,7 +1,7 @@
{
"name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.",
- "version": "0.9.176-alpha.3",
+ "version": "0.9.180-alpha.0",
"license": "AGPL-3.0",
"svelte": "src/index.js",
"module": "dist/bbui.es.js",
diff --git a/packages/builder/package.json b/packages/builder/package.json
index 56811c6d11..c362ce6912 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
- "version": "0.9.176-alpha.3",
+ "version": "0.9.180-alpha.0",
"license": "AGPL-3.0",
"private": true,
"scripts": {
@@ -65,10 +65,10 @@
}
},
"dependencies": {
- "@budibase/bbui": "^0.9.176-alpha.3",
- "@budibase/client": "^0.9.176-alpha.3",
+ "@budibase/bbui": "^0.9.180-alpha.0",
+ "@budibase/client": "^0.9.180-alpha.0",
"@budibase/colorpicker": "1.1.2",
- "@budibase/string-templates": "^0.9.176-alpha.3",
+ "@budibase/string-templates": "^0.9.180-alpha.0",
"@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",
diff --git a/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte b/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte
index f3273aa5ec..cf3dc8f314 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/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte
index 2c8ae25abe..1db5e46261 100644
--- a/packages/builder/src/components/start/CreateAppModal.svelte
+++ b/packages/builder/src/components/start/CreateAppModal.svelte
@@ -9,7 +9,7 @@
Checkbox,
} from "@budibase/bbui"
import { store, automationStore, hostingStore } from "builderStore"
- import { admin } from "stores/portal"
+ import { admin, auth } from "stores/portal"
import { string, mixed, object } from "yup"
import api, { get, post } from "builderStore/api"
import analytics, { Events } from "analytics"
@@ -139,6 +139,7 @@
}
const userResp = await api.post(`/api/users/metadata/self`, user)
await userResp.json()
+ await auth.setInitInfo({})
$goto(`/builder/app/${appJson.instance._id}`)
} catch (error) {
console.error(error)
@@ -146,6 +147,16 @@
submitting = false
}
}
+
+ function getModalTitle() {
+ let title = "Create App"
+ if (template.fromFile) {
+ title = "Import App"
+ } else if (template.key) {
+ title = "Create app from template"
+ }
+ return title
+ }
{#if showTemplateSelection}
@@ -172,7 +183,7 @@
{:else}
(template = null) : null}
diff --git a/packages/builder/src/pages/builder/_layout.svelte b/packages/builder/src/pages/builder/_layout.svelte
index 39c93f0fb0..179131107f 100644
--- a/packages/builder/src/pages/builder/_layout.svelte
+++ b/packages/builder/src/pages/builder/_layout.svelte
@@ -1,5 +1,5 @@
diff --git a/packages/builder/src/stores/portal/auth.js b/packages/builder/src/stores/portal/auth.js
index 134232dd74..bd08611fc9 100644
--- a/packages/builder/src/stores/portal/auth.js
+++ b/packages/builder/src/stores/portal/auth.js
@@ -83,6 +83,13 @@ export function createAuthStore() {
return {
subscribe: store.subscribe,
setOrganisation: setOrganisation,
+ getInitInfo: async () => {
+ const response = await api.get(`/api/global/auth/init`)
+ return await response.json()
+ },
+ setInitInfo: async info => {
+ await api.post(`/api/global/auth/init`, info)
+ },
checkQueryString: async () => {
const urlParams = new URLSearchParams(window.location.search)
if (urlParams.has("tenantId")) {
diff --git a/packages/cli/package.json b/packages/cli/package.json
index f6f873edc1..02c0c8e08e 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/cli",
- "version": "0.9.176-alpha.3",
+ "version": "0.9.180-alpha.0",
"description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js",
"bin": {
diff --git a/packages/client/package.json b/packages/client/package.json
index dc487f6bf5..c925087740 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/client",
- "version": "0.9.176-alpha.3",
+ "version": "0.9.180-alpha.0",
"license": "MPL-2.0",
"module": "dist/budibase-client.js",
"main": "dist/budibase-client.js",
@@ -19,9 +19,9 @@
"dev:builder": "rollup -cw"
},
"dependencies": {
- "@budibase/bbui": "^0.9.176-alpha.3",
+ "@budibase/bbui": "^0.9.180-alpha.0",
"@budibase/standard-components": "^0.9.139",
- "@budibase/string-templates": "^0.9.176-alpha.3",
+ "@budibase/string-templates": "^0.9.180-alpha.0",
"regexparam": "^1.3.0",
"shortid": "^2.2.15",
"svelte-spa-router": "^3.0.5"
diff --git a/packages/client/src/stores/dataSource.js b/packages/client/src/stores/dataSource.js
index c60039aa8a..247ba18835 100644
--- a/packages/client/src/stores/dataSource.js
+++ b/packages/client/src/stores/dataSource.js
@@ -59,11 +59,10 @@ export const createDataSourceStore = () => {
// Emit this as a window event, so parent screens which are iframing us in
// can also invalidate the same datasource
- window.dispatchEvent(
- new CustomEvent("invalidate-datasource", {
- detail: { dataSourceId },
- })
- )
+ window.parent.postMessage({
+ type: "close-screen-modal",
+ detail: { dataSourceId },
+ })
let invalidations = [dataSourceId]
diff --git a/packages/client/src/stores/notification.js b/packages/client/src/stores/notification.js
index 97193b2092..64178328c0 100644
--- a/packages/client/src/stores/notification.js
+++ b/packages/client/src/stores/notification.js
@@ -34,11 +34,6 @@ const createNotificationStore = () => {
icon,
},
})
- // window.dispatchEvent(
- // new CustomEvent("notification", {
- // detail: { message, type, icon },
- // })
- // )
return
}
diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js
index 11aa033c1d..1fb2284375 100644
--- a/packages/client/src/utils/buttonActions.js
+++ b/packages/client/src/utils/buttonActions.js
@@ -120,7 +120,7 @@ const changeFormStepHandler = async (action, context) => {
const closeScreenModalHandler = () => {
// Emit this as a window event, so parent screens which are iframing us in
// can close the modal
- window.dispatchEvent(new Event("close-screen-modal"))
+ window.parent.postMessage({ type: "close-screen-modal" })
}
const updateStateHandler = action => {
diff --git a/packages/server/package.json b/packages/server/package.json
index cee6a2a2e8..27c274f2d4 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -1,7 +1,7 @@
{
"name": "@budibase/server",
"email": "hi@budibase.com",
- "version": "0.9.176-alpha.3",
+ "version": "0.9.180-alpha.0",
"description": "Budibase Web Server",
"main": "src/index.js",
"repository": {
@@ -68,9 +68,9 @@
"author": "Budibase",
"license": "AGPL-3.0-or-later",
"dependencies": {
- "@budibase/auth": "^0.9.176-alpha.3",
- "@budibase/client": "^0.9.176-alpha.3",
- "@budibase/string-templates": "^0.9.176-alpha.3",
+ "@budibase/auth": "^0.9.180-alpha.0",
+ "@budibase/client": "^0.9.180-alpha.0",
+ "@budibase/string-templates": "^0.9.180-alpha.0",
"@elastic/elasticsearch": "7.10.0",
"@koa/router": "8.0.0",
"@sendgrid/mail": "7.1.1",
diff --git a/packages/server/src/api/controllers/query.js b/packages/server/src/api/controllers/query.js
index 5f9fadf99b..4383ff2910 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 c810f85004..15ee748d8d 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 34299d23b6..f0637c3351 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 4bef91a153..f2d1bf5699 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 9c6ece52d6..cf234518d9 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 cb69d3c38d..7b128a6d14 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 bd064f7e66..d91311e165 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 01b9dcc248..5682a860b9 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/usageQuota.js b/packages/server/src/middleware/usageQuota.js
index c62f0078cd..2b189b8660 100644
--- a/packages/server/src/middleware/usageQuota.js
+++ b/packages/server/src/middleware/usageQuota.js
@@ -2,6 +2,10 @@ const CouchDB = require("../db")
const usageQuota = require("../utilities/usageQuota")
const env = require("../environment")
const { getTenantId } = require("@budibase/auth/tenancy")
+const {
+ isExternalTable,
+ isRowId: isExternalRowId,
+} = require("../integrations/utils")
// tenants without limits
const EXCLUDED_TENANTS = ["bb", "default", "bbtest", "bbstaging"]
@@ -46,14 +50,24 @@ module.exports = async (ctx, next) => {
}
// post request could be a save of a pre-existing entry
if (ctx.request.body && ctx.request.body._id && ctx.request.body._rev) {
+ const usageId = ctx.request.body._id
try {
if (ctx.appId) {
const db = new CouchDB(ctx.appId)
- await db.get(ctx.request.body._id)
+ await db.get(usageId)
}
return next()
} catch (err) {
- ctx.throw(404, `${ctx.request.body._id} does not exist`)
+ if (
+ isExternalTable(usageId) ||
+ (ctx.request.body.tableId &&
+ isExternalTable(ctx.request.body.tableId)) ||
+ isExternalRowId(usageId)
+ ) {
+ return next()
+ } else {
+ ctx.throw(404, `${usageId} does not exist`)
+ }
}
}
diff --git a/packages/server/src/middleware/utils.js b/packages/server/src/middleware/utils.js
new file mode 100644
index 0000000000..b1eea8cd66
--- /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)
+}
diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json
index 3a7268920f..187f720407 100644
--- a/packages/string-templates/package.json
+++ b/packages/string-templates/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/string-templates",
- "version": "0.9.176-alpha.3",
+ "version": "0.9.180-alpha.0",
"description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs",
"module": "dist/bundle.mjs",
diff --git a/packages/worker/package.json b/packages/worker/package.json
index 8d1c7141b4..84650b7713 100644
--- a/packages/worker/package.json
+++ b/packages/worker/package.json
@@ -1,7 +1,7 @@
{
"name": "@budibase/worker",
"email": "hi@budibase.com",
- "version": "0.9.176-alpha.3",
+ "version": "0.9.180-alpha.0",
"description": "Budibase background service",
"main": "src/index.js",
"repository": {
@@ -29,8 +29,8 @@
"author": "Budibase",
"license": "AGPL-3.0-or-later",
"dependencies": {
- "@budibase/auth": "^0.9.176-alpha.3",
- "@budibase/string-templates": "^0.9.176-alpha.3",
+ "@budibase/auth": "^0.9.180-alpha.0",
+ "@budibase/string-templates": "^0.9.180-alpha.0",
"@koa/router": "^8.0.0",
"@sentry/node": "^6.0.0",
"@techpass/passport-openidconnect": "^0.3.0",
diff --git a/packages/worker/src/api/controllers/global/auth.js b/packages/worker/src/api/controllers/global/auth.js
index 50b4ec969b..e111619041 100644
--- a/packages/worker/src/api/controllers/global/auth.js
+++ b/packages/worker/src/api/controllers/global/auth.js
@@ -77,6 +77,17 @@ exports.authenticate = async (ctx, next) => {
})(ctx, next)
}
+exports.setInitInfo = ctx => {
+ const initInfo = ctx.request.body
+ setCookie(ctx, initInfo, Cookies.Init)
+ ctx.status = 200
+}
+
+exports.getInitInfo = ctx => {
+ const initInfo = getCookie(ctx, Cookies.Init)
+ ctx.body = initInfo
+}
+
/**
* Reset the user password, used as part of a forgotten password flow.
*/
diff --git a/packages/worker/src/api/routes/global/auth.js b/packages/worker/src/api/routes/global/auth.js
index 9cd77ce153..7baad60ecd 100644
--- a/packages/worker/src/api/routes/global/auth.js
+++ b/packages/worker/src/api/routes/global/auth.js
@@ -56,6 +56,8 @@ router
authController.resetUpdate
)
.post("/api/global/auth/logout", authController.logout)
+ .post("/api/global/auth/init", authController.setInitInfo)
+ .get("/api/global/auth/init", authController.getInitInfo)
.get(
"/api/global/auth/:tenantId/google",
updateTenant,