Merge branch 'develop' of github.com:Budibase/budibase into feature/json-backend

This commit is contained in:
mike12345567 2021-11-24 15:53:53 +00:00
commit 4903b06e4c
17 changed files with 230 additions and 869 deletions

View File

@ -1,5 +1,5 @@
{ {
"version": "0.9.185-alpha.19", "version": "0.9.185-alpha.20",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/auth", "name": "@budibase/auth",
"version": "0.9.185-alpha.19", "version": "0.9.185-alpha.20",
"description": "Authentication middlewares for budibase builder and apps", "description": "Authentication middlewares for budibase builder and apps",
"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": "0.9.185-alpha.19", "version": "0.9.185-alpha.20",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"module": "dist/bbui.es.js", "module": "dist/bbui.es.js",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "0.9.185-alpha.19", "version": "0.9.185-alpha.20",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -65,10 +65,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^0.9.185-alpha.19", "@budibase/bbui": "^0.9.185-alpha.20",
"@budibase/client": "^0.9.185-alpha.19", "@budibase/client": "^0.9.185-alpha.20",
"@budibase/colorpicker": "1.1.2", "@budibase/colorpicker": "1.1.2",
"@budibase/string-templates": "^0.9.185-alpha.19", "@budibase/string-templates": "^0.9.185-alpha.20",
"@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

@ -10,6 +10,7 @@
ModalContent, ModalContent,
Context, Context,
Modal, Modal,
notifications,
} from "@budibase/bbui" } from "@budibase/bbui"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
@ -26,7 +27,6 @@
SWITCHABLE_TYPES, SWITCHABLE_TYPES,
} from "constants/backend" } from "constants/backend"
import { getAutoColumnInformation, buildAutoColumn } from "builderStore/utils" import { getAutoColumnInformation, buildAutoColumn } from "builderStore/utils"
import { notifications } from "@budibase/bbui"
import ValuesList from "components/common/ValuesList.svelte" import ValuesList from "components/common/ValuesList.svelte"
import ConfirmDialog from "components/common/ConfirmDialog.svelte" import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import { truncate } from "lodash" import { truncate } from "lodash"
@ -76,13 +76,8 @@
$: invalid = $: invalid =
!field.name || !field.name ||
(field.type === LINK_TYPE && !field.tableId) || (field.type === LINK_TYPE && !field.tableId) ||
Object.keys($tables.draft?.schema ?? {}).some( Object.keys(errors).length !== 0
key => key !== originalName && key === field.name $: errors = checkErrors(field)
) ||
columnNameInvalid
$: columnNameInvalid = PROHIBITED_COLUMN_NAMES.some(
name => field.name === name
)
// used to select what different options can be displayed for column type // used to select what different options can be displayed for column type
$: canBeSearched = $: canBeSearched =
@ -111,13 +106,17 @@
if (field.type === AUTO_TYPE) { if (field.type === AUTO_TYPE) {
field = buildAutoColumn($tables.draft.name, field.name, field.subtype) field = buildAutoColumn($tables.draft.name, field.name, field.subtype)
} }
await tables.saveField({ try {
originalName, await tables.saveField({
field, originalName,
primaryDisplay, field,
indexes, primaryDisplay,
}) indexes,
dispatch("updatecolumns") })
dispatch("updatecolumns")
} catch (err) {
notifications.error(err)
}
} }
function deleteColumn() { function deleteColumn() {
@ -267,6 +266,31 @@
fieldToCheck.constraints.numericality = {} fieldToCheck.constraints.numericality = {}
} }
} }
function checkErrors(fieldInfo) {
function inUse(tbl, column, ogName = null) {
return Object.keys(tbl?.schema || {}).some(
key => key !== ogName && key === column
)
}
const newError = {}
if (PROHIBITED_COLUMN_NAMES.some(name => fieldInfo.name === name)) {
newError.name = `${PROHIBITED_COLUMN_NAMES.join(
", "
)} are not allowed as column names`
} else if (inUse($tables.draft, fieldInfo.name, originalName)) {
newError.name = `Column name already in use.`
}
if (fieldInfo.fieldName && fieldInfo.tableId) {
const relatedTable = $tables.list.find(
tbl => tbl._id === fieldInfo.tableId
)
if (inUse(relatedTable, fieldInfo.fieldName)) {
newError.relatedName = `Column name already in use in table ${relatedTable.name}`
}
}
return newError
}
</script> </script>
<ModalContent <ModalContent
@ -279,9 +303,7 @@
label="Name" label="Name"
bind:value={field.name} bind:value={field.name}
disabled={uneditable || (linkEditDisabled && field.type === LINK_TYPE)} disabled={uneditable || (linkEditDisabled && field.type === LINK_TYPE)}
error={columnNameInvalid error={errors?.name}
? `${PROHIBITED_COLUMN_NAMES.join(", ")} are not allowed as column names`
: ""}
/> />
<Select <Select
@ -390,6 +412,7 @@
disabled={linkEditDisabled} disabled={linkEditDisabled}
label={`Column name in other table`} label={`Column name in other table`}
bind:value={field.fieldName} bind:value={field.fieldName}
error={errors.relatedName}
/> />
{:else if field.type === FORMULA_TYPE} {:else if field.type === FORMULA_TYPE}
<ModalBindableInput <ModalBindableInput

View File

@ -71,6 +71,9 @@
if ($touched.toCol && !toRelate.name) { if ($touched.toCol && !toRelate.name) {
errors.toCol = colNotSet errors.toCol = colNotSet
} }
if ($touched.primary && !fromPrimary) {
errors.primary = "Please pick the primary key"
}
// currently don't support relationships back onto the table itself, needs to relate out // currently don't support relationships back onto the table itself, needs to relate out
const tableError = "From/to/through tables must be different" const tableError = "From/to/through tables must be different"
if (fromTable && (fromTable === toTable || fromTable === throughTable)) { if (fromTable && (fromTable === toTable || fromTable === throughTable)) {
@ -95,6 +98,16 @@
return errors return errors
} }
let fromPrimary
$: {
if (!fromPrimary && fromTable) {
fromPrimary = fromTable.primary[0]
}
}
$: isManyToMany =
fromRelationship?.relationshipType === RelationshipTypes.MANY_TO_MANY
$: isManyToOne =
fromRelationship?.relationshipType === RelationshipTypes.MANY_TO_ONE
$: tableOptions = plusTables.map(table => ({ $: tableOptions = plusTables.map(table => ({
label: table.name, label: table.name,
value: table._id, value: table._id,
@ -179,13 +192,13 @@
// foreignKey is what is linking out of the current table. // foreignKey is what is linking out of the current table.
relateFrom = { relateFrom = {
...relateFrom, ...relateFrom,
foreignKey: fromTable.primary[0], foreignKey: fromPrimary,
} }
relateTo = { relateTo = {
...relateTo, ...relateTo,
relationshipType: RelationshipTypes.ONE_TO_MANY, relationshipType: RelationshipTypes.ONE_TO_MANY,
foreignKey: relateFrom.fieldName, foreignKey: relateFrom.fieldName,
fieldName: fromTable.primary[0], fieldName: fromPrimary,
} }
} }
@ -264,6 +277,15 @@
bind:error={errors.from} bind:error={errors.from}
bind:value={toRelationship.tableId} bind:value={toRelationship.tableId}
/> />
{#if isManyToOne && fromTable}
<Select
label={`Primary Key (${fromTable?.name})`}
options={Object.keys(fromTable?.schema)}
on:change={() => ($touched.primary = true)}
bind:error={errors.primary}
bind:value={fromPrimary}
/>
{/if}
<Select <Select
label={"Select to table"} label={"Select to table"}
options={tableOptions} options={tableOptions}
@ -271,7 +293,7 @@
bind:error={errors.to} bind:error={errors.to}
bind:value={fromRelationship.tableId} bind:value={fromRelationship.tableId}
/> />
{#if fromRelationship?.relationshipType === RelationshipTypes.MANY_TO_MANY} {#if isManyToMany}
<Select <Select
label={"Through"} label={"Through"}
options={tableOptions} options={tableOptions}
@ -295,7 +317,7 @@
bind:value={fromRelationship.throughFrom} bind:value={fromRelationship.throughFrom}
/> />
{/if} {/if}
{:else if fromRelationship?.relationshipType && toTable} {:else if isManyToOne && toTable}
<Select <Select
label={`Foreign Key (${toTable?.name})`} label={`Foreign Key (${toTable?.name})`}
options={Object.keys(toTable?.schema).filter( options={Object.keys(toTable?.schema).filter(

View File

@ -1,13 +1,20 @@
<script> <script>
import { store } from "builderStore" import { store } from "builderStore"
import { tables } from "stores/backend" import { tables } from "stores/backend"
import { ModalContent, Body, Detail, Layout, Icon } from "@budibase/bbui" import {
ModalContent,
Body,
Detail,
Layout,
Icon,
ProgressCircle,
} from "@budibase/bbui"
import getTemplates from "builderStore/store/screenTemplates" import getTemplates from "builderStore/store/screenTemplates"
export let selectedScreens = [] export let selectedScreens = []
export let chooseModal export let chooseModal
export let save export let save
export let showProgressCircle = false
const blankScreen = "createFromScratch" const blankScreen = "createFromScratch"
$: blankSelected = selectedScreens?.length === 1 $: blankSelected = selectedScreens?.length === 1
@ -27,78 +34,108 @@
} }
</script> </script>
<ModalContent <div style="overflow-y: auto; max-height: 1000px">
title="Add screens" <ModalContent
confirmText="Add Screens" title="Add screens"
cancelText="Cancel" confirmText="Add Screens"
onConfirm={() => (autoSelected ? save() : chooseModal(1))} cancelText="Cancel"
disabled={!selectedScreens.length} onConfirm={() => (autoSelected ? save() : chooseModal(1))}
size="L" disabled={!selectedScreens.length}
> size="L"
<Body size="XS"
>Please select the screens you would like to add to your application.
Autogenerated screens come with CRUD functionality.</Body
> >
<Body size="XS"
<Layout noPadding gap="S"> >Please select the screens you would like to add to your application.
<Detail size="S">Blank screen</Detail> Autogenerated screens come with CRUD functionality.</Body
<div
class="item"
class:selected={selectedScreens.find(x => x.id.includes(blankScreen))}
on:click={() =>
toggleScreenSelection(templates.find(t => t.id === blankScreen))}
class:disabled={autoSelected}
> >
<div data-cy="blank-screen" class="content">
<Body size="S">Blank</Body>
</div>
<div style="color: var(--spectrum-global-color-green-600); float: right">
{#if selectedScreens.find(x => x.id === blankScreen)}
<Icon size="S" name="CheckmarkCircleOutline" />
{/if}
</div>
</div>
<Detail size="S">Autogenerated Screens</Detail>
{#each $tables.list.filter(table => table._id !== "ta_users") as table} <Layout noPadding gap="S">
<Detail size="S">Blank screen</Detail>
<div <div
class:disabled={blankSelected}
class:selected={selectedScreens.find(x => x.name.includes(table.name))}
on:click={() => toggleScreenSelection(table)}
class="item" class="item"
class:selected={selectedScreens.find(x => x.id.includes(blankScreen))}
on:click={() =>
toggleScreenSelection(templates.find(t => t.id === blankScreen))}
class:disabled={autoSelected}
> >
<div class="content"> <div data-cy="blank-screen" class="content">
{table.name} <div class="text">Blank</div>
</div> </div>
<div <div
style="color: var(--spectrum-global-color-green-600); float: right" style="color: var(--spectrum-global-color-green-600); float: right"
> >
{#if selectedScreens.find(x => x.name.includes(table.name))} {#if selectedScreens.find(x => x.id === blankScreen)}
<Icon size="S" name="CheckmarkCircleOutline" /> <div class="checkmark-spacing">
<Icon size="S" name="CheckmarkCircleOutline" />
</div>
{/if} {/if}
</div> </div>
</div> </div>
{/each} <Detail size="S">Autogenerated Screens</Detail>
</Layout>
</ModalContent> {#each $tables.list.filter(table => table._id !== "ta_users") as table}
<div
class:disabled={blankSelected}
class:selected={selectedScreens.find(x =>
x.name.includes(table.name)
)}
on:click={() => toggleScreenSelection(table)}
class="item"
>
<div class="content">
<div class="text">{table.name}</div>
</div>
<div
style="color: var(--spectrum-global-color-green-600); float: right"
>
{#if selectedScreens.find(x => x.name.includes(table.name))}
<div class="checkmark-spacing">
<Icon size="S" name="CheckmarkCircleOutline" />
</div>
{/if}
</div>
</div>
{/each}
</Layout>
<div slot="footer">
{#if showProgressCircle}
<div class="footer-progress"><ProgressCircle size="S" /></div>
{/if}
</div>
</ModalContent>
</div>
<style> <style>
.disabled { .disabled {
opacity: 0.3; opacity: 0.3;
pointer-events: none; pointer-events: none;
} }
.checkmark-spacing {
margin-right: var(--spacing-m);
}
.content { .content {
letter-spacing: 0px; letter-spacing: 0px;
} }
.footer-progress {
margin-top: var(--spacing-s);
}
.text {
font-weight: 600;
margin-left: var(--spacing-m);
font-size: 14px;
text-transform: capitalize;
}
.item { .item {
cursor: pointer; cursor: pointer;
grid-gap: var(--spectrum-alias-grid-margin-xsmall); grid-gap: var(--spectrum-alias-grid-margin-xsmall);
padding: var(--spectrum-alias-item-padding-s); padding: var(--spectrum-alias-item-padding-s);
background: var(--background); background: var(--spectrum-alias-background-color-primary);
transition: 0.3s all; transition: 0.3s all;
border: solid var(--spectrum-alias-border-color); border: 1px solid #e7e7e7;
border-radius: 2px; border-radius: 4px;
box-sizing: border-box; box-sizing: border-box;
border-width: 1px; border-width: 1px;
display: flex; display: flex;

View File

@ -1,5 +1,5 @@
<script> <script>
import { ModalContent, Input } from "@budibase/bbui" import { ModalContent, Input, ProgressCircle } from "@budibase/bbui"
import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl" import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl"
import { selectedAccessRole, allScreens } from "builderStore" import { selectedAccessRole, allScreens } from "builderStore"
@ -7,6 +7,8 @@
export let url export let url
export let chooseModal export let chooseModal
export let save export let save
export let showProgressCircle = false
let routeError let routeError
let roleId = $selectedAccessRole || "BASIC" let roleId = $selectedAccessRole || "BASIC"
@ -48,4 +50,15 @@
bind:value={url} bind:value={url}
on:change={routeChanged} on:change={routeChanged}
/> />
<div slot="footer">
{#if showProgressCircle}
<div class="footer-progress"><ProgressCircle size="S" /></div>
{/if}
</div>
</ModalContent> </ModalContent>
<style>
.footer-progress {
margin-top: var(--spacing-s);
}
</style>

View File

@ -12,32 +12,36 @@
let screenName = "" let screenName = ""
let url = "" let url = ""
let selectedScreens = [] let selectedScreens = []
let roleId = $selectedAccessRole || "BASIC" let roleId = $selectedAccessRole || "BASIC"
let showProgressCircle = false
let routeError let routeError
let createdScreens = [] let createdScreens = []
$: {
selectedScreens?.forEach(screen => { const createScreens = async () => {
createdScreens = [...createdScreens, screen.create()] for (let screen of selectedScreens) {
}) let test = screen.create()
createdScreens.push(test)
}
} }
const save = async () => { const save = async () => {
showProgressCircle = true
await createScreens()
for (let screen of createdScreens) { for (let screen of createdScreens) {
await saveScreens(screen) await saveScreens(screen)
} }
await store.actions.routing.fetch() await store.actions.routing.fetch()
selectedScreens = [] selectedScreens = []
createdScreens = []
screenName = "" screenName = ""
url = "" url = ""
showProgressCircle = false
} }
const saveScreens = async draftScreen => { const saveScreens = async draftScreen => {
let existingScreenCount = $store.screens.filter( let existingScreenCount = $store.screens.filter(
s => s.props._instanceName == draftScreen.props._instanceName s => s.props._instanceName == draftScreen.props._instanceName
).length ).length
if (existingScreenCount > 0) { if (existingScreenCount > 0) {
let oldUrlArr = draftScreen.routing.route.split("/") let oldUrlArr = draftScreen.routing.route.split("/")
oldUrlArr[1] = `${oldUrlArr[1]}-${existingScreenCount + 1}` oldUrlArr[1] = `${oldUrlArr[1]}-${existingScreenCount + 1}`
@ -86,6 +90,7 @@
selectedScreens = [] selectedScreens = []
screenName = "" screenName = ""
url = "" url = ""
createdScreens = []
}) })
export const showModal = () => { export const showModal = () => {
@ -109,9 +114,20 @@
</script> </script>
<Modal bind:this={newScreenModal}> <Modal bind:this={newScreenModal}>
<NewScreenModal bind:selectedScreens {save} {chooseModal} /> <NewScreenModal
bind:selectedScreens
{showProgressCircle}
{save}
{chooseModal}
/>
</Modal> </Modal>
<Modal bind:this={screenDetailsModal}> <Modal bind:this={screenDetailsModal}>
<ScreenDetailsModal bind:screenName bind:url {save} {chooseModal} /> <ScreenDetailsModal
bind:screenName
bind:url
{showProgressCircle}
{save}
{chooseModal}
/>
</Modal> </Modal>

View File

@ -66,6 +66,9 @@ export function createTablesStore() {
} }
const response = await api.post(`/api/tables`, updatedTable) const response = await api.post(`/api/tables`, updatedTable)
if (response.status !== 200) {
throw (await response.json()).message
}
const savedTable = await response.json() const savedTable = await response.json()
await fetch() await fetch()
if (table.type === "external") { if (table.type === "external") {

View File

@ -42,7 +42,7 @@ describe("Tables Store", () => {
}) })
it("saving a table also selects it", async () => { it("saving a table also selects it", async () => {
api.post.mockReturnValue({ json: () => SAVE_TABLES_RESPONSE}) api.post.mockReturnValue({ status: 200, json: () => SAVE_TABLES_RESPONSE})
await store.save(A_TABLE) await store.save(A_TABLE)
@ -50,7 +50,7 @@ describe("Tables Store", () => {
}) })
it("saving the table returns a response", async () => { it("saving the table returns a response", async () => {
api.post.mockReturnValue({ json: () => SAVE_TABLES_RESPONSE}) api.post.mockReturnValue({ status: 200, json: () => SAVE_TABLES_RESPONSE})
const response = await store.save(A_TABLE) const response = await store.save(A_TABLE)

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "0.9.185-alpha.19", "version": "0.9.185-alpha.20",
"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

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "0.9.185-alpha.19", "version": "0.9.185-alpha.20",
"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": "^0.9.185-alpha.19", "@budibase/bbui": "^0.9.185-alpha.20",
"@budibase/standard-components": "^0.9.139", "@budibase/standard-components": "^0.9.139",
"@budibase/string-templates": "^0.9.185-alpha.19", "@budibase/string-templates": "^0.9.185-alpha.20",
"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

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "0.9.185-alpha.19", "version": "0.9.185-alpha.20",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.js", "main": "src/index.js",
"repository": { "repository": {
@ -68,9 +68,9 @@
"author": "Budibase", "author": "Budibase",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@budibase/auth": "^0.9.185-alpha.19", "@budibase/auth": "^0.9.185-alpha.20",
"@budibase/client": "^0.9.185-alpha.19", "@budibase/client": "^0.9.185-alpha.20",
"@budibase/string-templates": "^0.9.185-alpha.19", "@budibase/string-templates": "^0.9.185-alpha.20",
"@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",

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "0.9.185-alpha.19", "version": "0.9.185-alpha.20",
"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": "0.9.185-alpha.19", "version": "0.9.185-alpha.20",
"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": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@budibase/auth": "^0.9.185-alpha.19", "@budibase/auth": "^0.9.185-alpha.20",
"@budibase/string-templates": "^0.9.185-alpha.19", "@budibase/string-templates": "^0.9.185-alpha.20",
"@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",