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. + +
+ + (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(), } }