Merge pull request #3177 from Budibase/ak-fixes

Relationship aware data provider automatic reloading + extras
This commit is contained in:
Andrew Kingston 2021-10-27 15:16:39 +01:00 committed by GitHub
commit 97dd8e9989
8 changed files with 99 additions and 62 deletions

View File

@ -20,7 +20,7 @@ export const executeQuery = async ({ queryId, parameters }) => {
notificationStore.actions.error("An error has occurred")
} else if (!query.readable) {
notificationStore.actions.success("Query executed successfully")
dataSourceStore.actions.invalidateDataSource(query.datasourceId)
await dataSourceStore.actions.invalidateDataSource(query.datasourceId)
}
return res
}

View File

@ -31,7 +31,7 @@ export const saveRow = async row => {
: notificationStore.actions.success("Row saved")
// Refresh related datasources
dataSourceStore.actions.invalidateDataSource(row.tableId)
await dataSourceStore.actions.invalidateDataSource(row.tableId)
return res
}
@ -52,7 +52,7 @@ export const updateRow = async row => {
: notificationStore.actions.success("Row updated")
// Refresh related datasources
dataSourceStore.actions.invalidateDataSource(row.tableId)
await dataSourceStore.actions.invalidateDataSource(row.tableId)
return res
}
@ -76,7 +76,7 @@ export const deleteRow = async ({ tableId, rowId, revId }) => {
: notificationStore.actions.success("Row deleted")
// Refresh related datasources
dataSourceStore.actions.invalidateDataSource(tableId)
await dataSourceStore.actions.invalidateDataSource(tableId)
return res
}
@ -99,7 +99,7 @@ export const deleteRows = async ({ tableId, rows }) => {
: notificationStore.actions.success(`${rows.length} row(s) deleted`)
// Refresh related datasources
dataSourceStore.actions.invalidateDataSource(tableId)
await dataSourceStore.actions.invalidateDataSource(tableId)
return res
}

View File

@ -22,7 +22,7 @@
// Register field with form
const formApi = formContext?.formApi
const labelPosition = fieldGroupContext?.labelPosition || "above"
const labelPos = fieldGroupContext?.labelPosition || "above"
const formField = formApi?.registerField(
field,
type,
@ -38,17 +38,23 @@
fieldApi = value?.fieldApi
fieldSchema = value?.fieldSchema
})
onDestroy(() => unsubscribe && unsubscribe())
onDestroy(() => unsubscribe?.())
// Keep validation rules up to date
// Keep field state up to date with props which might change due to
// conditional UI
$: updateValidation(validation)
$: updateDisabled(disabled)
// Determine label class from position
$: labelClass = labelPos === "above" ? "" : `spectrum-FieldLabel--${labelPos}`
const updateValidation = validation => {
fieldApi?.updateValidation(validation)
}
// Extract label position from field group context
$: labelPositionClass =
labelPosition === "above" ? "" : `spectrum-FieldLabel--${labelPosition}`
const updateDisabled = disabled => {
fieldApi?.setDisabled(disabled)
}
</script>
<FieldGroupFallback>
@ -56,7 +62,7 @@
<label
class:hidden={!label}
for={fieldState?.fieldId}
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${labelPositionClass}`}
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${labelClass}`}
>
{label || ""}
</label>

View File

@ -248,10 +248,25 @@
}
}
// Updates the disabled state of a certain field
const setDisabled = fieldDisabled => {
const fieldInfo = getField(field)
// Auto columns are always disabled
const isAutoColumn = !!schema?.[field]?.autocolumn
// Update disabled state
fieldInfo.update(state => {
state.fieldState.disabled = disabled || fieldDisabled || isAutoColumn
return state
})
}
return {
setValue,
clearValue,
updateValidation,
setDisabled,
validate: () => {
// Validate the field by force setting the same value again
const { fieldState } = get(getField(field))

View File

@ -1,6 +1,6 @@
<script>
import Provider from "./Provider.svelte"
import { onMount } from "svelte"
import { onMount, onDestroy } from "svelte"
let width = window.innerWidth
let height = window.innerHeight
@ -21,12 +21,11 @@
}
onMount(() => {
const doc = document.getElementById("app-root")
resizeObserver.observe(doc)
resizeObserver.observe(document.getElementById("app-root"))
})
return () => {
resizeObserver.unobserve(doc)
}
onDestroy(() => {
resizeObserver.unobserve(document.getElementById("app-root"))
})
</script>

View File

@ -1,5 +1,5 @@
<script>
import { getContext, setContext, onMount } from "svelte"
import { getContext, setContext, onDestroy } from "svelte"
import { dataSourceStore, createContextStore } from "stores"
import { ActionTypes } from "constants"
import { generate } from "shortid"
@ -56,9 +56,9 @@
}
}
onMount(() => {
onDestroy(() => {
// Unregister all datasource instances when unmounting this provider
return () => dataSourceStore.actions.unregisterInstance(instanceId)
dataSourceStore.actions.unregisterInstance(instanceId)
})
</script>

View File

@ -8,7 +8,7 @@
</script>
<script>
import { onMount } from "svelte"
import { onMount, onDestroy } from "svelte"
import { get } from "svelte/store"
import IndicatorSet from "./IndicatorSet.svelte"
import DNDPositionIndicator from "./DNDPositionIndicator.svelte"
@ -209,18 +209,18 @@
document.addEventListener("dragenter", onDragEnter, false)
document.addEventListener("dragleave", onDragLeave, false)
document.addEventListener("drop", onDrop, false)
})
return () => {
// Events fired on the draggable target
document.removeEventListener("dragstart", onDragStart, false)
document.removeEventListener("dragend", onDragEnd, false)
onDestroy(() => {
// Events fired on the draggable target
document.removeEventListener("dragstart", onDragStart, false)
document.removeEventListener("dragend", onDragEnd, false)
// Events fired on the drop targets
document.removeEventListener("dragover", onDragOver, false)
document.removeEventListener("dragenter", onDragEnter, false)
document.removeEventListener("dragleave", onDragLeave, false)
document.removeEventListener("drop", onDrop, false)
}
// Events fired on the drop targets
document.removeEventListener("dragover", onDragOver, false)
document.removeEventListener("dragenter", onDragEnter, false)
document.removeEventListener("dragleave", onDragLeave, false)
document.removeEventListener("drop", onDrop, false)
})
</script>

View File

@ -1,4 +1,5 @@
import { writable, get } from "svelte/store"
import { fetchTableDefinition } from "../api"
export const createDataSourceStore = () => {
const store = writable([])
@ -9,43 +10,32 @@ export const createDataSourceStore = () => {
return
}
// Create a list of all relevant dataSource IDs which would require that
// this dataSource is refreshed
let dataSourceIds = []
// Extract the relevant datasource ID for this datasource
let dataSourceId = null
// Extract table ID
if (dataSource.type === "table" || dataSource.type === "view") {
if (dataSource.tableId) {
dataSourceIds.push(dataSource.tableId)
}
dataSourceId = dataSource.tableId
}
// Extract both table IDs from both sides of the relationship
// Only one side of the relationship is required as a trigger, as it will
// automatically invalidate related table IDs
else if (dataSource.type === "link") {
if (dataSource.rowTableId) {
dataSourceIds.push(dataSource.rowTableId)
}
if (dataSource.tableId) {
dataSourceIds.push(dataSource.tableId)
}
dataSourceId = dataSource.tableId || dataSource.rowTableId
}
// Extract the dataSource ID (not the query ID) for queries
else if (dataSource.type === "query") {
if (dataSource.dataSourceId) {
dataSourceIds.push(dataSource.dataSourceId)
}
dataSourceId = dataSource.dataSourceId
}
// Store configs for each relevant dataSource ID
if (dataSourceIds.length) {
if (dataSourceId) {
store.update(state => {
dataSourceIds.forEach(id => {
state.push({
dataSourceId: id,
instanceId,
refresh,
})
state.push({
dataSourceId,
instanceId,
refresh,
})
return state
})
@ -62,13 +52,10 @@ export const createDataSourceStore = () => {
// Invalidates a specific dataSource ID by refreshing all instances
// which depend on data from that dataSource
const invalidateDataSource = dataSourceId => {
const relatedInstances = get(store).filter(instance => {
return instance.dataSourceId === dataSourceId
})
relatedInstances?.forEach(instance => {
instance.refresh()
})
const invalidateDataSource = async dataSourceId => {
if (!dataSourceId) {
return
}
// Emit this as a window event, so parent screens which are iframing us in
// can also invalidate the same datasource
@ -77,6 +64,36 @@ export const createDataSourceStore = () => {
detail: { dataSourceId },
})
)
let invalidations = [dataSourceId]
// Fetch related table IDs from table schema
const definition = await fetchTableDefinition(dataSourceId)
const schema = definition?.schema
if (schema) {
Object.values(schema).forEach(fieldSchema => {
if (
fieldSchema.type === "link" &&
fieldSchema.tableId &&
!fieldSchema.autocolumn
) {
invalidations.push(fieldSchema.tableId)
}
})
}
// Remove any dupes
invalidations = [...new Set(invalidations)]
// Invalidate all sources
invalidations.forEach(id => {
const relatedInstances = get(store).filter(instance => {
return instance.dataSourceId === id
})
relatedInstances?.forEach(instance => {
instance.refresh()
})
})
}
return {