send individual events for view calcs and joins
This commit is contained in:
parent
41c1632d60
commit
917c235295
|
@ -13,6 +13,7 @@ const EXCLUDED_EVENTS: Event[] = [
|
|||
Event.ROLE_UPDATED,
|
||||
Event.DATASOURCE_UPDATED,
|
||||
Event.QUERY_UPDATED,
|
||||
Event.VIEW_UPDATED,
|
||||
Event.VIEW_FILTER_UPDATED,
|
||||
Event.VIEW_CALCULATION_UPDATED,
|
||||
Event.AUTOMATION_TRIGGER_UPDATED,
|
||||
|
|
|
@ -5,7 +5,7 @@ async function created(
|
|||
rowAction: RowActionCreatedEvent,
|
||||
timestamp?: string | number
|
||||
) {
|
||||
await publishEvent(Event.TABLE_CREATED, rowAction, timestamp)
|
||||
await publishEvent(Event.ROW_ACTION_CREATED, rowAction, timestamp)
|
||||
}
|
||||
|
||||
export default {
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import { publishEvent } from "../events"
|
||||
import {
|
||||
CalculationType,
|
||||
Event,
|
||||
Table,
|
||||
TableExportFormat,
|
||||
View,
|
||||
ViewCalculation,
|
||||
ViewCalculationCreatedEvent,
|
||||
ViewCalculationDeletedEvent,
|
||||
ViewCalculationUpdatedEvent,
|
||||
|
@ -11,11 +16,8 @@ import {
|
|||
ViewFilterDeletedEvent,
|
||||
ViewFilterUpdatedEvent,
|
||||
ViewUpdatedEvent,
|
||||
View,
|
||||
ViewV2,
|
||||
ViewCalculation,
|
||||
Table,
|
||||
TableExportFormat,
|
||||
ViewJoinCreatedEvent,
|
||||
} from "@budibase/types"
|
||||
|
||||
/* eslint-disable */
|
||||
|
@ -29,17 +31,9 @@ async function created(view: ViewV2, timestamp?: string | number) {
|
|||
await publishEvent(Event.VIEW_CREATED, properties, timestamp)
|
||||
}
|
||||
|
||||
async function updated(newView: ViewV2) {
|
||||
let viewJoins = 0
|
||||
for (const key in newView.schema) {
|
||||
if (newView.schema[key]?.columns) {
|
||||
viewJoins += Object.keys(newView.schema[key]?.columns).length
|
||||
}
|
||||
}
|
||||
async function updated(view: ViewV2) {
|
||||
const properties: ViewUpdatedEvent = {
|
||||
tableId: newView.tableId,
|
||||
groupedFilters: newView.queryUI?.groups?.length || 0,
|
||||
viewJoins,
|
||||
tableId: view.tableId,
|
||||
}
|
||||
await publishEvent(Event.VIEW_UPDATED, properties)
|
||||
}
|
||||
|
@ -59,16 +53,27 @@ async function exported(table: Table, format: TableExportFormat) {
|
|||
await publishEvent(Event.VIEW_EXPORTED, properties)
|
||||
}
|
||||
|
||||
async function filterCreated(view: View, timestamp?: string | number) {
|
||||
async function filterCreated(
|
||||
{ tableId, filterGroups }: { tableId: string; filterGroups: number },
|
||||
timestamp?: string | number
|
||||
) {
|
||||
const properties: ViewFilterCreatedEvent = {
|
||||
tableId: view.tableId,
|
||||
tableId,
|
||||
filterGroups,
|
||||
}
|
||||
await publishEvent(Event.VIEW_FILTER_CREATED, properties, timestamp)
|
||||
}
|
||||
|
||||
async function filterUpdated(view: View) {
|
||||
async function filterUpdated({
|
||||
tableId,
|
||||
filterGroups,
|
||||
}: {
|
||||
tableId: string
|
||||
filterGroups: number
|
||||
}) {
|
||||
const properties: ViewFilterUpdatedEvent = {
|
||||
tableId: view.tableId,
|
||||
tableId: tableId,
|
||||
filterGroups,
|
||||
}
|
||||
await publishEvent(Event.VIEW_FILTER_UPDATED, properties)
|
||||
}
|
||||
|
@ -80,10 +85,16 @@ async function filterDeleted(view: View) {
|
|||
await publishEvent(Event.VIEW_FILTER_DELETED, properties)
|
||||
}
|
||||
|
||||
async function calculationCreated(view: View, timestamp?: string | number) {
|
||||
async function calculationCreated(
|
||||
{
|
||||
tableId,
|
||||
calculationType,
|
||||
}: { tableId: string; calculationType: CalculationType },
|
||||
timestamp?: string | number
|
||||
) {
|
||||
const properties: ViewCalculationCreatedEvent = {
|
||||
tableId: view.tableId,
|
||||
calculation: view.calculation as ViewCalculation,
|
||||
tableId,
|
||||
calculation: calculationType,
|
||||
}
|
||||
await publishEvent(Event.VIEW_CALCULATION_CREATED, properties, timestamp)
|
||||
}
|
||||
|
@ -104,6 +115,13 @@ async function calculationDeleted(existingView: View) {
|
|||
await publishEvent(Event.VIEW_CALCULATION_DELETED, properties)
|
||||
}
|
||||
|
||||
async function viewJoinCreated(tableId: any, timestamp?: string | number) {
|
||||
const properties: ViewJoinCreatedEvent = {
|
||||
tableId,
|
||||
}
|
||||
await publishEvent(Event.VIEW_JOIN_CREATED, properties, timestamp)
|
||||
}
|
||||
|
||||
export default {
|
||||
created,
|
||||
updated,
|
||||
|
@ -115,4 +133,5 @@ export default {
|
|||
calculationCreated,
|
||||
calculationUpdated,
|
||||
calculationDeleted,
|
||||
viewJoinCreated,
|
||||
}
|
||||
|
|
|
@ -117,6 +117,7 @@ beforeAll(async () => {
|
|||
jest.spyOn(events.view, "calculationCreated")
|
||||
jest.spyOn(events.view, "calculationUpdated")
|
||||
jest.spyOn(events.view, "calculationDeleted")
|
||||
jest.spyOn(events.view, "viewJoinCreated")
|
||||
|
||||
jest.spyOn(events.plugin, "init")
|
||||
jest.spyOn(events.plugin, "imported")
|
||||
|
|
|
@ -65,31 +65,6 @@ export async function save(ctx: Ctx) {
|
|||
builderSocket?.emitTableUpdate(ctx, table)
|
||||
}
|
||||
|
||||
export async function filterEvents(existingView: View, newView: View) {
|
||||
const hasExistingFilters = !!(
|
||||
existingView &&
|
||||
existingView.filters &&
|
||||
existingView.filters.length
|
||||
)
|
||||
const hasNewFilters = !!(newView && newView.filters && newView.filters.length)
|
||||
|
||||
if (hasExistingFilters && !hasNewFilters) {
|
||||
await events.view.filterDeleted(newView)
|
||||
}
|
||||
|
||||
if (!hasExistingFilters && hasNewFilters) {
|
||||
await events.view.filterCreated(newView)
|
||||
}
|
||||
|
||||
if (
|
||||
hasExistingFilters &&
|
||||
hasNewFilters &&
|
||||
!isEqual(existingView.filters, newView.filters)
|
||||
) {
|
||||
await events.view.filterUpdated(newView)
|
||||
}
|
||||
}
|
||||
|
||||
export async function destroy(ctx: Ctx) {
|
||||
const db = context.getAppDB()
|
||||
const viewName = decodeURIComponent(ctx.params.viewName)
|
||||
|
|
|
@ -16,10 +16,14 @@ import {
|
|||
CountCalculationFieldMetadata,
|
||||
CreateViewResponse,
|
||||
UpdateViewResponse,
|
||||
View,
|
||||
Event,
|
||||
} from "@budibase/types"
|
||||
import { events } from "@budibase/backend-core"
|
||||
import { builderSocket, gridSocket } from "../../../websockets"
|
||||
import { helpers } from "@budibase/shared-core"
|
||||
import isEqual from "lodash/isEqual"
|
||||
import { publishEvent } from "@budibase/backend-core/src/events"
|
||||
|
||||
function stripUnknownFields(
|
||||
field: ViewFieldMetadata
|
||||
|
@ -164,6 +168,54 @@ export async function create(ctx: Ctx<CreateViewRequest, CreateViewResponse>) {
|
|||
gridSocket?.emitViewUpdate(ctx, result)
|
||||
}
|
||||
|
||||
async function handleViewEvents(existingView: ViewV2, view: ViewV2) {
|
||||
// Grouped filters
|
||||
if (view.queryUI?.groups) {
|
||||
const filterGroups = view.queryUI?.groups?.length || 0
|
||||
const properties = { filterGroups, tableId: view.tableId }
|
||||
if (!existingView?.queryUI) {
|
||||
await publishEvent(Event.VIEW_FILTER_CREATED, properties)
|
||||
await events.view.filterCreated(properties)
|
||||
} else {
|
||||
if (
|
||||
filterGroups >
|
||||
((existingView && existingView?.queryUI?.groups?.length) || 0)
|
||||
) {
|
||||
await events.view.filterUpdated(properties)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if new columns in the view
|
||||
for (const key in view.schema) {
|
||||
if (!existingView?.schema?.[key]) {
|
||||
const newColumn = view.schema[key]
|
||||
|
||||
// view calculations
|
||||
// @ts-expect-error non calculation types just won't have the calculationType field
|
||||
const calculationType = newColumn.calculationType
|
||||
if (calculationType) {
|
||||
// Send the event
|
||||
await events.view.calculationCreated({
|
||||
calculationType,
|
||||
tableId: view.tableId,
|
||||
})
|
||||
}
|
||||
|
||||
// view joins
|
||||
if (newColumn.columns) {
|
||||
for (const column in newColumn?.columns) {
|
||||
// if the new column is visible and it wasn't before
|
||||
if (!existingView?.schema?.[key].columns?.[column].visible) {
|
||||
// new view join exposing a column
|
||||
await events.view.viewJoinCreated({ tableId: view.tableId })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function update(ctx: Ctx<UpdateViewRequest, UpdateViewResponse>) {
|
||||
const view = ctx.request.body
|
||||
|
||||
|
@ -191,8 +243,12 @@ export async function update(ctx: Ctx<UpdateViewRequest, UpdateViewResponse>) {
|
|||
primaryDisplay: view.primaryDisplay,
|
||||
}
|
||||
|
||||
const { view: result } = await sdk.views.update(tableId, parsedView)
|
||||
const { view: result, existingView } = await sdk.views.update(
|
||||
tableId,
|
||||
parsedView
|
||||
)
|
||||
|
||||
await handleViewEvents(existingView, result)
|
||||
await events.view.updated(result)
|
||||
|
||||
ctx.body = { data: result }
|
||||
|
|
|
@ -42,7 +42,7 @@ import {
|
|||
} from "../../../integrations/tests/utils"
|
||||
import merge from "lodash/merge"
|
||||
import { quotas } from "@budibase/pro"
|
||||
import { db, roles, context } from "@budibase/backend-core"
|
||||
import { db, roles, context, events } from "@budibase/backend-core"
|
||||
|
||||
const descriptions = datasourceDescribe({ exclude: [DatabaseName.MONGODB] })
|
||||
|
||||
|
@ -129,6 +129,7 @@ if (descriptions.length) {
|
|||
id: expect.stringMatching(new RegExp(`${table._id!}_`)),
|
||||
version: 2,
|
||||
})
|
||||
expect(events.view.created).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("can persist views with all fields", async () => {
|
||||
|
@ -195,6 +196,7 @@ if (descriptions.length) {
|
|||
}
|
||||
|
||||
expect(res).toEqual(expected)
|
||||
expect(events.view.created).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("can create a view with just a query field, no queryUI, for backwards compatibility", async () => {
|
||||
|
@ -224,6 +226,7 @@ if (descriptions.length) {
|
|||
},
|
||||
}
|
||||
const res = await config.api.viewV2.create(newView)
|
||||
expect(events.view.created).toHaveBeenCalledTimes(1)
|
||||
|
||||
const expected: ViewV2 = {
|
||||
...newView,
|
||||
|
@ -283,6 +286,7 @@ if (descriptions.length) {
|
|||
}
|
||||
|
||||
const createdView = await config.api.viewV2.create(newView)
|
||||
expect(events.view.created).toHaveBeenCalledTimes(1)
|
||||
|
||||
expect(createdView).toEqual({
|
||||
...newView,
|
||||
|
@ -990,6 +994,7 @@ if (descriptions.length) {
|
|||
expect((await config.api.table.get(tableId)).views).toEqual({
|
||||
[view.name]: expected,
|
||||
})
|
||||
expect(events.view.updated).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("can update all fields", async () => {
|
||||
|
@ -1621,6 +1626,7 @@ if (descriptions.length) {
|
|||
field: "age",
|
||||
}
|
||||
await config.api.viewV2.update(view)
|
||||
expect(events.view.calculationCreated).toHaveBeenCalledTimes(1)
|
||||
|
||||
const { rows } = await config.api.row.search(view.id)
|
||||
expect(rows).toHaveLength(2)
|
||||
|
@ -2154,6 +2160,7 @@ if (descriptions.length) {
|
|||
}),
|
||||
})
|
||||
)
|
||||
expect(events.view.viewJoinCreated).not.toBeCalled()
|
||||
})
|
||||
|
||||
it("does not rename columns with the same name but from other tables", async () => {
|
||||
|
|
|
@ -118,6 +118,7 @@ export enum Event {
|
|||
VIEW_CALCULATION_CREATED = "view:calculation:created",
|
||||
VIEW_CALCULATION_UPDATED = "view:calculation:updated",
|
||||
VIEW_CALCULATION_DELETED = "view:calculation:deleted",
|
||||
VIEW_JOIN_CREATED = "view:join:created",
|
||||
|
||||
// ROWS
|
||||
ROWS_CREATED = "rows:created",
|
||||
|
@ -192,6 +193,9 @@ export enum Event {
|
|||
// AUDIT LOG
|
||||
AUDIT_LOGS_FILTERED = "audit_log:filtered",
|
||||
AUDIT_LOGS_DOWNLOADED = "audit_log:downloaded",
|
||||
|
||||
// ROW ACTION
|
||||
ROW_ACTION_CREATED = "row_action:created",
|
||||
}
|
||||
|
||||
export const UserGroupSyncEvents: Event[] = [
|
||||
|
@ -376,6 +380,7 @@ export const AuditedEventFriendlyName: Record<Event, string | undefined> = {
|
|||
[Event.VIEW_CALCULATION_CREATED]: undefined,
|
||||
[Event.VIEW_CALCULATION_UPDATED]: undefined,
|
||||
[Event.VIEW_CALCULATION_DELETED]: undefined,
|
||||
[Event.VIEW_JOIN_CREATED]: undefined,
|
||||
|
||||
// SERVED - NOT AUDITED
|
||||
[Event.SERVED_BUILDER]: undefined,
|
||||
|
@ -395,6 +400,9 @@ export const AuditedEventFriendlyName: Record<Event, string | undefined> = {
|
|||
// AUDIT LOG - NOT AUDITED
|
||||
[Event.AUDIT_LOGS_FILTERED]: undefined,
|
||||
[Event.AUDIT_LOGS_DOWNLOADED]: undefined,
|
||||
|
||||
// ROW ACTIONS - NOT AUDITED
|
||||
[Event.ROW_ACTION_CREATED]: undefined,
|
||||
}
|
||||
|
||||
// properties added at the final stage of the event pipeline
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ViewCalculation, ViewV2Type } from "../../documents"
|
||||
import { CalculationType, ViewCalculation, ViewV2Type } from "../../documents"
|
||||
import { BaseEvent, TableExportFormat } from "./event"
|
||||
|
||||
export interface ViewCreatedEvent extends BaseEvent {
|
||||
|
@ -9,8 +9,6 @@ export interface ViewCreatedEvent extends BaseEvent {
|
|||
|
||||
export interface ViewUpdatedEvent extends BaseEvent {
|
||||
tableId: string
|
||||
groupedFilters: number
|
||||
viewJoins: number
|
||||
}
|
||||
|
||||
export interface ViewDeletedEvent extends BaseEvent {
|
||||
|
@ -24,10 +22,12 @@ export interface ViewExportedEvent extends BaseEvent {
|
|||
|
||||
export interface ViewFilterCreatedEvent extends BaseEvent {
|
||||
tableId: string
|
||||
filterGroups: number
|
||||
}
|
||||
|
||||
export interface ViewFilterUpdatedEvent extends BaseEvent {
|
||||
tableId: string
|
||||
filterGroups: number
|
||||
}
|
||||
|
||||
export interface ViewFilterDeletedEvent extends BaseEvent {
|
||||
|
@ -36,7 +36,7 @@ export interface ViewFilterDeletedEvent extends BaseEvent {
|
|||
|
||||
export interface ViewCalculationCreatedEvent extends BaseEvent {
|
||||
tableId: string
|
||||
calculation: ViewCalculation
|
||||
calculation: CalculationType
|
||||
}
|
||||
|
||||
export interface ViewCalculationUpdatedEvent extends BaseEvent {
|
||||
|
@ -48,3 +48,7 @@ export interface ViewCalculationDeletedEvent extends BaseEvent {
|
|||
tableId: string
|
||||
calculation: ViewCalculation
|
||||
}
|
||||
|
||||
export interface ViewJoinCreatedEvent extends BaseEvent {
|
||||
tableId: string
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue