diff --git a/lerna.json b/lerna.json
index 4240dd788a..18235a5914 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "1.0.44-alpha.1",
+ "version": "1.0.44-alpha.6",
"npmClient": "yarn",
"packages": [
"packages/*"
diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json
index f01880d87f..14fcbea357 100644
--- a/packages/backend-core/package.json
+++ b/packages/backend-core/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/backend-core",
- "version": "1.0.44-alpha.1",
+ "version": "1.0.44-alpha.6",
"description": "Budibase backend core libraries used in server and worker",
"main": "src/index.js",
"author": "Budibase",
diff --git a/packages/bbui/package.json b/packages/bbui/package.json
index 9515461230..83af25fba5 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": "1.0.44-alpha.1",
+ "version": "1.0.44-alpha.6",
"license": "MPL-2.0",
"svelte": "src/index.js",
"module": "dist/bbui.es.js",
diff --git a/packages/builder/cypress/integration/addMultiOptionDatatype.spec.js b/packages/builder/cypress/integration/addMultiOptionDatatype.spec.js
index 63611d6c02..b3e0f413f8 100644
--- a/packages/builder/cypress/integration/addMultiOptionDatatype.spec.js
+++ b/packages/builder/cypress/integration/addMultiOptionDatatype.spec.js
@@ -41,4 +41,4 @@ context("Add Multi-Option Datatype", () => {
.contains("(5)")
})
})
-
+})
diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js
index 8203ca84f9..e67057344a 100644
--- a/packages/builder/cypress/support/commands.js
+++ b/packages/builder/cypress/support/commands.js
@@ -36,7 +36,6 @@ Cypress.Commands.add("createApp", name => {
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
cy.wait(500)
cy.contains(/Start from scratch/).dblclick()
- cy.wait(2000)
cy.get(".spectrum-Modal").within(() => {
cy.get("input").eq(0).type(name).should("have.value", name).blur()
cy.get(".spectrum-ButtonGroup").contains("Create app").click()
diff --git a/packages/builder/package.json b/packages/builder/package.json
index cb46a2a961..fcd4dd17e1 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
- "version": "1.0.44-alpha.1",
+ "version": "1.0.44-alpha.6",
"license": "GPL-3.0",
"private": true,
"scripts": {
@@ -65,10 +65,10 @@
}
},
"dependencies": {
- "@budibase/bbui": "^1.0.44-alpha.1",
- "@budibase/client": "^1.0.44-alpha.1",
+ "@budibase/bbui": "^1.0.44-alpha.6",
+ "@budibase/client": "^1.0.44-alpha.6",
"@budibase/colorpicker": "1.1.2",
- "@budibase/string-templates": "^1.0.44-alpha.1",
+ "@budibase/string-templates": "^1.0.44-alpha.6",
"@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/CreateExternalTableModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/CreateExternalTableModal.svelte
index 1d9e246d20..52402a0396 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/CreateExternalTableModal.svelte
+++ b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/CreateExternalTableModal.svelte
@@ -6,9 +6,12 @@
export let datasource
let name = ""
+ let submitted = false
$: valid = name && name.length > 0 && !datasource?.entities[name]
$: error =
- name && datasource?.entities[name] ? "Table name already in use." : null
+ !submitted && name && datasource?.entities[name]
+ ? "Table name already in use."
+ : null
function buildDefaultTable(tableName, datasourceId) {
return {
@@ -26,6 +29,7 @@
}
async function saveTable() {
+ submitted = true
const table = await tables.save(buildDefaultTable(name, datasource._id))
await datasources.fetch()
$goto(`../../table/${table._id}`)
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ButtonActionEditor/actions/LogOut.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ButtonActionEditor/actions/LogOut.svelte
index 3434d63480..f0606d86b3 100644
--- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ButtonActionEditor/actions/LogOut.svelte
+++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ButtonActionEditor/actions/LogOut.svelte
@@ -1,13 +1,38 @@
- This action doesn't require any additional settings.
+
+
+ Please enter the URL you would like to be redirected to after logging out.
+ If you don't enter a value, you'll be redirected to the login screen.
+
+
+ Redirect URL
+ (parameters.redirectUrl = value.detail)}
+ {bindings}
+ />
+
+
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 9c6acedfd0..335ea917c4 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/cli",
- "version": "1.0.44-alpha.1",
+ "version": "1.0.44-alpha.6",
"description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js",
"bin": {
diff --git a/packages/client/manifest.json b/packages/client/manifest.json
index e618ca1c11..3f8d4c0e3b 100644
--- a/packages/client/manifest.json
+++ b/packages/client/manifest.json
@@ -2065,6 +2065,26 @@
}
]
},
+ {
+ "type": "select",
+ "label": "Direction",
+ "key": "direction",
+ "defaultValue": "vertical",
+ "options": [
+ {
+ "label": "Horizontal",
+ "value": "horizontal"
+ },
+ {
+ "label": "Vertical",
+ "value": "vertical"
+ }
+ ],
+ "dependsOn": {
+ "setting": "optionsType",
+ "value": "radio"
+ }
+ },
{
"type": "text",
"label": "Default value",
diff --git a/packages/client/package.json b/packages/client/package.json
index 94eb8e9fc1..a5e3264aca 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/client",
- "version": "1.0.44-alpha.1",
+ "version": "1.0.44-alpha.6",
"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": "^1.0.44-alpha.1",
+ "@budibase/bbui": "^1.0.44-alpha.6",
"@budibase/standard-components": "^0.9.139",
- "@budibase/string-templates": "^1.0.44-alpha.1",
+ "@budibase/string-templates": "^1.0.44-alpha.6",
"regexparam": "^1.3.0",
"shortid": "^2.2.15",
"svelte-spa-router": "^3.0.5"
diff --git a/packages/client/src/api/auth.js b/packages/client/src/api/auth.js
index 68ca5dbc80..9ac09f5571 100644
--- a/packages/client/src/api/auth.js
+++ b/packages/client/src/api/auth.js
@@ -18,6 +18,15 @@ export const logIn = async ({ email, password }) => {
})
}
+/**
+ * Logs the user out and invaidates their session.
+ */
+export const logOut = async () => {
+ return await API.post({
+ url: "/api/global/auth/logout",
+ })
+}
+
/**
* Fetches the currently logged in user object
*/
diff --git a/packages/client/src/components/app/forms/OptionsField.svelte b/packages/client/src/components/app/forms/OptionsField.svelte
index 4ad8f4611e..8140600e7e 100644
--- a/packages/client/src/components/app/forms/OptionsField.svelte
+++ b/packages/client/src/components/app/forms/OptionsField.svelte
@@ -15,6 +15,7 @@
export let valueColumn
export let customOptions
export let autocomplete = false
+ export let direction = "vertical"
let fieldState
let fieldApi
@@ -64,6 +65,7 @@
disabled={fieldState.disabled}
error={fieldState.error}
{options}
+ {direction}
on:change={e => fieldApi.setValue(e.detail)}
getOptionLabel={flatOptions ? x => x : x => x.label}
getOptionValue={flatOptions ? x => x : x => x.value}
diff --git a/packages/client/src/stores/auth.js b/packages/client/src/stores/auth.js
index 1fa4ae17b0..9cd2613e24 100644
--- a/packages/client/src/stores/auth.js
+++ b/packages/client/src/stores/auth.js
@@ -11,8 +11,14 @@ const createAuthStore = () => {
}
const logOut = async () => {
+ try {
+ await API.logOut()
+ } catch (error) {
+ // Do nothing
+ }
+
+ // Manually destroy cookie to be sure
window.document.cookie = `budibase:auth=; budibase:currentapp=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;`
- window.location = "/builder/auth/login"
}
return {
diff --git a/packages/client/src/stores/routes.js b/packages/client/src/stores/routes.js
index 1d5dca1645..d50677493b 100644
--- a/packages/client/src/stores/routes.js
+++ b/packages/client/src/stores/routes.js
@@ -18,8 +18,8 @@ const createRouteStore = () => {
const fetchRoutes = async () => {
const routeConfig = await API.fetchRoutes()
let routes = []
- Object.values(routeConfig.routes).forEach(route => {
- Object.entries(route.subpaths).forEach(([path, config]) => {
+ Object.values(routeConfig.routes || {}).forEach(route => {
+ Object.entries(route.subpaths || {}).forEach(([path, config]) => {
routes.push({
path,
screenId: config.screenId,
@@ -83,12 +83,23 @@ const createRouteStore = () => {
const setRouterLoaded = () => {
store.update(state => ({ ...state, routerLoaded: true }))
}
+ const createFullURL = relativeURL => {
+ if (!relativeURL?.startsWith("/")) {
+ return relativeURL
+ }
+ if (!window.location.href.includes("#")) {
+ return `${window.location.href}#${relativeURL}`
+ }
+ const base = window.location.href.split("#")[0]
+ return `${base}#${relativeURL}`
+ }
return {
subscribe: store.subscribe,
actions: {
fetchRoutes,
navigate,
+ createFullURL,
setRouteParams,
setQueryParams,
setActiveRoute,
diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js
index 6b4dd4235a..2ef324d23c 100644
--- a/packages/client/src/utils/buttonActions.js
+++ b/packages/client/src/utils/buttonActions.js
@@ -112,8 +112,20 @@ const refreshDataProviderHandler = async (action, context) => {
)
}
-const logoutHandler = async () => {
+const logoutHandler = async action => {
await authStore.actions.logOut()
+ let redirectUrl = "/builder/auth/login"
+ let internal = false
+ if (action.parameters.redirectUrl) {
+ internal = action.parameters.redirectUrl?.startsWith("/")
+ redirectUrl = routeStore.actions.createFullURL(
+ action.parameters.redirectUrl
+ )
+ }
+ window.location.href = redirectUrl
+ if (internal) {
+ window.location.reload()
+ }
}
const clearFormHandler = async (action, context) => {
diff --git a/packages/server/package.json b/packages/server/package.json
index e05326f559..a2e5aa5bb9 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -1,7 +1,7 @@
{
"name": "@budibase/server",
"email": "hi@budibase.com",
- "version": "1.0.44-alpha.1",
+ "version": "1.0.44-alpha.6",
"description": "Budibase Web Server",
"main": "src/index.ts",
"repository": {
@@ -70,9 +70,9 @@
"license": "GPL-3.0",
"dependencies": {
"@apidevtools/swagger-parser": "^10.0.3",
- "@budibase/backend-core": "^1.0.44-alpha.1",
- "@budibase/client": "^1.0.44-alpha.1",
- "@budibase/string-templates": "^1.0.44-alpha.1",
+ "@budibase/backend-core": "^1.0.44-alpha.6",
+ "@budibase/client": "^1.0.44-alpha.6",
+ "@budibase/string-templates": "^1.0.44-alpha.6",
"@bull-board/api": "^3.7.0",
"@bull-board/koa": "^3.7.0",
"@elastic/elasticsearch": "7.10.0",
diff --git a/packages/server/src/integrations/utils.ts b/packages/server/src/integrations/utils.ts
index 46bec0e33e..b8c96efffe 100644
--- a/packages/server/src/integrations/utils.ts
+++ b/packages/server/src/integrations/utils.ts
@@ -143,11 +143,46 @@ export function isIsoDateString(str: string) {
return d.toISOString() === str
}
-// add the existing relationships from the entities if they exist, to prevent them from being overridden
+/**
+ * This function will determine whether a column is a relationship and whether it
+ * is currently valid. The reason for the validity check is that tables can be deleted
+ * outside of Budibase control and if this is the case it will break Budibase relationships.
+ * The tableIds is a list passed down from the main finalise tables function, which is
+ * based on the tables that have just been fetched. This will only really be used on subsequent
+ * fetches to the first one - if the user is periodically refreshing Budibase knowledge of tables.
+ * @param column The column to check, to see if it is a valid relationship.
+ * @param tableIds The IDs of the tables which currently exist.
+ */
+function shouldCopyRelationship(column: { type: string, tableId?: string }, tableIds: [string]) {
+ return column.type === FieldTypes.LINK && column.tableId && tableIds.includes(column.tableId)
+}
+
+/**
+ * Similar function to the shouldCopyRelationship function, but instead this looks for options and boolean
+ * types. It is possible to switch a string -> options and a number -> boolean (and vice versus) need to make
+ * sure that these get copied over when tables are fetched. Also checks whether they are still valid, if a
+ * column has changed type in the external database then copying it over may not be possible.
+ * @param column The column to check for options or boolean type.
+ * @param fetchedColumn The fetched column to check for the type in the external database.
+ */
+function shouldCopySpecialColumn(column: { type: string }, fetchedColumn: { type: string } | undefined) {
+ return column.type === FieldTypes.OPTIONS ||
+ ((!fetchedColumn || fetchedColumn.type === FieldTypes.NUMBER) && column.type === FieldTypes.BOOLEAN)
+}
+
+/**
+ * Looks for columns which need to be copied over into the new table definitions, like relationships
+ * and options types.
+ * @param tableName The name of the table which is being checked.
+ * @param table The specific table which is being checked.
+ * @param entities All the tables that existed before - the old table definitions.
+ * @param tableIds The IDs of the tables which exist now, to check if anything has been removed.
+ */
function copyExistingPropsOver(
tableName: string,
table: Table,
- entities: { [key: string]: any }
+ entities: { [key: string]: any },
+ tableIds: [string]
) {
if (entities && entities[tableName]) {
if (entities[tableName].primaryDisplay) {
@@ -158,11 +193,10 @@ function copyExistingPropsOver(
if (!existingTableSchema.hasOwnProperty(key)) {
continue
}
+ const column = existingTableSchema[key]
if (
- existingTableSchema[key].type === FieldTypes.LINK ||
- existingTableSchema[key].type === FieldTypes.OPTIONS ||
- ((!table.schema[key] || table.schema[key].type === FieldTypes.NUMBER) &&
- existingTableSchema[key].type === FieldTypes.BOOLEAN)
+ shouldCopyRelationship(column, tableIds) ||
+ shouldCopySpecialColumn(column, table.schema[key])
) {
table.schema[key] = existingTableSchema[key]
}
@@ -171,6 +205,13 @@ function copyExistingPropsOver(
return table
}
+/**
+ * Look through the final table definitions to see if anything needs to be
+ * copied over from the old and if any errors have occurred mark them so
+ * that the user can be made aware.
+ * @param tables The list of tables that have been retrieved from the external database.
+ * @param entities The old list of tables, if there was any to look for definitions in.
+ */
export function finaliseExternalTables(
tables: { [key: string]: any },
entities: { [key: string]: any }
@@ -178,6 +219,8 @@ export function finaliseExternalTables(
const invalidColumns = Object.values(InvalidColumns)
let finalTables: { [key: string]: any } = {}
const errors: { [key: string]: string } = {}
+ // @ts-ignore
+ const tableIds: [string] = Object.values(tables).map(table => table._id)
for (let [name, table] of Object.entries(tables)) {
const schemaFields = Object.keys(table.schema)
// make sure every table has a key
@@ -189,7 +232,7 @@ export function finaliseExternalTables(
continue
}
// make sure all previous props have been added back
- finalTables[name] = copyExistingPropsOver(name, table, entities)
+ finalTables[name] = copyExistingPropsOver(name, table, entities, tableIds)
}
// sort the tables by name
finalTables = Object.entries(finalTables)
diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json
index fa8fd868e5..0a745d6129 100644
--- a/packages/string-templates/package.json
+++ b/packages/string-templates/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/string-templates",
- "version": "1.0.44-alpha.1",
+ "version": "1.0.44-alpha.6",
"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 a77c80ebc8..906eb7b204 100644
--- a/packages/worker/package.json
+++ b/packages/worker/package.json
@@ -1,7 +1,7 @@
{
"name": "@budibase/worker",
"email": "hi@budibase.com",
- "version": "1.0.44-alpha.1",
+ "version": "1.0.44-alpha.6",
"description": "Budibase background service",
"main": "src/index.js",
"repository": {
@@ -29,8 +29,8 @@
"author": "Budibase",
"license": "GPL-3.0",
"dependencies": {
- "@budibase/backend-core": "^1.0.44-alpha.1",
- "@budibase/string-templates": "^1.0.44-alpha.1",
+ "@budibase/backend-core": "^1.0.44-alpha.6",
+ "@budibase/string-templates": "^1.0.44-alpha.6",
"@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 2ba12194ca..44ee99aee7 100644
--- a/packages/worker/src/api/controllers/global/auth.js
+++ b/packages/worker/src/api/controllers/global/auth.js
@@ -141,7 +141,9 @@ exports.resetUpdate = async ctx => {
}
exports.logout = async ctx => {
- await platformLogout({ ctx, userId: ctx.user._id })
+ if (ctx.user && ctx.user._id) {
+ await platformLogout({ ctx, userId: ctx.user._id })
+ }
ctx.body = { message: "User logged out." }
}
diff --git a/packages/worker/src/api/controllers/system/environment.js b/packages/worker/src/api/controllers/system/environment.js
index b897fbd943..4edf1ff8d3 100644
--- a/packages/worker/src/api/controllers/system/environment.js
+++ b/packages/worker/src/api/controllers/system/environment.js
@@ -6,6 +6,7 @@ exports.fetch = async ctx => {
cloud: !env.SELF_HOSTED,
accountPortalUrl: env.ACCOUNT_PORTAL_URL,
disableAccountPortal: env.DISABLE_ACCOUNT_PORTAL,
- isDev: env.isDev(),
+ // in test need to pretend its in production for the UI (Cypress)
+ isDev: env.isDev() && !env.isTest(),
}
}