import { writable, get } from "svelte/store" import { API } from "api" import { FieldTypes } from "../constants" import { routeStore } from "./routes" export const createDataSourceStore = () => { const store = writable([]) // Registers a new dataSource instance const registerDataSource = (dataSource, instanceId, refresh) => { if (!dataSource || !instanceId || !refresh) { return } // Extract the relevant datasource ID for this datasource let dataSourceId = null // Extract table ID if (dataSource.type === "table" || dataSource.type === "view") { dataSourceId = dataSource.tableId } // Only one side of the relationship is required as a trigger, as it will // automatically invalidate related table IDs else if (dataSource.type === FieldTypes.LINK) { dataSourceId = dataSource.tableId || dataSource.rowTableId } // Extract the dataSource ID (not the query ID) for queries else if (dataSource.type === "query") { dataSourceId = dataSource.dataSourceId } // Store configs for each relevant dataSource ID if (dataSourceId) { store.update(state => { state.push({ dataSourceId, instanceId, refresh, }) return state }) } } // Removes all registered dataSource instances belonging to a particular // instance ID const unregisterInstance = instanceId => { store.update(state => { return state.filter(instance => instance.instanceId !== instanceId) }) } // Invalidates a specific dataSource ID by refreshing all instances // which depend on data from that dataSource const invalidateDataSource = async (dataSourceId, options) => { if (!dataSourceId) { return } // Merge default options options = { invalidateRelationships: false, ...options, } // Emit this as a window event, so parent screens which are iframing us in // can also invalidate the same datasource const inModal = get(routeStore).queryParams?.peek if (inModal) { window.parent.postMessage({ type: "invalidate-datasource", detail: { dataSourceId, options }, }) } let invalidations = [dataSourceId] // Fetch related table IDs from table schema let schema if (options.invalidateRelationships) { try { const definition = await API.fetchTableDefinition(dataSourceId) schema = definition?.schema } catch (error) { schema = null } } if (schema) { Object.values(schema).forEach(fieldSchema => { if ( fieldSchema.type === FieldTypes.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 { subscribe: store.subscribe, actions: { registerDataSource, unregisterInstance, invalidateDataSource }, } } export const dataSourceStore = createDataSourceStore()