Getting client partially working, having an issue with search fields not updating for a table block.
This commit is contained in:
parent
ee29245b6a
commit
018e0bd73b
|
@ -14,7 +14,7 @@
|
||||||
import ClientBindingPanel from "components/common/bindings/ClientBindingPanel.svelte"
|
import ClientBindingPanel from "components/common/bindings/ClientBindingPanel.svelte"
|
||||||
import { generate } from "shortid"
|
import { generate } from "shortid"
|
||||||
import { getValidOperatorsForType, OperatorOptions } from "constants/lucene"
|
import { getValidOperatorsForType, OperatorOptions } from "constants/lucene"
|
||||||
import { tables } from "stores/backend"
|
import { getFields } from "helpers/searchFields"
|
||||||
|
|
||||||
export let schemaFields
|
export let schemaFields
|
||||||
export let filters = []
|
export let filters = []
|
||||||
|
@ -22,34 +22,6 @@
|
||||||
export let panel = ClientBindingPanel
|
export let panel = ClientBindingPanel
|
||||||
export let allowBindings = true
|
export let allowBindings = true
|
||||||
|
|
||||||
const BannedTypes = ["link", "attachment", "formula", "json", "jsonarray"]
|
|
||||||
|
|
||||||
function getTableFields(linkField) {
|
|
||||||
const table = $tables.list.find(table => table._id === linkField.tableId)
|
|
||||||
if (!table || !table.sql) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
const linkFields = getFields(Object.values(table.schema), {
|
|
||||||
allowLinks: false,
|
|
||||||
})
|
|
||||||
return linkFields.map(field => ({
|
|
||||||
...field,
|
|
||||||
name: `${linkField.name}.${field.name}`,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFields(fields, { allowLinks } = { allowLinks: true }) {
|
|
||||||
let fieldNames = fields.filter(field => !BannedTypes.includes(field.type))
|
|
||||||
if (allowLinks) {
|
|
||||||
const linkFields = fields.filter(field => field.type === "link")
|
|
||||||
for (let linkField of linkFields) {
|
|
||||||
// only allow one depth of SQL relationship filtering
|
|
||||||
fieldNames = fieldNames.concat(getTableFields(linkField))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fieldNames
|
|
||||||
}
|
|
||||||
|
|
||||||
$: enrichedSchemaFields = getFields(schemaFields || [])
|
$: enrichedSchemaFields = getFields(schemaFields || [])
|
||||||
$: fieldOptions = enrichedSchemaFields.map(field => field.name) || []
|
$: fieldOptions = enrichedSchemaFields.map(field => field.name) || []
|
||||||
$: valueTypeOptions = allowBindings ? ["Value", "Binding"] : ["Value"]
|
$: valueTypeOptions = allowBindings ? ["Value", "Binding"] : ["Value"]
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
<script>
|
||||||
|
import { Multiselect } from "@budibase/bbui"
|
||||||
|
import {
|
||||||
|
getDatasourceForProvider,
|
||||||
|
getSchemaForDatasource,
|
||||||
|
} from "builderStore/dataBinding"
|
||||||
|
import { currentAsset } from "builderStore"
|
||||||
|
import { tables } from "stores/backend"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
import { getTableFields } from "helpers/searchFields"
|
||||||
|
|
||||||
|
export let componentInstance = {}
|
||||||
|
export let value = ""
|
||||||
|
export let placeholder
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
$: datasource = getDatasourceForProvider($currentAsset, componentInstance)
|
||||||
|
$: schema = getSchemaForDatasource($currentAsset, datasource).schema
|
||||||
|
$: options = getOptions(datasource, schema || {})
|
||||||
|
$: boundValue = getSelectedOption(value, options)
|
||||||
|
|
||||||
|
function getOptions(ds, dsSchema) {
|
||||||
|
let base = Object.keys(dsSchema)
|
||||||
|
if (!ds?.tableId) {
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
const currentTable = $tables.list.find(table => table._id === ds.tableId)
|
||||||
|
if (currentTable && currentTable.sql) {
|
||||||
|
for (let column of Object.values(currentTable.schema)) {
|
||||||
|
if (column.type === "link") {
|
||||||
|
base = base.concat(getTableFields(column).map(field => field.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSelectedOption(selectedOptions, allOptions) {
|
||||||
|
// Fix the hardcoded default string value
|
||||||
|
if (!Array.isArray(selectedOptions)) {
|
||||||
|
selectedOptions = []
|
||||||
|
}
|
||||||
|
return selectedOptions.filter(val => allOptions.indexOf(val) !== -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setValue = value => {
|
||||||
|
boundValue = getSelectedOption(value.detail, options)
|
||||||
|
dispatch("change", boundValue)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Multiselect {placeholder} value={boundValue} on:change={setValue} {options} />
|
|
@ -7,6 +7,7 @@ import ColorPicker from "./ColorPicker.svelte"
|
||||||
import { IconSelect } from "./IconSelect"
|
import { IconSelect } from "./IconSelect"
|
||||||
import FieldSelect from "./FieldSelect.svelte"
|
import FieldSelect from "./FieldSelect.svelte"
|
||||||
import MultiFieldSelect from "./MultiFieldSelect.svelte"
|
import MultiFieldSelect from "./MultiFieldSelect.svelte"
|
||||||
|
import SearchFieldSelect from "./SearchFieldSelect.svelte"
|
||||||
import SchemaSelect from "./SchemaSelect.svelte"
|
import SchemaSelect from "./SchemaSelect.svelte"
|
||||||
import SectionSelect from "./SectionSelect.svelte"
|
import SectionSelect from "./SectionSelect.svelte"
|
||||||
import NavigationEditor from "./NavigationEditor/NavigationEditor.svelte"
|
import NavigationEditor from "./NavigationEditor/NavigationEditor.svelte"
|
||||||
|
@ -30,6 +31,7 @@ const componentMap = {
|
||||||
icon: IconSelect,
|
icon: IconSelect,
|
||||||
field: FieldSelect,
|
field: FieldSelect,
|
||||||
multifield: MultiFieldSelect,
|
multifield: MultiFieldSelect,
|
||||||
|
searchfield: SearchFieldSelect,
|
||||||
options: OptionsEditor,
|
options: OptionsEditor,
|
||||||
schema: SchemaSelect,
|
schema: SchemaSelect,
|
||||||
section: SectionSelect,
|
section: SectionSelect,
|
||||||
|
|
|
@ -229,3 +229,11 @@ export const PaginationLocations = [
|
||||||
{ label: "Query parameters", value: "query" },
|
{ label: "Query parameters", value: "query" },
|
||||||
{ label: "Request body", value: "body" },
|
{ label: "Request body", value: "body" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export const BannedSearchTypes = [
|
||||||
|
"link",
|
||||||
|
"attachment",
|
||||||
|
"formula",
|
||||||
|
"json",
|
||||||
|
"jsonarray",
|
||||||
|
]
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { tables } from "../stores/backend"
|
||||||
|
import { BannedSearchTypes } from "../constants/backend"
|
||||||
|
import { get } from "svelte/store"
|
||||||
|
|
||||||
|
export function getTableFields(linkField) {
|
||||||
|
const table = get(tables).list.find(table => table._id === linkField.tableId)
|
||||||
|
if (!table || !table.sql) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
const linkFields = getFields(Object.values(table.schema), {
|
||||||
|
allowLinks: false,
|
||||||
|
})
|
||||||
|
return linkFields.map(field => ({
|
||||||
|
...field,
|
||||||
|
name: `${table.name}.${field.name}`,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFields(fields, { allowLinks } = { allowLinks: true }) {
|
||||||
|
let fieldNames = fields.filter(
|
||||||
|
field => !BannedSearchTypes.includes(field.type)
|
||||||
|
)
|
||||||
|
if (allowLinks) {
|
||||||
|
const linkFields = fields.filter(field => field.type === "link")
|
||||||
|
for (let linkField of linkFields) {
|
||||||
|
// only allow one depth of SQL relationship filtering
|
||||||
|
fieldNames = fieldNames.concat(getTableFields(linkField))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fieldNames
|
||||||
|
}
|
|
@ -2811,7 +2811,7 @@
|
||||||
"key": "dataSource"
|
"key": "dataSource"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "multifield",
|
"type": "searchfield",
|
||||||
"label": "Search Columns",
|
"label": "Search Columns",
|
||||||
"key": "searchColumns",
|
"key": "searchColumns",
|
||||||
"placeholder": "Choose search columns"
|
"placeholder": "Choose search columns"
|
||||||
|
|
|
@ -41,9 +41,11 @@
|
||||||
let dataProviderId
|
let dataProviderId
|
||||||
let schema
|
let schema
|
||||||
let schemaLoaded = false
|
let schemaLoaded = false
|
||||||
|
let enrichedSearchColumns
|
||||||
|
let enrichedSearchColumnsLoaded = false
|
||||||
|
|
||||||
$: fetchSchema(dataSource)
|
$: fetchSchema(dataSource)
|
||||||
$: enrichedSearchColumns = enrichSearchColumns(searchColumns, schema)
|
$: enrichSearchColumns(searchColumns, schema)
|
||||||
$: enrichedFilter = enrichFilter(filter, enrichedSearchColumns, formId)
|
$: enrichedFilter = enrichFilter(filter, enrichedSearchColumns, formId)
|
||||||
$: titleButtonAction = [
|
$: titleButtonAction = [
|
||||||
{
|
{
|
||||||
|
@ -71,9 +73,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine data types for search fields and only use those that are valid
|
// Determine data types for search fields and only use those that are valid
|
||||||
const enrichSearchColumns = (searchColumns, schema) => {
|
const enrichSearchColumns = async (searchColumns, schema) => {
|
||||||
let enrichedColumns = []
|
let enrichedColumns = []
|
||||||
searchColumns?.forEach(column => {
|
const addType = column => {
|
||||||
const schemaType = schema?.[column]?.type
|
const schemaType = schema?.[column]?.type
|
||||||
const componentType = schemaComponentMap[schemaType]
|
const componentType = schemaComponentMap[schemaType]
|
||||||
if (componentType) {
|
if (componentType) {
|
||||||
|
@ -82,9 +84,39 @@
|
||||||
componentType,
|
componentType,
|
||||||
type: schemaType,
|
type: schemaType,
|
||||||
})
|
})
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
})
|
return false
|
||||||
return enrichedColumns.slice(0, 3)
|
}
|
||||||
|
for (let column of searchColumns || []) {
|
||||||
|
// if addType returns false, it didn't find one, look for SQL relationships
|
||||||
|
if (!addType(column) && column.includes(".")) {
|
||||||
|
const [tableName, linkColumn] = column.split(".")
|
||||||
|
for (let colSchema of Object.values(schema || {})) {
|
||||||
|
// found the related table
|
||||||
|
if (
|
||||||
|
colSchema.type === "link" &&
|
||||||
|
colSchema.tableId &&
|
||||||
|
colSchema.tableId.endsWith(tableName)
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const linkSchema = await fetchDatasourceSchema({
|
||||||
|
...dataSource,
|
||||||
|
tableId: colSchema.tableId,
|
||||||
|
})
|
||||||
|
if (linkSchema) {
|
||||||
|
schema[column] = linkSchema[linkColumn]
|
||||||
|
addType(column)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// ignore the error, couldn't get table
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enrichedSearchColumns = enrichedColumns.slice(0, 3)
|
||||||
|
enrichedSearchColumnsLoaded = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the datasource schema so we can determine column types
|
// Load the datasource schema so we can determine column types
|
||||||
|
@ -96,7 +128,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if schemaLoaded}
|
{#if schemaLoaded && enrichedSearchColumnsLoaded}
|
||||||
<Block>
|
<Block>
|
||||||
<div class={size} use:styleable={$component.styles}>
|
<div class={size} use:styleable={$component.styles}>
|
||||||
<BlockComponent type="form" bind:id={formId} props={{ dataSource }}>
|
<BlockComponent type="form" bind:id={formId} props={{ dataSource }}>
|
||||||
|
|
|
@ -146,3 +146,14 @@ describe("check manifest", () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("check full stops that are safe", () => {
|
||||||
|
it("should allow using an escaped full stop", async () => {
|
||||||
|
const data = {
|
||||||
|
"c53a4a604fa754d33baaafd5bca4d3658-YXuUBqt5vI": { "persons.firstname": "1" }
|
||||||
|
}
|
||||||
|
const template = "{{ [c53a4a604fa754d33baaafd5bca4d3658-YXuUBqt5vI].[persons.firstname] }}"
|
||||||
|
const output = await processString(template, data)
|
||||||
|
expect(output).toEqual("1")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
Loading…
Reference in New Issue