Enrich datasources so that data can be correctly fetched reactively using only the datasource definition

This commit is contained in:
Andrew Kingston 2021-01-21 10:42:14 +00:00
parent ce6d89bc5c
commit 9056b0e49d
9 changed files with 61 additions and 50 deletions

View File

@ -37,6 +37,7 @@
$: queries = $backendUiStore.queries.map(query => ({
label: query.name,
name: query.name,
tableId: query._id,
...query,
schema: query.schema,
parameters: query.parameters,
@ -59,9 +60,12 @@
providerId: property.providerId,
label: property.readableBinding,
fieldName: property.fieldSchema.name,
name: `all_${property.fieldSchema.tableId}`,
tableId: property.fieldSchema.tableId,
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"
bind:this={anchorRight}
on:click={dropdownRight.show}>
<span>{value.label ? value.label : 'Table / View / Query'}</span>
<span>{value?.label ? value.label : 'Choose option'}</span>
<Icon name="arrowdown" />
</div>
{#if value.type === 'query'}
{#if value?.type === 'query'}
<i class="ri-settings-5-line" on:click={drawer.show} />
<Drawer title={'Query'}>
<Drawer title={'Query'} bind:this={drawer}>
<div slot="buttons">
<Button
blue

View File

@ -1,16 +1,14 @@
import { get } from "svelte/store"
import { cloneDeep } from "lodash/fp"
import { fetchTableData } from "./tables"
import { fetchViewData } from "./views"
import { fetchRelationshipData } from "./relationships"
import { executeQuery } from "./queries"
import { enrichRows } from "./rows"
import { enrichDataBindings } from "../utils/enrichDataBinding"
import { bindingStore } from "../store/binding"
/**
* Fetches all rows for a particular Budibase data source.
*/
export const fetchDatasource = async (datasource, dataContext) => {
export const fetchDatasource = async datasource => {
if (!datasource || !datasource.type) {
return []
}
@ -23,28 +21,22 @@ export const fetchDatasource = async (datasource, dataContext) => {
} else if (type === "view") {
rows = await fetchViewData(datasource)
} else if (type === "query") {
const bindings = get(bindingStore)
// Set the default query params
let queryParams = datasource.queryParams || {}
let parameters = cloneDeep(datasource.queryParams || {})
for (let param of datasource.parameters) {
if (!queryParams[param.name]) {
queryParams[param.name] = param.default
if (!parameters[param.name]) {
parameters[param.name] = param.default
}
}
const parameters = enrichDataBindings(queryParams, {
...bindings,
...dataContext,
})
return await executeQuery({ queryId: datasource._id, parameters })
} else if (type === "link") {
const row = dataContext[datasource.providerId]
rows = await fetchRelationshipData({
rowId: row?._id,
tableId: row?.tableId,
rowId: datasource.rowId,
tableId: datasource.rowTableId,
fieldName,
})
}
// Enrich rows
// Enrich rows so they can displayed properly
return await enrichRows(rows, tableId)
}

View File

@ -1,3 +1,4 @@
import { cloneDeep } from "lodash/fp"
import mustache from "mustache"
// 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) => {
let enrichedProps = {}
Object.entries(props).forEach(([key, value]) => {
enrichedProps[key] = enrichDataBinding(value, context)
})
return enrichedProps
let clonedProps = cloneDeep(props)
recursiveEnrich(clonedProps, context)
return clonedProps
}
/**
* 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)
}
})
}

View File

@ -1,26 +1,21 @@
<script>
import { getContext, onMount } from "svelte"
import { getContext } from "svelte"
import { isEmpty } from "lodash/fp"
const { API, styleable, DataProvider } = getContext("sdk")
const component = getContext("component")
const dataContext = getContext("data")
export let datasource = []
let rows = []
$: datasource && fetchData()
$: fetchData(datasource)
async function fetchData() {
rows = await API.fetchDatasource(datasource, $dataContext)
}
onMount(async () => {
async function fetchData(datasource) {
if (!isEmpty(datasource)) {
fetchData()
rows = await API.fetchDatasource(datasource)
}
})
}
</script>
<div use:styleable={$component.styles}>

View File

@ -35,7 +35,7 @@
// Fetch, filter and sort data
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 hasAllColumns = row => allCols.reduce(reducer(row), true)
const data = result

View File

@ -33,7 +33,7 @@
// Fetch, filter and sort data
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 hasAllColumns = row => allCols.reduce(reducer(row), true)
const data = result

View File

@ -41,7 +41,7 @@
// Fetch, filter and sort data
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 hasAllColumns = row => allCols.reduce(reducer(row), true)
const data = result

View File

@ -31,7 +31,7 @@
// Fetch, filter and sort data
const schema = (await API.fetchTableDefinition(datasource.tableId)).schema
const result = await API.fetchDatasource(datasource, $dataContext)
const result = await API.fetchDatasource(datasource)
const data = result
.filter(row => row[labelColumn] != null && row[valueColumn] != null)
.slice(0, 20)

View File

@ -3,7 +3,7 @@
import { number } from "./valueSetters"
import { getRenderer } from "./customRenderer"
import { isEmpty } from "lodash/fp"
import { getContext, onMount } from "svelte"
import { getContext } from "svelte"
import AgGrid from "@budibase/svelte-ag-grid"
import {
TextButton as DeleteButton,
@ -34,6 +34,7 @@
["--grid-height"]: `${height}px`,
},
}
$: fetchData(datasource)
// These can never change at runtime so don't need to be reactive
let canEdit = editable && datasource && datasource.type !== "view"
@ -57,8 +58,11 @@
pagination,
}
async function fetchData() {
data = await API.fetchDatasource(datasource, $dataContext)
async function fetchData(datasource) {
if (isEmpty(datasource)) {
return
}
data = await API.fetchDatasource(datasource)
let schema
@ -115,12 +119,6 @@
dataLoaded = true
}
$: datasource && fetchData()
onMount(() => {
if (!isEmpty(datasource)) fetchData()
})
const shouldHideField = name => {
if (name.startsWith("_")) return true
// always 'row'