1
0
mirror of https://gitlab.com/bramw/baserow.git synced 2024-11-21 23:37:55 +00:00
bramw_baserow/web-frontend/modules/builder/store/dataSource.js
2024-10-17 15:49:51 +00:00

409 lines
12 KiB
JavaScript

import DataSourceService from '@baserow/modules/builder/services/dataSource'
import PublishedBuilderService from '@baserow/modules/builder/services/publishedBuilder'
const state = {}
const updateContext = {
updateTimeout: null,
promiseResolve: null,
lastUpdatedValues: null,
valuesToUpdate: {},
}
const mutations = {
ADD_ITEM(state, { page, dataSource, beforeId = null }) {
if (beforeId === null) {
page.dataSources.push(dataSource)
} else {
const insertionIndex = page.dataSources.findIndex(
(e) => e.id === beforeId
)
page.dataSources.splice(insertionIndex, 0, dataSource)
}
},
UPDATE_ITEM(state, { page, dataSource: dataSourceToUpdate, values }) {
const index = page.dataSources.findIndex(
(dataSource) => dataSource.id === dataSourceToUpdate.id
)
page.dataSources.splice(index, 1, {
...page.dataSources[index],
...values,
})
},
FULL_UPDATE_ITEM(state, { page, dataSource: dataSourceToUpdate, values }) {
const index = page.dataSources.findIndex(
(dataSource) => dataSource.id === dataSourceToUpdate.id
)
page.dataSources.splice(index, 1, {
...values,
})
},
DELETE_ITEM(state, { page, dataSourceId }) {
const index = page.dataSources.findIndex(
(dataSource) => dataSource.id === dataSourceId
)
if (index > -1) {
page.dataSources.splice(index, 1)
}
},
MOVE_ITEM(state, { page, index, oldIndex }) {
page.dataSources.splice(index, 0, page.dataSources.splice(oldIndex, 1)[0])
},
MOVE_ITEM_PAGE(state, { pageSource, pageDest, dataSourceId }) {
const index = pageSource.dataSources.findIndex(
(dataSource) => dataSource.id === dataSourceId
)
if (index > -1) {
const moved = structuredClone(pageSource.dataSources[index])
moved.page_id = pageDest.id
pageSource.dataSources.splice(index, 1)
pageDest.dataSources.push(moved)
}
},
CLEAR_ITEMS(state, { page }) {
page.dataSources = []
},
SET_LOADING(state, { page, value }) {
page._.dataSourceLoading = value
},
}
const actions = {
forceCreate({ commit }, { page, dataSource, beforeId = null }) {
commit('ADD_ITEM', { page, dataSource, beforeId })
},
forceUpdate({ commit, dispatch }, { page, dataSource, values }) {
commit('UPDATE_ITEM', { page, dataSource, values })
},
forceDelete({ commit, dispatch }, { page, dataSourceId }) {
commit('DELETE_ITEM', { page, dataSourceId })
},
forceMove({ commit, getters }, { page, dataSourceId, beforeDataSourceId }) {
const currentOrder = getters
.getPageDataSources(page)
.map((dataSource) => dataSource.id)
const oldIndex = currentOrder.findIndex((id) => id === dataSourceId)
const index = beforeDataSourceId
? currentOrder.findIndex((id) => id === beforeDataSourceId)
: getters.getPageDataSources(page).length
// If the dataSource is before the beforeDataSource we must decrease the target index by
// one to compensate the removed dataSource.
if (oldIndex < index) {
commit('MOVE_ITEM', { page, index: index - 1, oldIndex })
} else {
commit('MOVE_ITEM', { page, index, oldIndex })
}
},
forcePageMove({ commit, getters }, { pageDest, pageSource, dataSourceId }) {
commit('MOVE_ITEM_PAGE', { pageSource, pageDest, dataSourceId })
},
async create({ commit, dispatch }, { page, values, beforeId }) {
commit('SET_LOADING', { page, value: true })
const { data: dataSource } = await DataSourceService(this.$client).create(
page.id,
values,
beforeId
)
await dispatch('forceCreate', { page, dataSource, beforeId })
commit('SET_LOADING', { page, value: false })
return dataSource
},
async update({ commit, dispatch, getters }, { page, dataSourceId, values }) {
const dataSourcesOfPage = getters.getPageDataSources(page)
const dataSource = dataSourcesOfPage.find(
(dataSource) => dataSource.id === dataSourceId
)
const oldValues = {}
const newValues = {}
Object.keys(values).forEach((name) => {
if (Object.prototype.hasOwnProperty.call(dataSource, name)) {
oldValues[name] = dataSource[name]
newValues[name] = values[name]
}
})
await dispatch('forceUpdate', { page, dataSource, values: newValues })
commit('SET_LOADING', { page, value: true })
try {
const { data: updatedDataSource } = await DataSourceService(
this.$client
).update(dataSource.id, values)
await dispatch('forceUpdate', {
page,
dataSource,
values: updatedDataSource,
})
} catch (error) {
await dispatch('forceUpdate', { page, dataSource, values: oldValues })
throw error
}
commit('SET_LOADING', { page, value: false })
},
async debouncedUpdate(
{ dispatch, getters, commit },
{ page, dataSourceId, values }
) {
const dataSourcesOfPage = getters.getPageDataSources(page)
const dataSource = dataSourcesOfPage.find(
(dataSource) => dataSource.id === dataSourceId
)
const oldValues = {}
Object.keys(values).forEach((name) => {
if (Object.prototype.hasOwnProperty.call(dataSource, name)) {
oldValues[name] = dataSource[name]
// Accumulate the changed values to send all the ongoing changes with the
// final request
updateContext.valuesToUpdate[name] = values[name]
}
})
// If we have a dataSource type, fetch it from the service type registry
// then call the registry's `beforeUpdate` hook to optionally manipulate
// the values prior to performing an update.
if (dataSource.type !== null) {
const dataSourceType = this.$registry.get('service', dataSource.type)
updateContext.valuesToUpdate = dataSourceType.beforeUpdate(
updateContext.valuesToUpdate,
oldValues
)
}
await dispatch('forceUpdate', {
page,
dataSource,
values: updateContext.valuesToUpdate,
})
return new Promise((resolve, reject) => {
const fire = async () => {
const toUpdate = updateContext.valuesToUpdate
updateContext.valuesToUpdate = {}
commit('SET_LOADING', { page, value: true })
try {
const { data } = await DataSourceService(this.$client).update(
dataSource.id,
toUpdate
)
await commit('FULL_UPDATE_ITEM', { page, dataSource, values: data })
updateContext.lastUpdatedValues = null
resolve()
} catch (error) {
// Revert to old values on error
await dispatch('forceUpdate', {
page,
dataSource,
values: updateContext.lastUpdatedValues,
})
updateContext.lastUpdatedValues = null
reject(error)
}
commit('SET_LOADING', { page, value: false })
}
if (updateContext.promiseResolve) {
updateContext.promiseResolve()
updateContext.promiseResolve = null
}
clearTimeout(updateContext.updateTimeout)
if (!updateContext.lastUpdatedValues) {
updateContext.lastUpdatedValues = oldValues
}
updateContext.updateTimeout = setTimeout(fire, 500)
updateContext.promiseResolve = resolve
})
},
async moveToPage(
{ commit, dispatch, getters },
{ pageSource, pageDest, dataSourceId }
) {
const dataSourcesOfSourcePage = getters.getPageDataSources(pageSource)
const dataSource = dataSourcesOfSourcePage.find(
(dataSource) => dataSource.id === dataSourceId
)
await dispatch('forcePageMove', {
pageDest,
pageSource,
dataSourceId: dataSource.id,
})
commit('SET_LOADING', { page: pageSource, value: true })
try {
const { data: updatedDataSource } = await DataSourceService(
this.$client
).update(dataSource.id, {
page_id: pageDest.id,
})
// Update the order and the name if it has been updated to prevent conflicts
await dispatch('forceUpdate', {
page: pageDest,
dataSource: updatedDataSource,
values: {
order: updatedDataSource.order,
name: updatedDataSource.name,
},
})
} catch (error) {
await dispatch('forcePageMove', {
pageDest: pageSource,
pageSource: pageDest,
dataSourceId: dataSource.id,
})
throw error
}
commit('SET_LOADING', { page: pageSource, value: false })
},
async delete({ commit, dispatch, getters }, { page, dataSourceId }) {
const dataSourcesOfPage = getters.getPageDataSources(page)
const dataSourceIndex = dataSourcesOfPage.findIndex(
(dataSource) => dataSource.id === dataSourceId
)
const dataSourceToDelete = dataSourcesOfPage[dataSourceIndex]
const beforeId =
dataSourceIndex !== dataSourcesOfPage.length - 1
? dataSourcesOfPage[dataSourceIndex + 1].id
: null
await dispatch('forceDelete', { page, dataSourceId })
commit('SET_LOADING', { page, value: true })
try {
await DataSourceService(this.$client).delete(dataSourceId)
} catch (error) {
await dispatch('forceCreate', {
page,
dataSource: dataSourceToDelete,
beforeId,
})
throw error
}
// After deleting the data source, find all collection elements
// which use this data source, and clear their element content.
const dataSourceCollectionElements = page.elements.filter((element) => {
return element.data_source_id === dataSourceToDelete.id
})
dataSourceCollectionElements.map(async (collectionElement) => {
await dispatch(
'elementContent/clearElementContent',
{
element: collectionElement,
},
{ root: true }
)
})
commit('SET_LOADING', { page, value: false })
},
async fetch({ dispatch, commit }, { page }) {
commit('SET_LOADING', { page, value: true })
dispatch(
'dataSourceContent/clearDataSourceContents',
{ page },
{ root: true }
)
const { data: dataSources } = await DataSourceService(
this.$client
).fetchAll(page.id)
commit('CLEAR_ITEMS', { page })
await Promise.all(
dataSources.map((dataSource) =>
dispatch('forceCreate', { page, dataSource })
)
)
commit('SET_LOADING', { page, value: false })
return dataSources
},
async fetchPublished({ dispatch, commit }, { page }) {
commit('SET_LOADING', { page, value: true })
dispatch(
'dataSourceContent/clearDataSourceContents',
{ page },
{ root: true }
)
const { data: dataSources } = await PublishedBuilderService(
this.$client
).fetchDataSources(page.id)
commit('CLEAR_ITEMS', { page })
await Promise.all(
dataSources.map((dataSource) =>
dispatch('forceCreate', { page, dataSource })
)
)
commit('SET_LOADING', { page, value: false })
return dataSources
},
async move({ dispatch }, { page, dataSourceId, beforeDataSourceId }) {
await dispatch('forceMove', { page, dataSourceId, beforeDataSourceId })
try {
await DataSourceService(this.$client).move(
dataSourceId,
beforeDataSourceId
)
} catch (error) {
await dispatch('forceMove', {
page,
dataSourceId: beforeDataSourceId,
beforeDataSourceId: dataSourceId,
})
throw error
}
},
async duplicate({ commit, getters, dispatch }, { page, dataSourceId }) {
const dataSourcesOfPage = getters.getPageDataSources(page)
const dataSource = dataSourcesOfPage.find((e) => e.id === dataSourceId)
commit('SET_LOADING', { page, value: true })
await dispatch('create', {
page,
dataSourceType: dataSource.type,
beforeId: dataSource.id,
})
commit('SET_LOADING', { page, value: false })
},
}
const getters = {
getPageDataSources: (state) => (page) => {
return page.dataSources
},
getPagesDataSources: (state) => (pages) => {
return pages.map(({ dataSources }) => dataSources).flat()
},
getPagesDataSourceById: (state, getters) => (pages, id) => {
return getters
.getPagesDataSources(pages)
.find((dataSource) => dataSource.id === id)
},
getPageDataSourceById: (state, getters) => (page, id) => {
return getters.getPagesDataSourceById([page], id)
},
getLoading: (state) => (page) => {
return page._.dataSourceLoading
},
}
export default {
namespaced: true,
state,
getters,
actions,
mutations,
}