Getting client partially working, having an issue with search fields not updating for a table block.

This commit is contained in:
mike12345567 2022-01-14 17:42:14 +00:00
parent 479220652e
commit aae2dc86d3
8 changed files with 144 additions and 36 deletions

View File

@ -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"]

View File

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

View File

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

View File

@ -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",
]

View File

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

View File

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

View File

@ -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
}
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,
}) })
return enrichedColumns.slice(0, 3) 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 }}>

View File

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