Merge pull request #3177 from Budibase/ak-fixes
Relationship aware data provider automatic reloading + extras
This commit is contained in:
commit
97dd8e9989
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue