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",
"packages": [
"packages/*"

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/auth",
"version": "0.9.185-alpha.19",
"version": "0.9.185-alpha.20",
"description": "Authentication middlewares for budibase builder and apps",
"main": "src/index.js",
"author": "Budibase",

View File

@ -1,7 +1,7 @@
{
"name": "@budibase/bbui",
"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",
"svelte": "src/index.js",
"module": "dist/bbui.es.js",

View File

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

View File

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

View File

@ -71,6 +71,9 @@
if ($touched.toCol && !toRelate.name) {
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
const tableError = "From/to/through tables must be different"
if (fromTable && (fromTable === toTable || fromTable === throughTable)) {
@ -95,6 +98,16 @@
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 => ({
label: table.name,
value: table._id,
@ -179,13 +192,13 @@
// foreignKey is what is linking out of the current table.
relateFrom = {
...relateFrom,
foreignKey: fromTable.primary[0],
foreignKey: fromPrimary,
}
relateTo = {
...relateTo,
relationshipType: RelationshipTypes.ONE_TO_MANY,
foreignKey: relateFrom.fieldName,
fieldName: fromTable.primary[0],
fieldName: fromPrimary,
}
}
@ -264,6 +277,15 @@
bind:error={errors.from}
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
label={"Select to table"}
options={tableOptions}
@ -271,7 +293,7 @@
bind:error={errors.to}
bind:value={fromRelationship.tableId}
/>
{#if fromRelationship?.relationshipType === RelationshipTypes.MANY_TO_MANY}
{#if isManyToMany}
<Select
label={"Through"}
options={tableOptions}
@ -295,7 +317,7 @@
bind:value={fromRelationship.throughFrom}
/>
{/if}
{:else if fromRelationship?.relationshipType && toTable}
{:else if isManyToOne && toTable}
<Select
label={`Foreign Key (${toTable?.name})`}
options={Object.keys(toTable?.schema).filter(

View File

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

View File

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

View File

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

View File

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

View File

@ -42,7 +42,7 @@ describe("Tables Store", () => {
})
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)
@ -50,7 +50,7 @@ describe("Tables Store", () => {
})
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)

View File

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

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/client",
"version": "0.9.185-alpha.19",
"version": "0.9.185-alpha.20",
"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.185-alpha.19",
"@budibase/bbui": "^0.9.185-alpha.20",
"@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",
"shortid": "^2.2.15",
"svelte-spa-router": "^3.0.5"

View File

@ -1,7 +1,7 @@
{
"name": "@budibase/server",
"email": "hi@budibase.com",
"version": "0.9.185-alpha.19",
"version": "0.9.185-alpha.20",
"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.185-alpha.19",
"@budibase/client": "^0.9.185-alpha.19",
"@budibase/string-templates": "^0.9.185-alpha.19",
"@budibase/auth": "^0.9.185-alpha.20",
"@budibase/client": "^0.9.185-alpha.20",
"@budibase/string-templates": "^0.9.185-alpha.20",
"@bull-board/api": "^3.7.0",
"@bull-board/koa": "^3.7.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",
"version": "0.9.185-alpha.19",
"version": "0.9.185-alpha.20",
"description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs",
"module": "dist/bundle.mjs",

View File

@ -1,7 +1,7 @@
{
"name": "@budibase/worker",
"email": "hi@budibase.com",
"version": "0.9.185-alpha.19",
"version": "0.9.185-alpha.20",
"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.185-alpha.19",
"@budibase/string-templates": "^0.9.185-alpha.19",
"@budibase/auth": "^0.9.185-alpha.20",
"@budibase/string-templates": "^0.9.185-alpha.20",
"@koa/router": "^8.0.0",
"@sentry/node": "^6.0.0",
"@techpass/passport-openidconnect": "^0.3.0",