Moving generation to builder because it reduces API calls and has no reason to be carried out server-side, handling array/object schema generation correctly.

This commit is contained in:
mike12345567 2021-11-29 17:11:08 +00:00
parent ed28bf664d
commit de0b23dd9f
5 changed files with 86 additions and 72 deletions

View File

@ -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
}

View File

@ -87,7 +87,10 @@
field.subtype !== AUTO_COLUMN_SUB_TYPES.CREATED_BY && field.subtype !== AUTO_COLUMN_SUB_TYPES.CREATED_BY &&
field.subtype !== AUTO_COLUMN_SUB_TYPES.UPDATED_BY && field.subtype !== AUTO_COLUMN_SUB_TYPES.UPDATED_BY &&
field.type !== FORMULA_TYPE 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 = $: canBeRequired =
field.type !== LINK_TYPE && !uneditable && field.type !== AUTO_TYPE field.type !== LINK_TYPE && !uneditable && field.type !== AUTO_TYPE
$: relationshipOptions = getRelationshipOptions(field) $: relationshipOptions = getRelationshipOptions(field)
@ -454,7 +457,11 @@
<Modal bind:this={jsonSchemaModal}> <Modal bind:this={jsonSchemaModal}>
<JSONSchemaModal <JSONSchemaModal
schema={field.schema} schema={field.schema}
on:save={({ detail }) => (field.schema = detail)} json={field.json}
on:save={({ detail }) => {
field.schema = detail.schema
field.json = detail.json
}}
/> />
</Modal> </Modal>
<ConfirmDialog <ConfirmDialog

View File

@ -7,20 +7,26 @@
Button, Button,
Input, Input,
Select, Select,
notifications,
} from "@budibase/bbui" } from "@budibase/bbui"
import { onMount, createEventDispatcher } from "svelte" import { onMount, createEventDispatcher } from "svelte"
import { post } from "builderStore/api" import { FIELDS } from "constants/backend"
import { generate } from "builderStore/schemaGenerator"
export let schema = {} export let schema = {}
export let json
let dispatcher = createEventDispatcher() let dispatcher = createEventDispatcher()
let mode = "Key/Value" let mode = "Key/Value"
let json
let fieldCount = 0 let fieldCount = 0
let fieldKeys = {}, let fieldKeys = {},
fieldTypes = {} 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 let invalid = false
async function onJsonUpdate({ detail }) { async function onJsonUpdate({ detail }) {
@ -29,17 +35,9 @@
try { try {
// check json valid first // check json valid first
let inputJson = JSON.parse(input) let inputJson = JSON.parse(input)
const response = await post("/api/tables/schema/generate", { schema = generate(inputJson)
json: inputJson,
})
if (response.status !== 200) {
const error = (await response.text()).message
notifications.error(error)
} else {
schema = await response.json()
updateCounts() updateCounts()
invalid = false invalid = false
}
} catch (err) { } catch (err) {
// json not currently valid // json not currently valid
invalid = true invalid = true
@ -62,11 +60,14 @@
function saveSchema() { function saveSchema() {
for (let i of Object.keys(fieldKeys)) { for (let i of Object.keys(fieldKeys)) {
const key = fieldKeys[i] const key = fieldKeys[i]
// they were added to schema, rather than generated
if (!schema[key]) {
schema[key] = { schema[key] = {
type: fieldTypes[i], type: fieldTypes[i],
} }
} }
dispatcher("save", schema) }
dispatcher("save", { schema, json })
} }
onMount(() => { onMount(() => {
@ -90,7 +91,8 @@
label="Type" label="Type"
options={keyValueOptions} options={keyValueOptions}
bind:value={fieldTypes[i]} bind:value={fieldTypes[i]}
getOptionValue={field => field.toLowerCase()} getOptionValue={field => field.value}
getOptionLabel={field => field.label}
/> />
</div> </div>
{/each} {/each}

View File

@ -9,7 +9,6 @@ const {
BudibaseInternalDB, BudibaseInternalDB,
} = require("../../../db/utils") } = require("../../../db/utils")
const { getTable } = require("./utils") const { getTable } = require("./utils")
const { FieldTypes } = require("../../../constants")
function pickApi({ tableId, table }) { function pickApi({ tableId, table }) {
if (table && !tableId) { if (table && !tableId) {
@ -82,38 +81,6 @@ exports.destroy = async function (ctx) {
ctx.body = { message: `Table ${tableId} deleted.` } ctx.body = { message: `Table ${tableId} deleted.` }
} }
exports.schemaGenerate = function (ctx) {
const { json } = ctx.request.body
function recurse(schemaLevel, objectLevel) {
for (let [key, value] of Object.entries(objectLevel)) {
const type = typeof value
// check array first, since arrays are objects
if (Array.isArray(value)) {
schemaLevel[key] = {
type: FieldTypes.ARRAY,
}
} else if (type === "object") {
schemaLevel[key] = recurse(schemaLevel[key], objectLevel)
} else if (type === "string") {
schemaLevel[key] = {
type: FieldTypes.STRING,
}
} else if (type === "boolean") {
schemaLevel[key] = {
type: FieldTypes.BOOLEAN,
}
} else if (type === "number") {
schemaLevel[key] = {
type: FieldTypes.NUMBER,
}
}
}
return schemaLevel
}
ctx.body = recurse({}, json) || {}
}
exports.bulkImport = async function (ctx) { exports.bulkImport = async function (ctx) {
const tableId = ctx.params.tableId const tableId = ctx.params.tableId
await pickApi({ tableId }).bulkImport(ctx) await pickApi({ tableId }).bulkImport(ctx)

View File

@ -139,24 +139,6 @@ router
generateSaveValidator(), generateSaveValidator(),
tableController.save tableController.save
) )
/**
* @api {post} /api/tables/schema/generate Generate schema from JSON
* @apiName Generate schema from JSON
* @apiGroup tables
* @apiPermission builder
* @apiDescription Given a JSON structure this will generate a nested schema that can be used for a key/value data
* type in a table.
*
* @apiParam (Body) {object} json The JSON structure from which a nest schema should be generated.
*
* @apiSuccess {object} schema The response body will contain the schema, which can now be used for a key/value
* data type.
*/
.post(
"/api/tables/schema/generate",
authorized(BUILDER),
tableController.schemaGenerate
)
/** /**
* @api {post} /api/tables/csv/validate Validate a CSV for a table * @api {post} /api/tables/csv/validate Validate a CSV for a table
* @apiName Validate a CSV for a table * @apiName Validate a CSV for a table