Enrich datasources so that data can be correctly fetched reactively using only the datasource definition
This commit is contained in:
parent
ce6d89bc5c
commit
9056b0e49d
|
@ -37,6 +37,7 @@
|
||||||
$: queries = $backendUiStore.queries.map(query => ({
|
$: queries = $backendUiStore.queries.map(query => ({
|
||||||
label: query.name,
|
label: query.name,
|
||||||
name: query.name,
|
name: query.name,
|
||||||
|
tableId: query._id,
|
||||||
...query,
|
...query,
|
||||||
schema: query.schema,
|
schema: query.schema,
|
||||||
parameters: query.parameters,
|
parameters: query.parameters,
|
||||||
|
@ -59,9 +60,12 @@
|
||||||
providerId: property.providerId,
|
providerId: property.providerId,
|
||||||
label: property.readableBinding,
|
label: property.readableBinding,
|
||||||
fieldName: property.fieldSchema.name,
|
fieldName: property.fieldSchema.name,
|
||||||
name: `all_${property.fieldSchema.tableId}`,
|
|
||||||
tableId: property.fieldSchema.tableId,
|
tableId: property.fieldSchema.tableId,
|
||||||
type: "link",
|
type: "link",
|
||||||
|
// These properties will be enriched by the client library and provide
|
||||||
|
// details of the parent row of the relationship field, from context
|
||||||
|
rowId: `{{ ${property.providerId}._id }}`,
|
||||||
|
rowTableId: `{{ ${property.providerId}.tableId }}`,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -84,12 +88,12 @@
|
||||||
class="dropdownbutton"
|
class="dropdownbutton"
|
||||||
bind:this={anchorRight}
|
bind:this={anchorRight}
|
||||||
on:click={dropdownRight.show}>
|
on:click={dropdownRight.show}>
|
||||||
<span>{value.label ? value.label : 'Table / View / Query'}</span>
|
<span>{value?.label ? value.label : 'Choose option'}</span>
|
||||||
<Icon name="arrowdown" />
|
<Icon name="arrowdown" />
|
||||||
</div>
|
</div>
|
||||||
{#if value.type === 'query'}
|
{#if value?.type === 'query'}
|
||||||
<i class="ri-settings-5-line" on:click={drawer.show} />
|
<i class="ri-settings-5-line" on:click={drawer.show} />
|
||||||
<Drawer title={'Query'}>
|
<Drawer title={'Query'} bind:this={drawer}>
|
||||||
<div slot="buttons">
|
<div slot="buttons">
|
||||||
<Button
|
<Button
|
||||||
blue
|
blue
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
import { get } from "svelte/store"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { fetchTableData } from "./tables"
|
import { fetchTableData } from "./tables"
|
||||||
import { fetchViewData } from "./views"
|
import { fetchViewData } from "./views"
|
||||||
import { fetchRelationshipData } from "./relationships"
|
import { fetchRelationshipData } from "./relationships"
|
||||||
import { executeQuery } from "./queries"
|
import { executeQuery } from "./queries"
|
||||||
import { enrichRows } from "./rows"
|
import { enrichRows } from "./rows"
|
||||||
import { enrichDataBindings } from "../utils/enrichDataBinding"
|
|
||||||
import { bindingStore } from "../store/binding"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches all rows for a particular Budibase data source.
|
* Fetches all rows for a particular Budibase data source.
|
||||||
*/
|
*/
|
||||||
export const fetchDatasource = async (datasource, dataContext) => {
|
export const fetchDatasource = async datasource => {
|
||||||
if (!datasource || !datasource.type) {
|
if (!datasource || !datasource.type) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -23,28 +21,22 @@ export const fetchDatasource = async (datasource, dataContext) => {
|
||||||
} else if (type === "view") {
|
} else if (type === "view") {
|
||||||
rows = await fetchViewData(datasource)
|
rows = await fetchViewData(datasource)
|
||||||
} else if (type === "query") {
|
} else if (type === "query") {
|
||||||
const bindings = get(bindingStore)
|
|
||||||
|
|
||||||
// Set the default query params
|
// Set the default query params
|
||||||
let queryParams = datasource.queryParams || {}
|
let parameters = cloneDeep(datasource.queryParams || {})
|
||||||
for (let param of datasource.parameters) {
|
for (let param of datasource.parameters) {
|
||||||
if (!queryParams[param.name]) {
|
if (!parameters[param.name]) {
|
||||||
queryParams[param.name] = param.default
|
parameters[param.name] = param.default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const parameters = enrichDataBindings(queryParams, {
|
|
||||||
...bindings,
|
|
||||||
...dataContext,
|
|
||||||
})
|
|
||||||
return await executeQuery({ queryId: datasource._id, parameters })
|
return await executeQuery({ queryId: datasource._id, parameters })
|
||||||
} else if (type === "link") {
|
} else if (type === "link") {
|
||||||
const row = dataContext[datasource.providerId]
|
|
||||||
rows = await fetchRelationshipData({
|
rows = await fetchRelationshipData({
|
||||||
rowId: row?._id,
|
rowId: datasource.rowId,
|
||||||
tableId: row?.tableId,
|
tableId: datasource.rowTableId,
|
||||||
fieldName,
|
fieldName,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Enrich rows
|
|
||||||
|
// Enrich rows so they can displayed properly
|
||||||
return await enrichRows(rows, tableId)
|
return await enrichRows(rows, tableId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { cloneDeep } from "lodash/fp"
|
||||||
import mustache from "mustache"
|
import mustache from "mustache"
|
||||||
|
|
||||||
// this is a much more liberal version of mustache's escape function
|
// this is a much more liberal version of mustache's escape function
|
||||||
|
@ -35,12 +36,33 @@ export const enrichDataBinding = (input, context) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enriches each prop in a props object
|
* Recursively enriches all props in a props object and returns the new props.
|
||||||
|
* Props are deeply cloned so that no mutation is done to the source object.
|
||||||
*/
|
*/
|
||||||
export const enrichDataBindings = (props, context) => {
|
export const enrichDataBindings = (props, context) => {
|
||||||
let enrichedProps = {}
|
let clonedProps = cloneDeep(props)
|
||||||
Object.entries(props).forEach(([key, value]) => {
|
recursiveEnrich(clonedProps, context)
|
||||||
enrichedProps[key] = enrichDataBinding(value, context)
|
return clonedProps
|
||||||
})
|
}
|
||||||
return enrichedProps
|
|
||||||
|
/**
|
||||||
|
* Recurses through an object and enriches all string props found.
|
||||||
|
*/
|
||||||
|
const recursiveEnrich = (props, context) => {
|
||||||
|
if (typeof props !== "object") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let keys = []
|
||||||
|
if (Array.isArray(props)) {
|
||||||
|
keys = Array.from(props.keys())
|
||||||
|
} else if (typeof props === "object") {
|
||||||
|
keys = Object.keys(props || {})
|
||||||
|
}
|
||||||
|
keys.forEach(key => {
|
||||||
|
if (typeof props[key] === "string") {
|
||||||
|
props[key] = enrichDataBinding(props[key], context)
|
||||||
|
} else {
|
||||||
|
recursiveEnrich(props[key], context)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,21 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, onMount } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import { isEmpty } from "lodash/fp"
|
import { isEmpty } from "lodash/fp"
|
||||||
|
|
||||||
const { API, styleable, DataProvider } = getContext("sdk")
|
const { API, styleable, DataProvider } = getContext("sdk")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
const dataContext = getContext("data")
|
|
||||||
|
|
||||||
export let datasource = []
|
export let datasource = []
|
||||||
|
|
||||||
let rows = []
|
let rows = []
|
||||||
|
|
||||||
$: datasource && fetchData()
|
$: fetchData(datasource)
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData(datasource) {
|
||||||
rows = await API.fetchDatasource(datasource, $dataContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
if (!isEmpty(datasource)) {
|
if (!isEmpty(datasource)) {
|
||||||
fetchData()
|
rows = await API.fetchDatasource(datasource)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div use:styleable={$component.styles}>
|
<div use:styleable={$component.styles}>
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
|
|
||||||
// Fetch, filter and sort data
|
// Fetch, filter and sort data
|
||||||
const schema = (await API.fetchTableDefinition(datasource.tableId)).schema
|
const schema = (await API.fetchTableDefinition(datasource.tableId)).schema
|
||||||
const result = await API.fetchDatasource(datasource, $dataContext)
|
const result = await API.fetchDatasource(datasource)
|
||||||
const reducer = row => (valid, column) => valid && row[column] != null
|
const reducer = row => (valid, column) => valid && row[column] != null
|
||||||
const hasAllColumns = row => allCols.reduce(reducer(row), true)
|
const hasAllColumns = row => allCols.reduce(reducer(row), true)
|
||||||
const data = result
|
const data = result
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
// Fetch, filter and sort data
|
// Fetch, filter and sort data
|
||||||
const schema = (await API.fetchTableDefinition(datasource.tableId)).schema
|
const schema = (await API.fetchTableDefinition(datasource.tableId)).schema
|
||||||
const result = await API.fetchDatasource(datasource, $dataContext)
|
const result = await API.fetchDatasource(datasource)
|
||||||
const reducer = row => (valid, column) => valid && row[column] != null
|
const reducer = row => (valid, column) => valid && row[column] != null
|
||||||
const hasAllColumns = row => allCols.reduce(reducer(row), true)
|
const hasAllColumns = row => allCols.reduce(reducer(row), true)
|
||||||
const data = result
|
const data = result
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
|
|
||||||
// Fetch, filter and sort data
|
// Fetch, filter and sort data
|
||||||
const schema = (await API.fetchTableDefinition(datasource.tableId)).schema
|
const schema = (await API.fetchTableDefinition(datasource.tableId)).schema
|
||||||
const result = await API.fetchDatasource(datasource, $dataContext)
|
const result = await API.fetchDatasource(datasource)
|
||||||
const reducer = row => (valid, column) => valid && row[column] != null
|
const reducer = row => (valid, column) => valid && row[column] != null
|
||||||
const hasAllColumns = row => allCols.reduce(reducer(row), true)
|
const hasAllColumns = row => allCols.reduce(reducer(row), true)
|
||||||
const data = result
|
const data = result
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
// Fetch, filter and sort data
|
// Fetch, filter and sort data
|
||||||
const schema = (await API.fetchTableDefinition(datasource.tableId)).schema
|
const schema = (await API.fetchTableDefinition(datasource.tableId)).schema
|
||||||
const result = await API.fetchDatasource(datasource, $dataContext)
|
const result = await API.fetchDatasource(datasource)
|
||||||
const data = result
|
const data = result
|
||||||
.filter(row => row[labelColumn] != null && row[valueColumn] != null)
|
.filter(row => row[labelColumn] != null && row[valueColumn] != null)
|
||||||
.slice(0, 20)
|
.slice(0, 20)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import { number } from "./valueSetters"
|
import { number } from "./valueSetters"
|
||||||
import { getRenderer } from "./customRenderer"
|
import { getRenderer } from "./customRenderer"
|
||||||
import { isEmpty } from "lodash/fp"
|
import { isEmpty } from "lodash/fp"
|
||||||
import { getContext, onMount } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import AgGrid from "@budibase/svelte-ag-grid"
|
import AgGrid from "@budibase/svelte-ag-grid"
|
||||||
import {
|
import {
|
||||||
TextButton as DeleteButton,
|
TextButton as DeleteButton,
|
||||||
|
@ -34,6 +34,7 @@
|
||||||
["--grid-height"]: `${height}px`,
|
["--grid-height"]: `${height}px`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
$: fetchData(datasource)
|
||||||
|
|
||||||
// These can never change at runtime so don't need to be reactive
|
// These can never change at runtime so don't need to be reactive
|
||||||
let canEdit = editable && datasource && datasource.type !== "view"
|
let canEdit = editable && datasource && datasource.type !== "view"
|
||||||
|
@ -57,8 +58,11 @@
|
||||||
pagination,
|
pagination,
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData(datasource) {
|
||||||
data = await API.fetchDatasource(datasource, $dataContext)
|
if (isEmpty(datasource)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data = await API.fetchDatasource(datasource)
|
||||||
|
|
||||||
let schema
|
let schema
|
||||||
|
|
||||||
|
@ -115,12 +119,6 @@
|
||||||
dataLoaded = true
|
dataLoaded = true
|
||||||
}
|
}
|
||||||
|
|
||||||
$: datasource && fetchData()
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
if (!isEmpty(datasource)) fetchData()
|
|
||||||
})
|
|
||||||
|
|
||||||
const shouldHideField = name => {
|
const shouldHideField = name => {
|
||||||
if (name.startsWith("_")) return true
|
if (name.startsWith("_")) return true
|
||||||
// always 'row'
|
// always 'row'
|
||||||
|
|
Loading…
Reference in New Issue