metrics for view joins and grouped filters

This commit is contained in:
Martin McKeaveney 2024-12-05 17:32:07 +00:00
parent 9ef6cbd566
commit 3332f2fa22
16 changed files with 100 additions and 67 deletions

View File

@ -13,8 +13,8 @@ const EXCLUDED_EVENTS: Event[] = [
Event.ROLE_UPDATED,
Event.DATASOURCE_UPDATED,
Event.QUERY_UPDATED,
Event.TABLE_UPDATED,
Event.VIEW_UPDATED,
// Event.TABLE_UPDATED,
// Event.VIEW_UPDATED,
Event.VIEW_FILTER_UPDATED,
Event.VIEW_CALCULATION_UPDATED,
Event.AUTOMATION_TRIGGER_UPDATED,

View File

@ -1,13 +1,14 @@
import { publishEvent } from "../events"
import {
Event,
TableExportFormat,
FieldType,
Table,
TableCreatedEvent,
TableUpdatedEvent,
TableDeletedEvent,
TableExportedEvent,
TableExportFormat,
TableImportedEvent,
TableUpdatedEvent,
} from "@budibase/types"
async function created(table: Table, timestamp?: string | number) {
@ -20,15 +21,35 @@ async function created(table: Table, timestamp?: string | number) {
await publishEvent(Event.TABLE_CREATED, properties, timestamp)
}
async function updated(table: Table) {
async function updated(oldTable: Table, newTable: Table) {
// only publish the event if it has fields we are interested in
let defaultValues, aiColumn
// check that new fields have been added
for (const key in newTable.schema) {
if (!oldTable.schema[key]) {
const newColumn = newTable.schema[key]
if ("default" in newColumn) {
defaultValues = true
}
if (newColumn.type === FieldType.AI) {
aiColumn = newColumn.operation
}
}
}
const properties: TableUpdatedEvent = {
tableId: table._id as string,
tableId: newTable._id as string,
defaultValues,
aiColumn,
audited: {
name: table.name,
name: newTable.name,
},
}
if (defaultValues || aiColumn) {
await publishEvent(Event.TABLE_UPDATED, properties)
}
}
async function deleted(table: Table) {
const properties: TableDeletedEvent = {

View File

@ -11,6 +11,7 @@ import {
ViewFilterDeletedEvent,
ViewFilterUpdatedEvent,
ViewUpdatedEvent,
View,
ViewV2,
ViewCalculation,
Table,
@ -19,17 +20,27 @@ import {
/* eslint-disable */
async function created(view: Partial<ViewV2>, timestamp?: string | number) {
async function created(view: ViewV2, timestamp?: string | number) {
const properties: ViewCreatedEvent = {
name: view.name,
type: view.type,
tableId: view.tableId,
}
await publishEvent(Event.VIEW_CREATED, properties, timestamp)
}
async function updated(view: View) {
async function updated(newView: ViewV2) {
// // check whether any of the fields are different
let viewJoins = 0
for (const key in newView.schema) {
if (newView.schema[key]?.columns) {
viewJoins += Object.keys(newView.schema[key]?.columns).length
}
}
const properties: ViewUpdatedEvent = {
tableId: view.tableId,
tableId: newView.tableId,
groupedFilters: newView.queryUI?.groups?.length || 0,
viewJoins,
}
await publishEvent(Event.VIEW_UPDATED, properties)
}

View File

@ -45,13 +45,13 @@ export async function updateTable(
inputs.created = true
}
try {
const { datasource, table } = await sdk.tables.external.save(
const { datasource, oldTable, table } = await sdk.tables.external.save(
datasourceId!,
inputs,
{ tableId, renaming }
)
builderSocket?.emitDatasourceUpdate(ctx, datasource)
return table
return { table, oldTable }
} catch (err: any) {
if (err instanceof Error) {
ctx.throw(400, err.message)

View File

@ -119,8 +119,15 @@ export async function save(ctx: UserCtx<SaveTableRequest, SaveTableResponse>) {
await events.table.created(savedTable)
} else {
const api = pickApi({ table })
savedTable = await api.updateTable(ctx, renaming)
await events.table.updated(savedTable)
const { table: updatedTable, oldTable } = await api.updateTable(
ctx,
renaming
)
savedTable = updatedTable
if (oldTable) {
await events.table.updated(oldTable, savedTable)
}
}
if (renaming) {
await sdk.views.renameLinkedViews(savedTable, renaming)

View File

@ -30,14 +30,14 @@ export async function updateTable(
}
try {
const { table } = await sdk.tables.internal.save(tableToSave, {
const { table, oldTable } = await sdk.tables.internal.save(tableToSave, {
userId: ctx.user._id,
rowsToImport: rows,
tableId: ctx.request.body._id,
renaming,
})
return table
return { table, oldTable }
} catch (err: any) {
if (err instanceof Error) {
ctx.throw(400, err.message)

View File

@ -60,35 +60,31 @@ export async function save(ctx: Ctx) {
existingTable.views[viewName] = existingTable.views[originalName]
}
await db.put(table)
await handleViewEvents(
existingTable.views[viewName] as View,
table.views[viewName]
)
ctx.body = table.views[viewName]
builderSocket?.emitTableUpdate(ctx, table)
}
export async function calculationEvents(existingView: View, newView: View) {
const existingCalculation = existingView && existingView.calculation
const newCalculation = newView && newView.calculation
if (existingCalculation && !newCalculation) {
await events.view.calculationDeleted(existingView)
}
if (!existingCalculation && newCalculation) {
await events.view.calculationCreated(newView)
}
if (
existingCalculation &&
newCalculation &&
existingCalculation !== newCalculation
) {
await events.view.calculationUpdated(newView)
}
}
// export async function calculationEvents(existingView: View, newView: View) {
// const existingCalculation = existingView && existingView.calculation
// const newCalculation = newView && newView.calculation
//
// if (existingCalculation && !newCalculation) {
// await events.view.calculationDeleted(existingView)
// }
//
// if (!existingCalculation && newCalculation) {
// await events.view.calculationCreated(newView)
// }
//
// if (
// existingCalculation &&
// newCalculation &&
// existingCalculation !== newCalculation
// ) {
// await events.view.calculationUpdated(newView)
// }
// }
export async function filterEvents(existingView: View, newView: View) {
const hasExistingFilters = !!(
@ -115,16 +111,6 @@ export async function filterEvents(existingView: View, newView: View) {
}
}
async function handleViewEvents(existingView: View, newView: View) {
if (!existingView) {
await events.view.created(newView)
} else {
await events.view.updated(newView)
}
await calculationEvents(existingView, newView)
await filterEvents(existingView, newView)
}
export async function destroy(ctx: Ctx) {
const db = context.getAppDB()
const viewName = decodeURIComponent(ctx.params.viewName)

View File

@ -151,7 +151,7 @@ export async function create(ctx: Ctx<CreateViewRequest, ViewResponse>) {
}
const result = await sdk.views.create(tableId, parsedView)
await events.view.created(view)
await events.view.created(result)
ctx.status = 201
ctx.body = {
@ -190,10 +190,11 @@ export async function update(ctx: Ctx<UpdateViewRequest, ViewResponse>) {
primaryDisplay: view.primaryDisplay,
}
const result = await sdk.views.update(tableId, parsedView)
ctx.body = {
data: result,
}
const { view: result } = await sdk.views.update(tableId, parsedView)
await events.view.updated(result)
ctx.body = { data: result }
const table = await sdk.tables.getTable(tableId)
builderSocket?.emitTableUpdate(ctx, table)

View File

@ -14,7 +14,7 @@ export const backfill = async (appDb: Database, timestamp: string | number) => {
continue
}
await events.view.created(view, timestamp)
// await events.view.created(view, timestamp)
if (view.calculation) {
await events.view.calculationCreated(view, timestamp)

View File

@ -282,7 +282,7 @@ export async function save(
tableToSave.sql = true
}
return { datasource: updatedDatasource, table: tableToSave }
return { datasource: updatedDatasource, table: tableToSave, oldTable }
}
export async function destroy(datasourceId: string, table: Table) {

View File

@ -171,7 +171,7 @@ export async function save(
}
// has to run after, make sure it has _id
await runStaticFormulaChecks(table, { oldTable, deletion: false })
return { table }
return { table, oldTable }
}
export async function destroy(table: Table) {

View File

@ -63,7 +63,7 @@ export async function create(
export async function update(
tableId: string,
view: Readonly<ViewV2>
): Promise<ViewV2> {
): Promise<{ view: ViewV2; existingView: ViewV2 }> {
const db = context.getAppDB()
const { datasourceId, tableName } = breakExternalTableId(tableId)
@ -87,7 +87,7 @@ export async function update(
delete views[existingView.name]
views[view.name] = view
await db.put(ds)
return view
return { view, existingView }
}
export async function remove(viewId: string): Promise<ViewV2> {

View File

@ -315,7 +315,10 @@ export async function create(
return view
}
export async function update(tableId: string, view: ViewV2): Promise<ViewV2> {
export async function update(
tableId: string,
view: ViewV2
): Promise<{ view: ViewV2; existingView: ViewV2 }> {
await guardViewSchema(tableId, view)
return pickApi(tableId).update(tableId, view)

View File

@ -54,7 +54,7 @@ export async function create(
export async function update(
tableId: string,
view: Readonly<ViewV2>
): Promise<ViewV2> {
): Promise<{ view: ViewV2; existingView: ViewV2 }> {
const db = context.getAppDB()
const table = await sdk.tables.getTable(tableId)
table.views ??= {}
@ -76,7 +76,7 @@ export async function update(
delete table.views[existingView.name]
table.views[view.name] = view
await db.put(table)
return view
return { view, existingView }
}
export async function remove(viewId: string): Promise<ViewV2> {

View File

@ -1,4 +1,5 @@
import { BaseEvent, TableExportFormat } from "./event"
import { AIOperationEnum } from "../ai"
export interface TableCreatedEvent extends BaseEvent {
tableId: string
@ -9,6 +10,8 @@ export interface TableCreatedEvent extends BaseEvent {
export interface TableUpdatedEvent extends BaseEvent {
tableId: string
defaultValues: boolean | undefined
aiColumn: AIOperationEnum | undefined
audited: {
name: string
}

View File

@ -1,15 +1,16 @@
import { ViewCalculation, ViewV2Schema, ViewV2Type } from "../../documents"
import { ViewCalculation, ViewV2Type } from "../../documents"
import { BaseEvent, TableExportFormat } from "./event"
import { LegacyFilter, SortOrder, SortType, UISearchFilter } from "../../api"
import { SearchFilters } from "../search"
export interface ViewCreatedEvent extends BaseEvent {
name: string
type?: ViewV2Type
tableId: string
}
export interface ViewUpdatedEvent extends BaseEvent {
tableId: string
groupedFilters: number
viewJoins: number
}
export interface ViewDeletedEvent extends BaseEvent {