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 => ({
|
||||
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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Reference in New Issue