Ensure 'table' type settings are migrated and handled properly. Allow deleting rows from views

This commit is contained in:
Andrew Kingston 2023-08-24 15:50:57 +01:00
parent b6e675e3ff
commit c78fcb2ba6
7 changed files with 60 additions and 82 deletions

View File

@ -1,12 +1,20 @@
<script> <script>
import { Select, Label, Checkbox, Input, Body } from "@budibase/bbui" import { Select, Label, Checkbox, Input, Body } from "@budibase/bbui"
import { tables } from "stores/backend" import { tables as tablesStore, viewsV2 } from "stores/backend"
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte" import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
export let parameters export let parameters
export let bindings = [] export let bindings = []
$: tableOptions = $tables.list || [] $: tables = $tablesStore.list.map(table => ({
label: table.name,
resourceId: table._id,
}))
$: views = $viewsV2.list.map(view => ({
label: view.name,
resourceId: view.id,
}))
$: options = [...(tables || []), ...(views || [])]
</script> </script>
<div class="root"> <div class="root">
@ -15,9 +23,9 @@
<Label>Table</Label> <Label>Table</Label>
<Select <Select
bind:value={parameters.tableId} bind:value={parameters.tableId}
options={tableOptions} {options}
getOptionLabel={table => table.name} getOptionLabel={x => x.label}
getOptionValue={table => table._id} getOptionValue={x => x.resourceId}
/> />
<Label small>Row IDs</Label> <Label small>Row IDs</Label>

View File

@ -1,7 +1,7 @@
<script> <script>
import { Select } from "@budibase/bbui" import { Select } from "@budibase/bbui"
import { createEventDispatcher, onMount } from "svelte" import { createEventDispatcher, onMount } from "svelte"
import { tables as tablesStore } from "stores/backend" import { tables as tablesStore, viewsV2 } from "stores/backend"
export let value export let value
@ -11,36 +11,28 @@
...table, ...table,
type: "table", type: "table",
label: table.name, label: table.name,
key: table._id, resourceId: table._id,
}))
$: views = $viewsV2.list.map(view => ({
...view,
type: "viewV2",
label: view.name,
resourceId: view.id,
})) }))
$: views = $tablesStore.list.reduce(
(acc, table) => [
...acc,
...Object.values(table.views || {})
.filter(view => view.version === 2)
.map(view => ({
...view,
type: "viewV2",
label: view.name,
key: view.id,
})),
],
[]
)
$: options = [...(tables || []), ...(views || [])] $: options = [...(tables || []), ...(views || [])]
const onChange = e => { const onChange = e => {
dispatch( dispatch(
"change", "change",
options.find(x => x.key === e.detail) options.find(x => x.resourceId === e.detail)
) )
} }
onMount(() => { onMount(() => {
// Migrate old values before "key" existed // Migrate old values before "resourceId" existed
if (value && !value.key) { if (value && !value.resourceId) {
const view = views.find(x => x.key === value.id) const view = views.find(x => x.resourceId === value.id)
const table = tables.find(x => x.key === value._id) const table = tables.find(x => x.resourceId === value._id)
dispatch("change", view || table) dispatch("change", view || table)
} }
}) })
@ -48,8 +40,8 @@
<Select <Select
on:change={onChange} on:change={onChange}
value={value?.key} value={value?.resourceId}
{options} {options}
getOptionValue={x => x.key} getOptionValue={x => x.resourceId}
getOptionLabel={x => x.label} getOptionLabel={x => x.label}
/> />

View File

@ -272,12 +272,36 @@
return missing return missing
}) })
// Run any migrations
runMigrations(instance, settingsDefinition)
// Force an initial enrichment of the new settings // Force an initial enrichment of the new settings
enrichComponentSettings(get(context), settingsDefinitionMap, { enrichComponentSettings(get(context), settingsDefinitionMap, {
force: true, force: true,
}) })
} }
const runMigrations = (instance, settingsDefinition) => {
settingsDefinition.forEach(setting => {
// Migrate "table" settings to ensure they have a type and resource ID
if (setting.type === "table") {
const val = instance[setting.key]
if (val) {
if (!val.type) {
val.type = "table"
}
if (!val.resourceId) {
if (val.type === "viewV2") {
val.resourceId = val.id || val.tableId
} else {
val.resourceId = val.tableId
}
}
}
}
})
}
const getSettingsDefinitionMap = settingsDefinition => { const getSettingsDefinitionMap = settingsDefinition => {
let map = {} let map = {}
settingsDefinition?.forEach(setting => { settingsDefinition?.forEach(setting => {

View File

@ -56,7 +56,6 @@
$: formattedFields = convertOldFieldFormat(fields) $: formattedFields = convertOldFieldFormat(fields)
$: fieldsOrDefault = getDefaultFields(formattedFields, schema) $: fieldsOrDefault = getDefaultFields(formattedFields, schema)
$: fetchSchema(dataSource) $: fetchSchema(dataSource)
$: dataProvider = `{{ literal ${safe(providerId)} }}` $: dataProvider = `{{ literal ${safe(providerId)} }}`
$: filter = [ $: filter = [

View File

@ -47,7 +47,7 @@
"##eventHandlerType": "Save Row", "##eventHandlerType": "Save Row",
parameters: { parameters: {
providerId: formId, providerId: formId,
tableId: dataSource?.tableId, tableId: dataSource?.resourceId,
notificationOverride, notificationOverride,
}, },
}, },
@ -80,7 +80,7 @@
"##eventHandlerType": "Delete Row", "##eventHandlerType": "Delete Row",
parameters: { parameters: {
confirm: true, confirm: true,
tableId: dataSource?.tableId, tableId: dataSource?.resourceId,
rowId: `{{ ${safe(repeaterId)}.${safe("_id")} }}`, rowId: `{{ ${safe(repeaterId)}.${safe("_id")} }}`,
revId: `{{ ${safe(repeaterId)}.${safe("_rev")} }}`, revId: `{{ ${safe(repeaterId)}.${safe("_rev")} }}`,
notificationOverride, notificationOverride,

View File

@ -35,7 +35,7 @@ export const buildRowEndpoints = API => ({
* @param suppressErrors whether or not to suppress error notifications * @param suppressErrors whether or not to suppress error notifications
*/ */
patchRow: async (row, suppressErrors = false) => { patchRow: async (row, suppressErrors = false) => {
if (!row?.tableId) { if (!row?.tableId && !row?._viewId) {
return return
} }
return await API.patch({ return await API.patch({
@ -47,7 +47,7 @@ export const buildRowEndpoints = API => ({
/** /**
* Deletes a row from a table. * Deletes a row from a table.
* @param tableId the ID of the table to delete from * @param tableId the ID of the table or view to delete from
* @param rowId the ID of the row to delete * @param rowId the ID of the row to delete
* @param revId the rev of the row to delete * @param revId the rev of the row to delete
*/ */
@ -66,10 +66,13 @@ export const buildRowEndpoints = API => ({
/** /**
* Deletes multiple rows from a table. * Deletes multiple rows from a table.
* @param tableId the table ID to delete the rows from * @param tableId the table or view ID to delete the rows from
* @param rows the array of rows to delete * @param rows the array of rows to delete
*/ */
deleteRows: async ({ tableId, rows }) => { deleteRows: async ({ tableId, rows }) => {
rows?.forEach(row => {
delete row?._viewId
})
return await API.delete({ return await API.delete({
url: `/api/${tableId}/rows`, url: `/api/${tableId}/rows`,
body: { body: {

View File

@ -69,52 +69,4 @@ export const buildViewV2Endpoints = API => ({
delete: async viewId => { delete: async viewId => {
return await API.delete({ url: `/api/v2/views/${viewId}` }) return await API.delete({ url: `/api/v2/views/${viewId}` })
}, },
/**
* Creates a row from a view
* @param row the row to create
* @param suppressErrors whether or not to suppress error notifications
*/
createRow: async (row, suppressErrors = false) => {
if (!row?._viewId || !row?.tableId) {
return
}
return await API.post({
url: `/api/v2/views/${row._viewId}/rows`,
body: row,
suppressErrors,
})
},
/**
* Updates an existing row through a view
* @param row the row to update
* @param suppressErrors whether or not to suppress error notifications
*/
updateRow: async (row, suppressErrors = false) => {
if (!row?._viewId || !row?.tableId || !row?._id) {
return
}
return await API.patch({
url: `/api/v2/views/${row._viewId}/rows/${row._id}`,
body: row,
suppressErrors,
})
},
/**
* Deletes multiple rows from a table through a view
* @param viewId the table ID to delete the rows from
* @param rows the array of rows to delete
*/
deleteRows: async ({ viewId, rows }) => {
// Ensure we delete _viewId from rows as otherwise this throws a 500
rows?.forEach(row => {
delete row?._viewId
})
return await API.delete({
url: `/api/v2/views/${viewId}/rows`,
body: {
rows,
},
})
},
}) })