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:
parent
5ff8716080
commit
05e2baa0d3
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue