Merge branch 'develop' of github.com:Budibase/budibase into develop

This commit is contained in:
Martin McKeaveney 2022-01-19 16:19:42 +01:00
commit 39a5f297d2
22 changed files with 171 additions and 37 deletions

View File

@ -1,5 +1,5 @@
{ {
"version": "1.0.44-alpha.1", "version": "1.0.44-alpha.6",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/backend-core", "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", "description": "Budibase backend core libraries used in server and worker",
"main": "src/index.js", "main": "src/index.js",
"author": "Budibase", "author": "Budibase",

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/bbui", "name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.", "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", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"module": "dist/bbui.es.js", "module": "dist/bbui.es.js",

View File

@ -41,4 +41,4 @@ context("Add Multi-Option Datatype", () => {
.contains("(5)") .contains("(5)")
}) })
}) })
})

View File

@ -36,7 +36,6 @@ Cypress.Commands.add("createApp", name => {
cy.visit(`localhost:${Cypress.env("PORT")}/builder`) cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
cy.wait(500) cy.wait(500)
cy.contains(/Start from scratch/).dblclick() cy.contains(/Start from scratch/).dblclick()
cy.wait(2000)
cy.get(".spectrum-Modal").within(() => { cy.get(".spectrum-Modal").within(() => {
cy.get("input").eq(0).type(name).should("have.value", name).blur() cy.get("input").eq(0).type(name).should("have.value", name).blur()
cy.get(".spectrum-ButtonGroup").contains("Create app").click() cy.get(".spectrum-ButtonGroup").contains("Create app").click()

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "1.0.44-alpha.1", "version": "1.0.44-alpha.6",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -65,10 +65,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.0.44-alpha.1", "@budibase/bbui": "^1.0.44-alpha.6",
"@budibase/client": "^1.0.44-alpha.1", "@budibase/client": "^1.0.44-alpha.6",
"@budibase/colorpicker": "1.1.2", "@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", "@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1", "@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1", "@spectrum-css/vars": "^3.0.1",

View File

@ -6,9 +6,12 @@
export let datasource export let datasource
let name = "" let name = ""
let submitted = false
$: valid = name && name.length > 0 && !datasource?.entities[name] $: valid = name && name.length > 0 && !datasource?.entities[name]
$: error = $: 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) { function buildDefaultTable(tableName, datasourceId) {
return { return {
@ -26,6 +29,7 @@
} }
async function saveTable() { async function saveTable() {
submitted = true
const table = await tables.save(buildDefaultTable(name, datasource._id)) const table = await tables.save(buildDefaultTable(name, datasource._id))
await datasources.fetch() await datasources.fetch()
$goto(`../../table/${table._id}`) $goto(`../../table/${table._id}`)

View File

@ -1,13 +1,38 @@
<script> <script>
import { Body } from "@budibase/bbui" import { Label, Body, Layout } from "@budibase/bbui"
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
export let parameters
export let bindings = []
</script> </script>
<div class="root"> <div class="root">
<Body size="S">This action doesn't require any additional settings.</Body> <Layout noPadding gap="M">
<Body size="S">
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.
</Body>
<div class="content">
<Label small>Redirect URL</Label>
<DrawerBindableInput
title="Return URL"
value={parameters.redirectUrl}
on:change={value => (parameters.redirectUrl = value.detail)}
{bindings}
/>
</div>
</Layout>
</div> </div>
<style> <style>
.root { .root {
max-width: 400px;
margin: 0 auto; margin: 0 auto;
} }
.content {
display: grid;
align-items: center;
gap: var(--spacing-m);
grid-template-columns: auto 1fr;
}
</style> </style>

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "1.0.44-alpha.1", "version": "1.0.44-alpha.6",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js", "main": "src/index.js",
"bin": { "bin": {

View File

@ -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", "type": "text",
"label": "Default value", "label": "Default value",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "1.0.44-alpha.1", "version": "1.0.44-alpha.6",
"license": "MPL-2.0", "license": "MPL-2.0",
"module": "dist/budibase-client.js", "module": "dist/budibase-client.js",
"main": "dist/budibase-client.js", "main": "dist/budibase-client.js",
@ -19,9 +19,9 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.0.44-alpha.1", "@budibase/bbui": "^1.0.44-alpha.6",
"@budibase/standard-components": "^0.9.139", "@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", "regexparam": "^1.3.0",
"shortid": "^2.2.15", "shortid": "^2.2.15",
"svelte-spa-router": "^3.0.5" "svelte-spa-router": "^3.0.5"

View File

@ -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 * Fetches the currently logged in user object
*/ */

View File

@ -15,6 +15,7 @@
export let valueColumn export let valueColumn
export let customOptions export let customOptions
export let autocomplete = false export let autocomplete = false
export let direction = "vertical"
let fieldState let fieldState
let fieldApi let fieldApi
@ -64,6 +65,7 @@
disabled={fieldState.disabled} disabled={fieldState.disabled}
error={fieldState.error} error={fieldState.error}
{options} {options}
{direction}
on:change={e => fieldApi.setValue(e.detail)} on:change={e => fieldApi.setValue(e.detail)}
getOptionLabel={flatOptions ? x => x : x => x.label} getOptionLabel={flatOptions ? x => x : x => x.label}
getOptionValue={flatOptions ? x => x : x => x.value} getOptionValue={flatOptions ? x => x : x => x.value}

View File

@ -11,8 +11,14 @@ const createAuthStore = () => {
} }
const logOut = async () => { 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.document.cookie = `budibase:auth=; budibase:currentapp=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;`
window.location = "/builder/auth/login"
} }
return { return {

View File

@ -18,8 +18,8 @@ const createRouteStore = () => {
const fetchRoutes = async () => { const fetchRoutes = async () => {
const routeConfig = await API.fetchRoutes() const routeConfig = await API.fetchRoutes()
let routes = [] let routes = []
Object.values(routeConfig.routes).forEach(route => { Object.values(routeConfig.routes || {}).forEach(route => {
Object.entries(route.subpaths).forEach(([path, config]) => { Object.entries(route.subpaths || {}).forEach(([path, config]) => {
routes.push({ routes.push({
path, path,
screenId: config.screenId, screenId: config.screenId,
@ -83,12 +83,23 @@ const createRouteStore = () => {
const setRouterLoaded = () => { const setRouterLoaded = () => {
store.update(state => ({ ...state, routerLoaded: true })) 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 { return {
subscribe: store.subscribe, subscribe: store.subscribe,
actions: { actions: {
fetchRoutes, fetchRoutes,
navigate, navigate,
createFullURL,
setRouteParams, setRouteParams,
setQueryParams, setQueryParams,
setActiveRoute, setActiveRoute,

View File

@ -112,8 +112,20 @@ const refreshDataProviderHandler = async (action, context) => {
) )
} }
const logoutHandler = async () => { const logoutHandler = async action => {
await authStore.actions.logOut() 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) => { const clearFormHandler = async (action, context) => {

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "1.0.44-alpha.1", "version": "1.0.44-alpha.6",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -70,9 +70,9 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@apidevtools/swagger-parser": "^10.0.3", "@apidevtools/swagger-parser": "^10.0.3",
"@budibase/backend-core": "^1.0.44-alpha.1", "@budibase/backend-core": "^1.0.44-alpha.6",
"@budibase/client": "^1.0.44-alpha.1", "@budibase/client": "^1.0.44-alpha.6",
"@budibase/string-templates": "^1.0.44-alpha.1", "@budibase/string-templates": "^1.0.44-alpha.6",
"@bull-board/api": "^3.7.0", "@bull-board/api": "^3.7.0",
"@bull-board/koa": "^3.7.0", "@bull-board/koa": "^3.7.0",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",

View File

@ -143,11 +143,46 @@ export function isIsoDateString(str: string) {
return d.toISOString() === str 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( function copyExistingPropsOver(
tableName: string, tableName: string,
table: Table, table: Table,
entities: { [key: string]: any } entities: { [key: string]: any },
tableIds: [string]
) { ) {
if (entities && entities[tableName]) { if (entities && entities[tableName]) {
if (entities[tableName].primaryDisplay) { if (entities[tableName].primaryDisplay) {
@ -158,11 +193,10 @@ function copyExistingPropsOver(
if (!existingTableSchema.hasOwnProperty(key)) { if (!existingTableSchema.hasOwnProperty(key)) {
continue continue
} }
const column = existingTableSchema[key]
if ( if (
existingTableSchema[key].type === FieldTypes.LINK || shouldCopyRelationship(column, tableIds) ||
existingTableSchema[key].type === FieldTypes.OPTIONS || shouldCopySpecialColumn(column, table.schema[key])
((!table.schema[key] || table.schema[key].type === FieldTypes.NUMBER) &&
existingTableSchema[key].type === FieldTypes.BOOLEAN)
) { ) {
table.schema[key] = existingTableSchema[key] table.schema[key] = existingTableSchema[key]
} }
@ -171,6 +205,13 @@ function copyExistingPropsOver(
return table 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( export function finaliseExternalTables(
tables: { [key: string]: any }, tables: { [key: string]: any },
entities: { [key: string]: any } entities: { [key: string]: any }
@ -178,6 +219,8 @@ export function finaliseExternalTables(
const invalidColumns = Object.values(InvalidColumns) const invalidColumns = Object.values(InvalidColumns)
let finalTables: { [key: string]: any } = {} let finalTables: { [key: string]: any } = {}
const errors: { [key: string]: string } = {} 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)) { for (let [name, table] of Object.entries(tables)) {
const schemaFields = Object.keys(table.schema) const schemaFields = Object.keys(table.schema)
// make sure every table has a key // make sure every table has a key
@ -189,7 +232,7 @@ export function finaliseExternalTables(
continue continue
} }
// make sure all previous props have been added back // 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 // sort the tables by name
finalTables = Object.entries(finalTables) finalTables = Object.entries(finalTables)

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "1.0.44-alpha.1", "version": "1.0.44-alpha.6",
"description": "Handlebars wrapper for Budibase templating.", "description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs", "main": "src/index.cjs",
"module": "dist/bundle.mjs", "module": "dist/bundle.mjs",

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "1.0.44-alpha.1", "version": "1.0.44-alpha.6",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.js", "main": "src/index.js",
"repository": { "repository": {
@ -29,8 +29,8 @@
"author": "Budibase", "author": "Budibase",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@budibase/backend-core": "^1.0.44-alpha.1", "@budibase/backend-core": "^1.0.44-alpha.6",
"@budibase/string-templates": "^1.0.44-alpha.1", "@budibase/string-templates": "^1.0.44-alpha.6",
"@koa/router": "^8.0.0", "@koa/router": "^8.0.0",
"@sentry/node": "^6.0.0", "@sentry/node": "^6.0.0",
"@techpass/passport-openidconnect": "^0.3.0", "@techpass/passport-openidconnect": "^0.3.0",

View File

@ -141,7 +141,9 @@ exports.resetUpdate = async ctx => {
} }
exports.logout = 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." } ctx.body = { message: "User logged out." }
} }

View File

@ -6,6 +6,7 @@ exports.fetch = async ctx => {
cloud: !env.SELF_HOSTED, cloud: !env.SELF_HOSTED,
accountPortalUrl: env.ACCOUNT_PORTAL_URL, accountPortalUrl: env.ACCOUNT_PORTAL_URL,
disableAccountPortal: env.DISABLE_ACCOUNT_PORTAL, 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(),
} }
} }