Merge branch 'views-v2-frontend' of github.com:Budibase/budibase into views-v2-frontend

This commit is contained in:
mike12345567 2023-08-11 13:16:56 +01:00
commit a24e1809b6
22 changed files with 303 additions and 107 deletions

View File

@ -734,9 +734,19 @@ export const getSchemaForDatasource = (asset, datasource, options) => {
// Determine the schema from the backing entity if not already determined // Determine the schema from the backing entity if not already determined
if (table && !schema) { if (table && !schema) {
if (type === "view") { if (type === "view") {
// For views, the schema is pulled from the `views` property of the // Old views
// table
schema = cloneDeep(table.views?.[datasource.name]?.schema) schema = cloneDeep(table.views?.[datasource.name]?.schema)
} else if (type === "viewV2") {
// New views which are DS+
const view = table.views?.[datasource.name]
schema = cloneDeep(view?.schema)
// Strip hidden fields
Object.keys(schema || {}).forEach(field => {
if (!schema[field].visible) {
delete schema[field]
}
})
} else if ( } else if (
type === "query" && type === "query" &&
(options.formSchema || options.searchableSchema) (options.formSchema || options.searchableSchema)

View File

@ -13,7 +13,7 @@
} }
const handleGridViewUpdate = async e => { const handleGridViewUpdate = async e => {
viewsV2.replace(id, e.detail) viewsV2.replaceView(id, e.detail)
} }
</script> </script>

View File

@ -1,7 +1,7 @@
<script> <script>
import { getContext } from "svelte" import { getContext } from "svelte"
import { Modal, ActionButton } from "@budibase/bbui" import { Modal, ActionButton } from "@budibase/bbui"
import CreateViewModal from "../../modals/CreateViewModal.svelte" import GridCreateViewModal from "../../modals/grid/GridCreateViewModal.svelte"
const { rows, columns } = getContext("grid") const { rows, columns } = getContext("grid")
@ -14,5 +14,5 @@
Add view Add view
</ActionButton> </ActionButton>
<Modal bind:this={modal}> <Modal bind:this={modal}>
<CreateViewModal /> <GridCreateViewModal />
</Modal> </Modal>

View File

@ -4,9 +4,6 @@
const { columns, datasource, filter, definition } = getContext("grid") const { columns, datasource, filter, definition } = getContext("grid")
// Wipe filter whenever table ID changes to avoid using stale filters
$: $datasource, filter.set([])
const onFilter = e => { const onFilter = e => {
filter.set(e.detail || []) filter.set(e.detail || [])
} }

View File

@ -3,16 +3,12 @@
import { Input, notifications, ModalContent } from "@budibase/bbui" import { Input, notifications, ModalContent } from "@budibase/bbui"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import { viewsV2 } from "stores/backend" import { viewsV2 } from "stores/backend"
import { LuceneUtils } from "@budibase/frontend-core"
const { filter, sort, table } = getContext("grid") const { filter, sort, definition } = getContext("grid")
$: query = LuceneUtils.buildLuceneQuery($filter)
let name let name
$: console.log($table) $: views = Object.keys($definition?.views || {})
$: views = Object.keys($table?.views || {})
$: nameExists = views.includes(name?.trim()) $: nameExists = views.includes(name?.trim())
const saveView = async () => { const saveView = async () => {
@ -20,14 +16,14 @@
try { try {
const newView = await viewsV2.create({ const newView = await viewsV2.create({
name, name,
tableId: $table._id, tableId: $definition._id,
query, query: $filter,
sort: { sort: {
field: $sort.column, field: $sort.column,
order: $sort.order, order: $sort.order,
}, },
schema: $table.schema, schema: $definition.schema,
primaryDisplay: $table.primaryDisplay, primaryDisplay: $definition.primaryDisplay,
}) })
notifications.success(`View ${name} created`) notifications.success(`View ${name} created`)
$goto(`../../view/v2/${newView.id}`) $goto(`../../view/v2/${newView.id}`)

View File

@ -58,7 +58,8 @@
$goto(`./view/v1/${encodeURIComponent(name)}`) $goto(`./view/v1/${encodeURIComponent(name)}`)
} }
}} }}
selectedBy={$userSelectedResourceMap[name]} selectedBy={$userSelectedResourceMap[name] ||
$userSelectedResourceMap[view.id]}
> >
<EditViewPopover {view} /> <EditViewPopover {view} />
</NavItem> </NavItem>

View File

@ -39,15 +39,33 @@
tableId: m._id, tableId: m._id,
type: "table", type: "table",
})) }))
$: views = $tablesStore.list.reduce((acc, cur) => { $: viewsV1 = $tablesStore.list.reduce(
let viewsArr = Object.entries(cur.views || {}).map(([key, value]) => ({ (acc, table) => [
label: key, ...acc,
name: key, ...Object.values(table.views || {})
...value, .filter(view => view.version !== 2)
type: "view", .map(view => ({
})) ...view,
return [...acc, ...viewsArr] label: view.name,
}, []) type: "view",
})),
],
[]
)
$: viewsV2 = $tablesStore.list.reduce(
(acc, table) => [
...acc,
...Object.values(table.views || {})
.filter(view => view.version === 2)
.map(view => ({
...view,
label: view.name,
type: "viewV2",
})),
],
[]
)
$: views = [...(viewsV1 || []), ...(viewsV2 || [])]
$: queries = $queriesStore.list $: queries = $queriesStore.list
.filter(q => showAllQueries || q.queryVerb === "read" || q.readable) .filter(q => showAllQueries || q.queryVerb === "read" || q.readable)
.map(query => ({ .map(query => ({

View File

@ -7,22 +7,50 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
$: tables = $tablesStore.list.map(m => ({ $: tables = $tablesStore.list.map(table => ({
label: m.name, ...table,
tableId: m._id,
type: "table", type: "table",
label: table.name,
key: table._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 || [])]
$: {
// Migrate old table values before "key" existed
if (value && !value.key) {
console.log("migrate")
dispatch(
"change",
tables.find(x => x.tableId === value.tableId)
)
}
}
const onChange = e => { const onChange = e => {
const dataSource = tables?.find(x => x.tableId === e.detail) dispatch(
dispatch("change", dataSource) "change",
options.find(x => x.key === e.detail)
)
} }
</script> </script>
<Select <Select
on:change={onChange} on:change={onChange}
value={value?.tableId} value={value?.key}
options={tables} {options}
getOptionValue={x => x.tableId} getOptionValue={x => x.key}
getOptionLabel={x => x.label} getOptionLabel={x => x.label}
/> />

View File

@ -1,4 +1,4 @@
import { writable, derived } from "svelte/store" import { writable, derived, get } from "svelte/store"
import { tables } from "./" import { tables } from "./"
import { API } from "api" import { API } from "api"
@ -30,44 +30,63 @@ export function createViewsV2Store() {
const deleteView = async view => { const deleteView = async view => {
await API.viewV2.delete(view.id) await API.viewV2.delete(view.id)
replaceView(view.id, null)
// Update tables
tables.update(state => {
const table = state.list.find(table => table._id === view.tableId)
delete table.views[view.name]
return { ...state }
})
} }
const create = async view => { const create = async view => {
const savedViewResponse = await API.viewV2.create(view) const savedViewResponse = await API.viewV2.create(view)
const savedView = savedViewResponse.data const savedView = savedViewResponse.data
replaceView(savedView.id, savedView)
// Update tables
tables.update(state => {
const table = state.list.find(table => table._id === view.tableId)
table.views[view.name] = savedView
return { ...state }
})
return savedView return savedView
} }
const save = async view => { const save = async view => {
const res = await API.viewV2.update(view) const res = await API.viewV2.update(view)
const savedView = res?.data const savedView = res?.data
replaceView(view.id, savedView)
}
// Update tables // Handles external updates of tables
tables.update(state => { const replaceView = (viewId, view) => {
const table = state.list.find(table => table._id === view.tableId) if (!viewId) {
if (table) { return
if (view.originalName) { }
delete table.views[view.originalName] const existingView = get(derivedStore).list.find(view => view.id === viewId)
} const tableIndex = get(tables).list.findIndex(table => {
table.views[view.name] = savedView return table._id === view?.tableId || table._id === existingView?.tableId
}
return { ...state }
}) })
if (tableIndex === -1) {
return
}
// Handle deletion
if (!view) {
tables.update(state => {
delete state.list[tableIndex].views[existingView.name]
return state
})
return
}
// Add new view
if (!existingView) {
tables.update(state => {
state.list[tableIndex].views[view.name] = view
return state
})
}
// Update existing view
else {
tables.update(state => {
// Remove old view
delete state.list[tableIndex].views[existingView.name]
// Add new view
state.list[tableIndex].views[view.name] = view
return state
})
}
} }
return { return {
@ -76,6 +95,7 @@ export function createViewsV2Store() {
delete: deleteView, delete: deleteView,
create, create,
save, save,
replaceView,
} }
} }

View File

@ -5274,7 +5274,7 @@
}, },
{ {
"type": "table", "type": "table",
"label": "Table", "label": "Data",
"key": "dataSource" "key": "dataSource"
}, },
{ {
@ -5549,7 +5549,7 @@
"settings": [ "settings": [
{ {
"type": "table", "type": "table",
"label": "Table", "label": "Data",
"key": "table", "key": "table",
"required": true "required": true
}, },

View File

@ -38,11 +38,8 @@
class:in-builder={$builderStore.inBuilder} class:in-builder={$builderStore.inBuilder}
> >
<Grid <Grid
tableId={table?.tableId} datasource={table}
{API} {API}
{allowAddRows}
{allowEditRows}
{allowDeleteRows}
{stripeRows} {stripeRows}
{initialFilter} {initialFilter}
{initialSortColumn} {initialSortColumn}
@ -50,9 +47,12 @@
{fixedRowHeight} {fixedRowHeight}
{columnWhitelist} {columnWhitelist}
{schemaOverrides} {schemaOverrides}
canAddRows={allowAddRows}
canEditRows={allowEditRows}
canDeleteRows={allowDeleteRows}
canExpandRows={false}
canSaveSchema={false}
showControls={false} showControls={false}
allowExpandRows={false}
allowSchemaChanges={false}
notifySuccess={notificationStore.actions.success} notifySuccess={notificationStore.actions.success}
notifyError={notificationStore.actions.error} notifyError={notificationStore.actions.error}
/> />

View File

@ -6,6 +6,7 @@ import RelationshipFetch from "@budibase/frontend-core/src/fetch/RelationshipFet
import NestedProviderFetch from "@budibase/frontend-core/src/fetch/NestedProviderFetch.js" import NestedProviderFetch from "@budibase/frontend-core/src/fetch/NestedProviderFetch.js"
import FieldFetch from "@budibase/frontend-core/src/fetch/FieldFetch.js" import FieldFetch from "@budibase/frontend-core/src/fetch/FieldFetch.js"
import JSONArrayFetch from "@budibase/frontend-core/src/fetch/JSONArrayFetch.js" import JSONArrayFetch from "@budibase/frontend-core/src/fetch/JSONArrayFetch.js"
import ViewV2Fetch from "@budibase/frontend-core/src/fetch/ViewV2Fetch.js"
/** /**
* Fetches the schema of any kind of datasource. * Fetches the schema of any kind of datasource.
@ -21,6 +22,7 @@ export const fetchDatasourceSchema = async (
const handler = { const handler = {
table: TableFetch, table: TableFetch,
view: ViewFetch, view: ViewFetch,
viewV2: ViewV2Fetch,
query: QueryFetch, query: QueryFetch,
link: RelationshipFetch, link: RelationshipFetch,
provider: NestedProviderFetch, provider: NestedProviderFetch,
@ -49,6 +51,15 @@ export const fetchDatasourceSchema = async (
return null return null
} }
// Strip hidden fields from views
if (datasource.type === "viewV2") {
Object.keys(schema).forEach(field => {
if (!schema[field].visible) {
delete schema[field]
}
})
}
// Enrich schema with relationships if required // Enrich schema with relationships if required
if (definition?.sql && options?.enrichRelationships) { if (definition?.sql && options?.enrichRelationships) {
const relationshipAdditions = await getRelationshipSchemaAdditions(schema) const relationshipAdditions = await getRelationshipSchemaAdditions(schema)

View File

@ -22,9 +22,33 @@ export const buildViewV2Endpoints = API => ({
/** /**
* Fetches all rows in a view * Fetches all rows in a view
* @param viewId the id of the view * @param viewId the id of the view
* @param paginate whether to paginate or not
* @param limit page size
* @param bookmark pagination cursor
* @param sort sort column
* @param sortOrder sort order
* @param sortType sort type (text or numeric)
*/ */
fetch: async viewId => { fetch: async ({
return await API.get({ url: `/api/v2/views/${viewId}/search` }) viewId,
paginate,
limit,
bookmark,
sort,
sortOrder,
sortType,
}) => {
return await API.post({
url: `/api/v2/views/${viewId}/search`,
body: {
paginate,
limit,
bookmark,
sort,
sortOrder,
sortType,
},
})
}, },
/** /**
* Delete a view * Delete a view

View File

@ -80,7 +80,7 @@ export const createActions = context => {
// Broadcast change to external state can be updated, as this change // Broadcast change to external state can be updated, as this change
// will not be received by the builder websocket because we caused it ourselves // will not be received by the builder websocket because we caused it ourselves
dispatch("updatedefinition", newDefinition) dispatch("updatedatasource", newDefinition)
} }
// Adds a row to the datasource // Adds a row to the datasource
@ -98,10 +98,16 @@ export const createActions = context => {
return await getAPI()?.actions.deleteRows(rows) return await getAPI()?.actions.deleteRows(rows)
} }
// Gets a single row from a datasource
const getRow = async id => { const getRow = async id => {
return await getAPI()?.actions.getRow(id) return await getAPI()?.actions.getRow(id)
} }
// Checks if a certain datasource config is valid
const isDatasourceValid = datasource => {
return getAPI()?.actions.isDatasourceValid(datasource)
}
return { return {
datasource: { datasource: {
...datasource, ...datasource,
@ -112,6 +118,7 @@ export const createActions = context => {
updateRow, updateRow,
deleteRows, deleteRows,
getRow, getRow,
isDatasourceValid,
}, },
}, },
} }

View File

@ -98,7 +98,7 @@ export const createActions = context => {
loading.set(true) loading.set(true)
// Abandon if we don't have a valid datasource // Abandon if we don't have a valid datasource
if (!$datasource) { if (!datasource.actions.isDatasourceValid($datasource)) {
return return
} }

View File

@ -25,6 +25,10 @@ export const createActions = context => {
}) })
} }
const isDatasourceValid = datasource => {
return datasource?.type === "table" && datasource?.tableId
}
const getRow = async id => { const getRow = async id => {
const res = await API.searchTable({ const res = await API.searchTable({
tableId: get(datasource).tableId, tableId: get(datasource).tableId,
@ -48,6 +52,7 @@ export const createActions = context => {
updateRow: saveRow, updateRow: saveRow,
deleteRows, deleteRows,
getRow, getRow,
isDatasourceValid,
}, },
}, },
} }
@ -56,38 +61,49 @@ export const createActions = context => {
export const initialise = context => { export const initialise = context => {
const { datasource, fetch, filter, sort, definition } = context const { datasource, fetch, filter, sort, definition } = context
// Wipe filter whenever table ID changes to avoid using stale filters
definition.subscribe(() => {
if (get(datasource)?.type !== "table") {
return
}
filter.set([])
})
// Update fetch when filter changes // Update fetch when filter changes
filter.subscribe($filter => { filter.subscribe($filter => {
if (get(datasource)?.type === "table") { if (get(datasource)?.type !== "table") {
get(fetch)?.update({ return
filter: $filter,
})
} }
get(fetch)?.update({
filter: $filter,
})
}) })
// Update fetch when sorting changes // Update fetch when sorting changes
sort.subscribe($sort => { sort.subscribe($sort => {
if (get(datasource)?.type === "table") { if (get(datasource)?.type !== "table") {
get(fetch)?.update({ return
sortOrder: $sort.order,
sortColumn: $sort.column,
})
} }
get(fetch)?.update({
sortOrder: $sort.order,
sortColumn: $sort.column,
})
}) })
// Ensure sorting UI reflects the fetch state whenever we reset the fetch, // Ensure sorting UI reflects the fetch state whenever we reset the fetch,
// which triggers a new definition // which triggers a new definition
definition.subscribe(() => { definition.subscribe(() => {
if (get(datasource)?.type === "table") { if (get(datasource)?.type !== "table") {
const $fetch = get(fetch) return
if (!$fetch) {
return
}
const { sortColumn, sortOrder } = get($fetch)
sort.set({
column: sortColumn,
order: sortOrder,
})
} }
const $fetch = get(fetch)
if (!$fetch) {
return
}
const { sortColumn, sortOrder } = get($fetch)
sort.set({
column: sortColumn,
order: sortOrder,
})
}) })
} }

View File

@ -60,6 +60,12 @@ export const createActions = context => {
return res?.rows?.[0] return res?.rows?.[0]
} }
const isDatasourceValid = datasource => {
return (
datasource?.type === "viewV2" && datasource?.id && datasource?.tableId
)
}
return { return {
viewV2: { viewV2: {
actions: { actions: {
@ -69,15 +75,16 @@ export const createActions = context => {
updateRow, updateRow,
deleteRows, deleteRows,
getRow, getRow,
isDatasourceValid,
}, },
}, },
} }
} }
export const initialise = context => { export const initialise = context => {
const { definition, datasource, sort, rows } = context const { definition, datasource, sort, rows, filter } = context
// For views, keep sort state in line with the view definition // Keep sort and filter state in line with the view definition
definition.subscribe($definition => { definition.subscribe($definition => {
if (!$definition || get(datasource)?.type !== "viewV2") { if (!$definition || get(datasource)?.type !== "viewV2") {
return return
@ -86,6 +93,7 @@ export const initialise = context => {
column: $definition.sort?.field, column: $definition.sort?.field,
order: $definition.sort?.order, order: $definition.sort?.order,
}) })
filter.set($definition.query || [])
}) })
// When sorting changes, ensure view definition is kept up to date // When sorting changes, ensure view definition is kept up to date
@ -108,4 +116,19 @@ export const initialise = context => {
await rows.actions.refreshData() await rows.actions.refreshData()
} }
}) })
// When filters change, ensure view definition is kept up to date
filter.subscribe(async $filter => {
const $view = get(definition)
if (!$view || get(datasource)?.type !== "viewV2") {
return
}
if (JSON.stringify($filter) !== JSON.stringify($view.query)) {
await datasource.actions.saveDefinition({
...$view,
query: $filter,
})
await rows.actions.refreshData()
}
})
} }

View File

@ -116,7 +116,7 @@ export default class DataFetch {
async getInitialData() { async getInitialData() {
const { datasource, filter, paginate } = this.options const { datasource, filter, paginate } = this.options
// Fetch datasource definition and extract filter and sort if configured // Fetch datasource definition and extract sort properties if configured
const definition = await this.getDefinition(datasource) const definition = await this.getDefinition(datasource)
if (definition?.sort?.field) { if (definition?.sort?.field) {
this.options.sortColumn = definition.sort.field this.options.sortColumn = definition.sort.field

View File

@ -1,15 +1,19 @@
import DataFetch from "./DataFetch.js" import DataFetch from "./DataFetch.js"
import { get } from "svelte/store"
export default class ViewV2Fetch extends DataFetch { export default class ViewV2Fetch extends DataFetch {
determineFeatureFlags() { determineFeatureFlags() {
return { return {
// The API does not actually support dynamic filtering, but since views
// have filters built in we don't want to perform client side filtering
// which would happen if we marked this as false
supportsSearch: true, supportsSearch: true,
supportsSort: true, supportsSort: true,
supportsPagination: true, supportsPagination: true,
} }
} }
async getSchema(datasource, definition) { getSchema(datasource, definition) {
return definition?.schema return definition?.schema
} }
@ -29,12 +33,30 @@ export default class ViewV2Fetch extends DataFetch {
} }
async getData() { async getData() {
const { datasource } = this.options const { datasource, limit, sortColumn, sortOrder, sortType, paginate } =
this.options
const { cursor } = get(this.store)
try { try {
const res = await this.API.viewV2.fetch(datasource.id) const res = await this.API.viewV2.fetch({
return { rows: res?.rows || [] } viewId: datasource.id,
paginate,
limit,
bookmark: cursor,
sort: sortColumn,
sortOrder,
sortType,
})
return {
rows: res?.rows || [],
hasNextPage: res?.hasNextPage || false,
cursor: res?.bookmark || null,
}
} catch (error) { } catch (error) {
return { rows: [] } return {
rows: [],
hasNextPage: false,
error,
}
} }
} }
} }

View File

@ -9,7 +9,7 @@ import {
fixAutoColumnSubType, fixAutoColumnSubType,
} from "../../../utilities/rowProcessor" } from "../../../utilities/rowProcessor"
import { runStaticFormulaChecks } from "./bulkFormula" import { runStaticFormulaChecks } from "./bulkFormula"
import { Table } from "@budibase/types" import { Table, ViewStatisticsSchema } from "@budibase/types"
import { quotas } from "@budibase/pro" import { quotas } from "@budibase/pro"
import isEqual from "lodash/isEqual" import isEqual from "lodash/isEqual"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
@ -36,7 +36,9 @@ function checkAutoColumns(table: Table, oldTable?: Table) {
export async function save(ctx: any) { export async function save(ctx: any) {
const db = context.getAppDB() const db = context.getAppDB()
const { rows, ...rest } = ctx.request.body const { rows, ...rest } = ctx.request.body
let tableToSave = { let tableToSave: Table & {
_rename?: { old: string; updated: string } | null
} = {
type: "table", type: "table",
_id: generateTableID(), _id: generateTableID(),
views: {}, views: {},
@ -44,7 +46,7 @@ export async function save(ctx: any) {
} }
// if the table obj had an _id then it will have been retrieved // if the table obj had an _id then it will have been retrieved
let oldTable let oldTable: Table | undefined
if (ctx.request.body && ctx.request.body._id) { if (ctx.request.body && ctx.request.body._id) {
oldTable = await sdk.tables.getTable(ctx.request.body._id) oldTable = await sdk.tables.getTable(ctx.request.body._id)
} }
@ -97,7 +99,17 @@ export async function save(ctx: any) {
const tableView = tableToSave.views[view] const tableView = tableToSave.views[view]
if (!tableView) continue if (!tableView) continue
if (tableView.schema.group || tableView.schema.field) continue if (sdk.views.isV2(tableView)) {
// We don't want to modify views from the tables controller
tableToSave.views[view] = oldTable!.views![view]
continue
}
if (
(tableView.schema as ViewStatisticsSchema).group ||
tableView.schema.field
)
continue
tableView.schema = tableToSave.schema tableView.schema = tableToSave.schema
} }

View File

@ -8,6 +8,7 @@ import {
ViewV2, ViewV2,
RequiredKeys, RequiredKeys,
} from "@budibase/types" } from "@budibase/types"
import { builderSocket } from "../../../websockets"
async function parseSchemaUI(ctx: Ctx, view: CreateViewRequest) { async function parseSchemaUI(ctx: Ctx, view: CreateViewRequest) {
if (!view.schema) { if (!view.schema) {
@ -86,6 +87,9 @@ export async function create(ctx: Ctx<CreateViewRequest, ViewResponse>) {
ctx.body = { ctx.body = {
data: result, data: result,
} }
const table = await sdk.tables.getTable(tableId)
builderSocket?.emitTableUpdate(ctx, table)
} }
export async function update(ctx: Ctx<UpdateViewRequest, ViewResponse>) { export async function update(ctx: Ctx<UpdateViewRequest, ViewResponse>) {
@ -118,11 +122,17 @@ export async function update(ctx: Ctx<UpdateViewRequest, ViewResponse>) {
ctx.body = { ctx.body = {
data: result, data: result,
} }
const table = await sdk.tables.getTable(tableId)
builderSocket?.emitTableUpdate(ctx, table)
} }
export async function remove(ctx: Ctx) { export async function remove(ctx: Ctx) {
const { viewId } = ctx.params const { viewId } = ctx.params
await sdk.views.remove(viewId) const view = await sdk.views.remove(viewId)
ctx.status = 204 ctx.status = 204
const table = await sdk.tables.getTable(view.tableId)
builderSocket?.emitTableUpdate(ctx, table)
} }

View File

@ -53,7 +53,7 @@ export function isV2(view: View | ViewV2): view is ViewV2 {
return (view as ViewV2).version === 2 return (view as ViewV2).version === 2
} }
export async function remove(viewId: string): Promise<void> { export async function remove(viewId: string): Promise<ViewV2> {
const db = context.getAppDB() const db = context.getAppDB()
const view = await get(viewId) const view = await get(viewId)
@ -64,6 +64,7 @@ export async function remove(viewId: string): Promise<void> {
delete table.views![view?.name] delete table.views![view?.name]
await db.put(table) await db.put(table)
return view
} }
export function enrichSchema(view: View | ViewV2, tableSchema: TableSchema) { export function enrichSchema(view: View | ViewV2, tableSchema: TableSchema) {