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") notificationStore.actions.error("An error has occurred")
} else if (!query.readable) { } else if (!query.readable) {
notificationStore.actions.success("Query executed successfully") notificationStore.actions.success("Query executed successfully")
dataSourceStore.actions.invalidateDataSource(query.datasourceId) await dataSourceStore.actions.invalidateDataSource(query.datasourceId)
} }
return res return res
} }

View File

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

View File

@ -22,7 +22,7 @@
// Register field with form // Register field with form
const formApi = formContext?.formApi const formApi = formContext?.formApi
const labelPosition = fieldGroupContext?.labelPosition || "above" const labelPos = fieldGroupContext?.labelPosition || "above"
const formField = formApi?.registerField( const formField = formApi?.registerField(
field, field,
type, type,
@ -38,17 +38,23 @@
fieldApi = value?.fieldApi fieldApi = value?.fieldApi
fieldSchema = value?.fieldSchema 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) $: updateValidation(validation)
$: updateDisabled(disabled)
// Determine label class from position
$: labelClass = labelPos === "above" ? "" : `spectrum-FieldLabel--${labelPos}`
const updateValidation = validation => { const updateValidation = validation => {
fieldApi?.updateValidation(validation) fieldApi?.updateValidation(validation)
} }
// Extract label position from field group context const updateDisabled = disabled => {
$: labelPositionClass = fieldApi?.setDisabled(disabled)
labelPosition === "above" ? "" : `spectrum-FieldLabel--${labelPosition}` }
</script> </script>
<FieldGroupFallback> <FieldGroupFallback>
@ -56,7 +62,7 @@
<label <label
class:hidden={!label} class:hidden={!label}
for={fieldState?.fieldId} 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 || ""}
</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 { return {
setValue, setValue,
clearValue, clearValue,
updateValidation, updateValidation,
setDisabled,
validate: () => { validate: () => {
// Validate the field by force setting the same value again // Validate the field by force setting the same value again
const { fieldState } = get(getField(field)) const { fieldState } = get(getField(field))

View File

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

View File

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

View File

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

View File

@ -1,4 +1,5 @@
import { writable, get } from "svelte/store" import { writable, get } from "svelte/store"
import { fetchTableDefinition } from "../api"
export const createDataSourceStore = () => { export const createDataSourceStore = () => {
const store = writable([]) const store = writable([])
@ -9,44 +10,33 @@ export const createDataSourceStore = () => {
return return
} }
// Create a list of all relevant dataSource IDs which would require that // Extract the relevant datasource ID for this datasource
// this dataSource is refreshed let dataSourceId = null
let dataSourceIds = []
// Extract table ID // Extract table ID
if (dataSource.type === "table" || dataSource.type === "view") { if (dataSource.type === "table" || dataSource.type === "view") {
if (dataSource.tableId) { dataSourceId = dataSource.tableId
dataSourceIds.push(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") { else if (dataSource.type === "link") {
if (dataSource.rowTableId) { dataSourceId = dataSource.tableId || dataSource.rowTableId
dataSourceIds.push(dataSource.rowTableId)
}
if (dataSource.tableId) {
dataSourceIds.push(dataSource.tableId)
}
} }
// Extract the dataSource ID (not the query ID) for queries // Extract the dataSource ID (not the query ID) for queries
else if (dataSource.type === "query") { else if (dataSource.type === "query") {
if (dataSource.dataSourceId) { dataSourceId = dataSource.dataSourceId
dataSourceIds.push(dataSource.dataSourceId)
}
} }
// Store configs for each relevant dataSource ID // Store configs for each relevant dataSource ID
if (dataSourceIds.length) { if (dataSourceId) {
store.update(state => { store.update(state => {
dataSourceIds.forEach(id => {
state.push({ state.push({
dataSourceId: id, dataSourceId,
instanceId, instanceId,
refresh, refresh,
}) })
})
return state return state
}) })
} }
@ -62,13 +52,10 @@ export const createDataSourceStore = () => {
// Invalidates a specific dataSource ID by refreshing all instances // Invalidates a specific dataSource ID by refreshing all instances
// which depend on data from that dataSource // which depend on data from that dataSource
const invalidateDataSource = dataSourceId => { const invalidateDataSource = async dataSourceId => {
const relatedInstances = get(store).filter(instance => { if (!dataSourceId) {
return instance.dataSourceId === dataSourceId return
}) }
relatedInstances?.forEach(instance => {
instance.refresh()
})
// Emit this as a window event, so parent screens which are iframing us in // Emit this as a window event, so parent screens which are iframing us in
// can also invalidate the same datasource // can also invalidate the same datasource
@ -77,6 +64,36 @@ export const createDataSourceStore = () => {
detail: { dataSourceId }, 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 { return {