Merge branch 'feature/json-backend' of github.com:Budibase/budibase into feature/json-backend
This commit is contained in:
commit
ba9d06df34
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "0.9.190-alpha.11",
|
||||
"version": "0.9.190-alpha.12",
|
||||
"npmClient": "yarn",
|
||||
"packages": [
|
||||
"packages/*"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/auth",
|
||||
"version": "0.9.190-alpha.11",
|
||||
"version": "0.9.190-alpha.12",
|
||||
"description": "Authentication middlewares for budibase builder and apps",
|
||||
"main": "src/index.js",
|
||||
"author": "Budibase",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/bbui",
|
||||
"description": "A UI solution used in the different Budibase projects.",
|
||||
"version": "0.9.190-alpha.11",
|
||||
"version": "0.9.190-alpha.12",
|
||||
"license": "MPL-2.0",
|
||||
"svelte": "src/index.js",
|
||||
"module": "dist/bbui.es.js",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/builder",
|
||||
"version": "0.9.190-alpha.11",
|
||||
"version": "0.9.190-alpha.12",
|
||||
"license": "GPL-3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
@ -65,10 +65,10 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "^0.9.190-alpha.11",
|
||||
"@budibase/client": "^0.9.190-alpha.11",
|
||||
"@budibase/bbui": "^0.9.190-alpha.12",
|
||||
"@budibase/client": "^0.9.190-alpha.12",
|
||||
"@budibase/colorpicker": "1.1.2",
|
||||
"@budibase/string-templates": "^0.9.190-alpha.11",
|
||||
"@budibase/string-templates": "^0.9.190-alpha.12",
|
||||
"@sentry/browser": "5.19.1",
|
||||
"@spectrum-css/page": "^3.0.1",
|
||||
"@spectrum-css/vars": "^3.0.1",
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import { FIELDS } from "constants/backend"
|
||||
|
||||
function baseConversion(type) {
|
||||
if (type === "string") {
|
||||
return {
|
||||
type: FIELDS.STRING.type,
|
||||
}
|
||||
} else if (type === "boolean") {
|
||||
return {
|
||||
type: FIELDS.BOOLEAN.type,
|
||||
}
|
||||
} else if (type === "number") {
|
||||
return {
|
||||
type: FIELDS.NUMBER.type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function recurse(schemaLevel = {}, objectLevel) {
|
||||
if (!objectLevel) {
|
||||
return null
|
||||
}
|
||||
const baseType = typeof objectLevel
|
||||
if (baseType !== "object") {
|
||||
return baseConversion(baseType)
|
||||
}
|
||||
for (let [key, value] of Object.entries(objectLevel)) {
|
||||
const type = typeof value
|
||||
// check array first, since arrays are objects
|
||||
if (Array.isArray(value)) {
|
||||
const schema = recurse(schemaLevel[key], value[0])
|
||||
if (schema) {
|
||||
schemaLevel[key] = {
|
||||
type: FIELDS.ARRAY.type,
|
||||
schema,
|
||||
}
|
||||
}
|
||||
} else if (type === "object") {
|
||||
const schema = recurse(schemaLevel[key], objectLevel[key])
|
||||
if (schema) {
|
||||
schemaLevel[key] = schema
|
||||
}
|
||||
} else {
|
||||
schemaLevel[key] = baseConversion(type)
|
||||
}
|
||||
}
|
||||
if (!schemaLevel.type) {
|
||||
return { type: FIELDS.JSON.type, schema: schemaLevel }
|
||||
} else {
|
||||
return schemaLevel
|
||||
}
|
||||
}
|
||||
|
||||
export function generate(object) {
|
||||
return recurse({}, object).schema
|
||||
}
|
|
@ -524,7 +524,7 @@ export const getFrontendStore = () => {
|
|||
}
|
||||
}
|
||||
},
|
||||
paste: async (targetComponent, mode) => {
|
||||
paste: async (targetComponent, mode, preserveBindings = false) => {
|
||||
let promises = []
|
||||
store.update(state => {
|
||||
// Stop if we have nothing to paste
|
||||
|
@ -536,7 +536,7 @@ export const getFrontendStore = () => {
|
|||
const cut = state.componentToPaste.isCut
|
||||
|
||||
// immediately need to remove bindings, currently these aren't valid when pasted
|
||||
if (!cut) {
|
||||
if (!cut && !preserveBindings) {
|
||||
state.componentToPaste = removeBindings(state.componentToPaste)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,16 +6,20 @@
|
|||
Toggle,
|
||||
TextArea,
|
||||
Multiselect,
|
||||
Label,
|
||||
} from "@budibase/bbui"
|
||||
import Dropzone from "components/common/Dropzone.svelte"
|
||||
import { capitalise } from "helpers"
|
||||
import LinkedRowSelector from "components/common/LinkedRowSelector.svelte"
|
||||
import Editor from "../../integration/QueryEditor.svelte"
|
||||
|
||||
export let defaultValue
|
||||
export let meta
|
||||
export let value = defaultValue || (meta.type === "boolean" ? false : "")
|
||||
export let readonly
|
||||
|
||||
$: stringVal =
|
||||
typeof value === "object" ? JSON.stringify(value, null, 2) : value
|
||||
$: type = meta?.type
|
||||
$: label = meta.name ? capitalise(meta.name) : ""
|
||||
</script>
|
||||
|
@ -40,6 +44,14 @@
|
|||
<LinkedRowSelector bind:linkedRows={value} schema={meta} />
|
||||
{:else if type === "longform"}
|
||||
<TextArea {label} bind:value />
|
||||
{:else if type === "json"}
|
||||
<Label>{label}</Label>
|
||||
<Editor
|
||||
editorHeight="250"
|
||||
mode="json"
|
||||
on:change={({ detail }) => (value = detail.value)}
|
||||
value={stringVal}
|
||||
/>
|
||||
{:else}
|
||||
<Input
|
||||
{label}
|
||||
|
|
|
@ -87,7 +87,10 @@
|
|||
field.subtype !== AUTO_COLUMN_SUB_TYPES.CREATED_BY &&
|
||||
field.subtype !== AUTO_COLUMN_SUB_TYPES.UPDATED_BY &&
|
||||
field.type !== FORMULA_TYPE
|
||||
$: canBeDisplay = field.type !== LINK_TYPE && field.type !== AUTO_TYPE
|
||||
$: canBeDisplay =
|
||||
field.type !== LINK_TYPE &&
|
||||
field.type !== AUTO_TYPE &&
|
||||
field.type !== JSON_TYPE
|
||||
$: canBeRequired =
|
||||
field.type !== LINK_TYPE && !uneditable && field.type !== AUTO_TYPE
|
||||
$: relationshipOptions = getRelationshipOptions(field)
|
||||
|
@ -452,7 +455,14 @@
|
|||
</div>
|
||||
</ModalContent>
|
||||
<Modal bind:this={jsonSchemaModal}>
|
||||
<JSONSchemaModal on:save={({ detail }) => console.log(detail)} />
|
||||
<JSONSchemaModal
|
||||
schema={field.schema}
|
||||
json={field.json}
|
||||
on:save={({ detail }) => {
|
||||
field.schema = detail.schema
|
||||
field.json = detail.json
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
<ConfirmDialog
|
||||
bind:this={confirmDeleteDialog}
|
||||
|
|
|
@ -9,36 +9,42 @@
|
|||
Select,
|
||||
} from "@budibase/bbui"
|
||||
import { onMount, createEventDispatcher } from "svelte"
|
||||
import { FIELDS } from "constants/backend"
|
||||
import { generate } from "builderStore/schemaGenerator"
|
||||
|
||||
export let schema = {}
|
||||
export let json
|
||||
|
||||
let dispatcher = createEventDispatcher()
|
||||
let mode = "Key/Value"
|
||||
let json
|
||||
let fieldCount = 0
|
||||
let fieldKeys = {},
|
||||
fieldTypes = {}
|
||||
let keyValueOptions = ["String", "Number", "Boolean", "Object", "Array"]
|
||||
let keyValueOptions = [
|
||||
{ label: "String", value: FIELDS.STRING.type },
|
||||
{ label: "Number", value: FIELDS.NUMBER.type },
|
||||
{ label: "Boolean", value: FIELDS.BOOLEAN.type },
|
||||
{ label: "Object", value: FIELDS.JSON.type },
|
||||
{ label: "Array", value: FIELDS.ARRAY.type },
|
||||
]
|
||||
let invalid = false
|
||||
|
||||
$: invalid = false
|
||||
|
||||
function onJsonUpdate({ detail }) {
|
||||
// TODO: make request
|
||||
async function onJsonUpdate({ detail }) {
|
||||
const input = detail.value
|
||||
console.log(input)
|
||||
}
|
||||
|
||||
function saveSchema() {
|
||||
for (let i of Object.keys(fieldKeys)) {
|
||||
const key = fieldKeys[i]
|
||||
schema[key] = {
|
||||
type: fieldTypes[i],
|
||||
}
|
||||
json = input
|
||||
try {
|
||||
// check json valid first
|
||||
let inputJson = JSON.parse(input)
|
||||
schema = generate(inputJson)
|
||||
updateCounts()
|
||||
invalid = false
|
||||
} catch (err) {
|
||||
// json not currently valid
|
||||
invalid = true
|
||||
}
|
||||
dispatcher("save", schema)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
function updateCounts() {
|
||||
if (!schema) {
|
||||
schema = {}
|
||||
}
|
||||
|
@ -49,6 +55,24 @@
|
|||
i++
|
||||
}
|
||||
fieldCount = i
|
||||
}
|
||||
|
||||
function saveSchema() {
|
||||
for (let i of Object.keys(fieldKeys)) {
|
||||
const key = fieldKeys[i]
|
||||
// they were added to schema, rather than generated
|
||||
if (!schema[key]) {
|
||||
schema[key] = {
|
||||
type: fieldTypes[i],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dispatcher("save", { schema, json })
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
updateCounts()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -56,7 +80,7 @@
|
|||
title={"Key/Value Schema Editor"}
|
||||
confirmText="Save Column"
|
||||
onConfirm={saveSchema}
|
||||
disabled={invalid}
|
||||
bind:disabled={invalid}
|
||||
size="L"
|
||||
>
|
||||
<Tabs selected={mode} noPadding>
|
||||
|
@ -68,7 +92,8 @@
|
|||
label="Type"
|
||||
options={keyValueOptions}
|
||||
bind:value={fieldTypes[i]}
|
||||
getOptionValue={field => field.toLowerCase()}
|
||||
getOptionValue={field => field.value}
|
||||
getOptionLabel={field => field.label}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
|
||||
const duplicateComponent = () => {
|
||||
storeComponentForCopy(false)
|
||||
pasteComponent("below")
|
||||
pasteComponent("below", true)
|
||||
}
|
||||
|
||||
const deleteComponent = async () => {
|
||||
|
@ -69,9 +69,9 @@
|
|||
store.actions.components.copy(component, cut)
|
||||
}
|
||||
|
||||
const pasteComponent = mode => {
|
||||
const pasteComponent = (mode, preserveBindings = false) => {
|
||||
// lives in store - also used by drag drop
|
||||
store.actions.components.paste(component, mode)
|
||||
store.actions.components.paste(component, mode, preserveBindings)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -2,13 +2,15 @@
|
|||
import { Button, ActionButton, Drawer } from "@budibase/bbui"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
import NavigationDrawer from "./NavigationDrawer.svelte"
|
||||
import { cloneDeep } from "lodash/fp"
|
||||
|
||||
export let value = []
|
||||
let drawer
|
||||
let links = cloneDeep(value)
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const save = () => {
|
||||
dispatch("change", value)
|
||||
dispatch("change", links)
|
||||
drawer.hide()
|
||||
}
|
||||
</script>
|
||||
|
@ -19,5 +21,5 @@
|
|||
Configure the links in your navigation bar.
|
||||
</svelte:fragment>
|
||||
<Button cta slot="buttons" on:click={save}>Save</Button>
|
||||
<NavigationDrawer slot="body" bind:links={value} />
|
||||
<NavigationDrawer slot="body" bind:links />
|
||||
</Drawer>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/cli",
|
||||
"version": "0.9.190-alpha.11",
|
||||
"version": "0.9.190-alpha.12",
|
||||
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
||||
"main": "src/index.js",
|
||||
"bin": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/client",
|
||||
"version": "0.9.190-alpha.11",
|
||||
"version": "0.9.190-alpha.12",
|
||||
"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.190-alpha.11",
|
||||
"@budibase/bbui": "^0.9.190-alpha.12",
|
||||
"@budibase/standard-components": "^0.9.139",
|
||||
"@budibase/string-templates": "^0.9.190-alpha.11",
|
||||
"@budibase/string-templates": "^0.9.190-alpha.12",
|
||||
"regexparam": "^1.3.0",
|
||||
"shortid": "^2.2.15",
|
||||
"svelte-spa-router": "^3.0.5"
|
||||
|
|
|
@ -313,6 +313,9 @@
|
|||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
.desktop.layout--left .links {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.desktop .nav--left {
|
||||
width: 250px;
|
||||
|
@ -379,6 +382,7 @@
|
|||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
padding: var(--spacing-xl);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.mobile .link {
|
||||
width: calc(100% - 30px);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/server",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "0.9.190-alpha.11",
|
||||
"version": "0.9.190-alpha.12",
|
||||
"description": "Budibase Web Server",
|
||||
"main": "src/index.ts",
|
||||
"repository": {
|
||||
|
@ -69,9 +69,9 @@
|
|||
"author": "Budibase",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@budibase/auth": "^0.9.190-alpha.11",
|
||||
"@budibase/client": "^0.9.190-alpha.11",
|
||||
"@budibase/string-templates": "^0.9.190-alpha.11",
|
||||
"@budibase/auth": "^0.9.190-alpha.12",
|
||||
"@budibase/client": "^0.9.190-alpha.12",
|
||||
"@budibase/string-templates": "^0.9.190-alpha.12",
|
||||
"@bull-board/api": "^3.7.0",
|
||||
"@bull-board/koa": "^3.7.0",
|
||||
"@elastic/elasticsearch": "7.10.0",
|
||||
|
|
|
@ -50,10 +50,10 @@ exports.validate = async ({ appId, tableId, row, table }) => {
|
|||
const errors = {}
|
||||
for (let fieldName of Object.keys(table.schema)) {
|
||||
const constraints = cloneDeep(table.schema[fieldName].constraints)
|
||||
const type = table.schema[fieldName].type
|
||||
// special case for options, need to always allow unselected (null)
|
||||
if (
|
||||
table.schema[fieldName].type ===
|
||||
(FieldTypes.OPTIONS || FieldTypes.ARRAY) &&
|
||||
(type === FieldTypes.OPTIONS || type === FieldTypes.ARRAY) &&
|
||||
constraints.inclusion
|
||||
) {
|
||||
constraints.inclusion.push(null)
|
||||
|
@ -61,17 +61,20 @@ exports.validate = async ({ appId, tableId, row, table }) => {
|
|||
let res
|
||||
|
||||
// Validate.js doesn't seem to handle array
|
||||
if (
|
||||
table.schema[fieldName].type === FieldTypes.ARRAY &&
|
||||
row[fieldName] &&
|
||||
row[fieldName].length
|
||||
) {
|
||||
if (type === FieldTypes.ARRAY && row[fieldName] && row[fieldName].length) {
|
||||
row[fieldName].map(val => {
|
||||
if (!constraints.inclusion.includes(val)) {
|
||||
errors[fieldName] = "Field not in list"
|
||||
}
|
||||
})
|
||||
} else if (table.schema[fieldName].type === FieldTypes.FORMULA) {
|
||||
} else if (type === FieldTypes.JSON && typeof row[fieldName] === "string") {
|
||||
// this should only happen if there is an error
|
||||
try {
|
||||
JSON.parse(row[fieldName])
|
||||
} catch (err) {
|
||||
errors[fieldName] = [`Contains invalid JSON`]
|
||||
}
|
||||
} else if (type === FieldTypes.FORMULA) {
|
||||
res = validateJs.single(
|
||||
processStringSync(table.schema[fieldName].formula, row),
|
||||
constraints
|
||||
|
|
|
@ -81,6 +81,18 @@ const TYPE_TRANSFORM_MAP = {
|
|||
[FieldTypes.AUTO]: {
|
||||
parse: () => undefined,
|
||||
},
|
||||
[FieldTypes.JSON]: {
|
||||
parse: input => {
|
||||
try {
|
||||
if (input === "") {
|
||||
return undefined
|
||||
}
|
||||
return JSON.parse(input)
|
||||
} catch (err) {
|
||||
return input
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/string-templates",
|
||||
"version": "0.9.190-alpha.11",
|
||||
"version": "0.9.190-alpha.12",
|
||||
"description": "Handlebars wrapper for Budibase templating.",
|
||||
"main": "src/index.cjs",
|
||||
"module": "dist/bundle.mjs",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/worker",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "0.9.190-alpha.11",
|
||||
"version": "0.9.190-alpha.12",
|
||||
"description": "Budibase background service",
|
||||
"main": "src/index.js",
|
||||
"repository": {
|
||||
|
@ -29,8 +29,8 @@
|
|||
"author": "Budibase",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@budibase/auth": "^0.9.190-alpha.11",
|
||||
"@budibase/string-templates": "^0.9.190-alpha.11",
|
||||
"@budibase/auth": "^0.9.190-alpha.12",
|
||||
"@budibase/string-templates": "^0.9.190-alpha.12",
|
||||
"@koa/router": "^8.0.0",
|
||||
"@sentry/node": "^6.0.0",
|
||||
"@techpass/passport-openidconnect": "^0.3.0",
|
||||
|
|
Loading…
Reference in New Issue