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.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)
@ -454,7 +457,11 @@
<Modal bind:this={jsonSchemaModal}>
<JSONSchemaModal
schema={field.schema}
on:save={({ detail }) => (field.schema = detail)}
json={field.json}
on:save={({ detail }) => {
field.schema = detail.schema
field.json = detail.json
}}
/>
</Modal>
<ConfirmDialog

View File

@ -7,20 +7,26 @@
Button,
Input,
Select,
notifications,
} from "@budibase/bbui"
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 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
async function onJsonUpdate({ detail }) {
@ -29,17 +35,9 @@
try {
// check json valid first
let inputJson = JSON.parse(input)
const response = await post("/api/tables/schema/generate", {
json: inputJson,
})
if (response.status !== 200) {
const error = (await response.text()).message
notifications.error(error)
} else {
schema = await response.json()
schema = generate(inputJson)
updateCounts()
invalid = false
}
} catch (err) {
// json not currently valid
invalid = true
@ -62,11 +60,14 @@
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)
}
dispatcher("save", { schema, json })
}
onMount(() => {
@ -90,7 +91,8 @@
label="Type"
options={keyValueOptions}
bind:value={fieldTypes[i]}
getOptionValue={field => field.toLowerCase()}
getOptionValue={field => field.value}
getOptionLabel={field => field.label}
/>
</div>
{/each}

View File

@ -9,7 +9,6 @@ const {
BudibaseInternalDB,
} = require("../../../db/utils")
const { getTable } = require("./utils")
const { FieldTypes } = require("../../../constants")
function pickApi({ tableId, table }) {
if (table && !tableId) {
@ -82,38 +81,6 @@ exports.destroy = async function (ctx) {
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) {
const tableId = ctx.params.tableId
await pickApi({ tableId }).bulkImport(ctx)

View File

@ -139,24 +139,6 @@ router
generateSaveValidator(),
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
* @apiName Validate a CSV for a table