View/Filter/Calculation events
This commit is contained in:
parent
6db5c62e48
commit
957e90fe86
|
@ -97,9 +97,11 @@ exports.Events = {
|
|||
VIEW_DELETED: "view:deleted",
|
||||
VIEW_EXPORTED: "view:exported",
|
||||
VIEW_FILTER_CREATED: "view:filter:created",
|
||||
VIEW_FILTER_DELETED: "view:filter:created",
|
||||
VIEW_FILTER_UPDATED: "view:filter:updated",
|
||||
VIEW_FILTER_DELETED: "view:filter:deleted",
|
||||
VIEW_CALCULATION_CREATED: "view:calculation:created",
|
||||
VIEW_CALCULATION_DELETED: "view:calculation:created",
|
||||
VIEW_CALCULATION_UPDATED: "view:calculation:updated",
|
||||
VIEW_CALCULATION_DELETED: "view:calculation:deleted",
|
||||
|
||||
// ROW
|
||||
// ROW_CREATED: "row:created",
|
||||
|
|
|
@ -8,13 +8,11 @@ exports.created = () => {
|
|||
events.processEvent(Events.VIEW_CREATED, properties)
|
||||
}
|
||||
|
||||
// TODO
|
||||
exports.updated = () => {
|
||||
const properties = {}
|
||||
events.processEvent(Events.VIEW_UPDATED, properties)
|
||||
}
|
||||
|
||||
// TODO
|
||||
exports.deleted = () => {
|
||||
const properties = {}
|
||||
events.processEvent(Events.VIEW_DELETED, properties)
|
||||
|
@ -25,25 +23,31 @@ exports.exported = (table, format) => {
|
|||
events.processEvent(Events.VIEW_EXPORTED, properties)
|
||||
}
|
||||
|
||||
// TODO
|
||||
exports.filterCreated = () => {
|
||||
const properties = {}
|
||||
events.processEvent(Events.VIEW_FILTER_CREATED, properties)
|
||||
}
|
||||
|
||||
// TODO
|
||||
exports.filterUpdated = () => {
|
||||
const properties = {}
|
||||
events.processEvent(Events.VIEW_FILTER_UPDATED, properties)
|
||||
}
|
||||
|
||||
exports.filterDeleted = () => {
|
||||
const properties = {}
|
||||
events.processEvent(Events.VIEW_FILTER_DELETED, properties)
|
||||
}
|
||||
|
||||
// TODO
|
||||
exports.calculationCreated = () => {
|
||||
const properties = {}
|
||||
events.processEvent(Events.VIEW_CALCULATION_CREATED, properties)
|
||||
}
|
||||
|
||||
// TODO
|
||||
exports.calculationUpdated = () => {
|
||||
const properties = {}
|
||||
events.processEvent(Events.VIEW_CALCULATION_UPDATED, properties)
|
||||
}
|
||||
|
||||
exports.calculationDeleted = () => {
|
||||
const properties = {}
|
||||
events.processEvent(Events.VIEW_CALCULATION_DELETED, properties)
|
||||
|
|
|
@ -106,7 +106,16 @@ jest.mock("../../../events", () => {
|
|||
permissionUpdated: jest.fn(),
|
||||
},
|
||||
view: {
|
||||
created: jest.fn(),
|
||||
updated: jest.fn(),
|
||||
deleted: jest.fn(),
|
||||
exported: jest.fn(),
|
||||
filterCreated: jest.fn(),
|
||||
filterUpdated: jest.fn(),
|
||||
filterDeleted: jest.fn(),
|
||||
calculationCreated: jest.fn(),
|
||||
calculationUpdated: jest.fn(),
|
||||
calculationDeleted: jest.fn(),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
|
@ -8,6 +8,7 @@ const { FieldTypes } = require("../../../constants")
|
|||
const { getAppDB } = require("@budibase/backend-core/context")
|
||||
const { events } = require("@budibase/backend-core")
|
||||
const { DocumentTypes } = require("../../../db/utils")
|
||||
const { cloneDeep, isEqual } = require("lodash")
|
||||
|
||||
exports.fetch = async ctx => {
|
||||
ctx.body = await getViews()
|
||||
|
@ -17,24 +18,28 @@ exports.save = async ctx => {
|
|||
const db = getAppDB()
|
||||
const { originalName, ...viewToSave } = ctx.request.body
|
||||
const view = viewTemplate(viewToSave)
|
||||
const viewName = viewToSave.name
|
||||
|
||||
if (!viewToSave.name) {
|
||||
if (!viewName) {
|
||||
ctx.throw(400, "Cannot create view without a name")
|
||||
}
|
||||
|
||||
await saveView(originalName, viewToSave.name, view)
|
||||
await saveView(originalName, viewName, view)
|
||||
|
||||
// add views to table document
|
||||
const table = await db.get(ctx.request.body.tableId)
|
||||
const existingTable = await db.get(ctx.request.body.tableId)
|
||||
const table = cloneDeep(existingTable)
|
||||
if (!table.views) table.views = {}
|
||||
if (!view.meta.schema) {
|
||||
view.meta.schema = table.schema
|
||||
}
|
||||
table.views[viewToSave.name] = view.meta
|
||||
table.views[viewName] = view.meta
|
||||
if (originalName) {
|
||||
delete table.views[originalName]
|
||||
existingTable.views[viewName] = existingTable.views[originalName]
|
||||
}
|
||||
await db.put(table)
|
||||
handleViewEvents(existingTable.views[viewName], table.views[viewName])
|
||||
|
||||
ctx.body = {
|
||||
...table.views[viewToSave.name],
|
||||
|
@ -42,6 +47,62 @@ exports.save = async ctx => {
|
|||
}
|
||||
}
|
||||
|
||||
const calculationEvents = (existingView, newView) => {
|
||||
const existingCalculation = existingView && existingView.calculation
|
||||
const newCalculation = newView && newView.calculation
|
||||
|
||||
if (existingCalculation && !newCalculation) {
|
||||
events.view.calculationDeleted()
|
||||
}
|
||||
|
||||
if (!existingCalculation && newCalculation) {
|
||||
events.view.calculationCreated()
|
||||
}
|
||||
|
||||
if (
|
||||
existingCalculation &&
|
||||
newCalculation &&
|
||||
existingCalculation !== newCalculation
|
||||
) {
|
||||
events.view.calculationUpdated()
|
||||
}
|
||||
}
|
||||
|
||||
const filterEvents = (existingView, newView) => {
|
||||
const hasExistingFilters = !!(
|
||||
existingView &&
|
||||
existingView.filters &&
|
||||
existingView.filters.length
|
||||
)
|
||||
const hasNewFilters = !!(newView && newView.filters && newView.filters.length)
|
||||
|
||||
if (hasExistingFilters && !hasNewFilters) {
|
||||
events.view.filterDeleted()
|
||||
}
|
||||
|
||||
if (!hasExistingFilters && hasNewFilters) {
|
||||
events.view.filterCreated()
|
||||
}
|
||||
|
||||
if (
|
||||
hasExistingFilters &&
|
||||
hasNewFilters &&
|
||||
!isEqual(existingView.filters, newView.filters)
|
||||
) {
|
||||
events.view.filterUpdated()
|
||||
}
|
||||
}
|
||||
|
||||
const handleViewEvents = (existingView, newView) => {
|
||||
if (!existingView) {
|
||||
events.view.created()
|
||||
} else {
|
||||
events.view.updated()
|
||||
}
|
||||
calculationEvents(existingView, newView)
|
||||
filterEvents(existingView, newView)
|
||||
}
|
||||
|
||||
exports.destroy = async ctx => {
|
||||
const db = getAppDB()
|
||||
const viewName = decodeURI(ctx.params.viewName)
|
||||
|
@ -49,6 +110,7 @@ exports.destroy = async ctx => {
|
|||
const table = await db.get(view.meta.tableId)
|
||||
delete table.views[viewName]
|
||||
await db.put(table)
|
||||
events.view.deleted()
|
||||
|
||||
ctx.body = view
|
||||
}
|
||||
|
|
|
@ -30,27 +30,70 @@ describe("/views", () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await config.init()
|
||||
table = await config.createTable(priceTable())
|
||||
})
|
||||
|
||||
const saveView = async (view) => {
|
||||
const viewToSave = {
|
||||
name: "TestView",
|
||||
field: "Price",
|
||||
calculation: "stats",
|
||||
tableId: table._id,
|
||||
...view
|
||||
}
|
||||
return request
|
||||
.post(`/api/views`)
|
||||
.send(viewToSave)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
}
|
||||
|
||||
describe("create", () => {
|
||||
beforeEach(async () => {
|
||||
table = await config.createTable(priceTable())
|
||||
})
|
||||
|
||||
it("returns a success message when the view is successfully created", async () => {
|
||||
const res = await request
|
||||
.post(`/api/views`)
|
||||
.send({
|
||||
name: "TestView",
|
||||
field: "Price",
|
||||
calculation: "stats",
|
||||
tableId: table._id,
|
||||
})
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
const res = await saveView()
|
||||
expect(res.body.tableId).toBe(table._id)
|
||||
expect(events.view.created).toBeCalledTimes(1)
|
||||
})
|
||||
|
||||
it("creates a view with a calculation", async () => {
|
||||
jest.clearAllMocks()
|
||||
|
||||
const res = await saveView({ calculation: "count" })
|
||||
|
||||
expect(res.body.tableId).toBe(table._id)
|
||||
expect(events.view.created).toBeCalledTimes(1)
|
||||
expect(events.view.updated).not.toBeCalled()
|
||||
expect(events.view.calculationCreated).toBeCalledTimes(1)
|
||||
expect(events.view.calculationUpdated).not.toBeCalled()
|
||||
expect(events.view.calculationDeleted).not.toBeCalled()
|
||||
expect(events.view.filterCreated).not.toBeCalled()
|
||||
expect(events.view.filterUpdated).not.toBeCalled()
|
||||
expect(events.view.filterDeleted).not.toBeCalled()
|
||||
})
|
||||
|
||||
it("creates a view with a filter", async () => {
|
||||
jest.clearAllMocks()
|
||||
|
||||
const res = await saveView({
|
||||
calculation: null,
|
||||
filters: [{
|
||||
value: "1",
|
||||
condition: "EQUALS",
|
||||
key: "price"
|
||||
}],
|
||||
})
|
||||
|
||||
expect(res.body.tableId).toBe(table._id)
|
||||
expect(events.view.created).toBeCalledTimes(1)
|
||||
expect(events.view.updated).not.toBeCalled()
|
||||
expect(events.view.calculationCreated).not.toBeCalled()
|
||||
expect(events.view.calculationUpdated).not.toBeCalled()
|
||||
expect(events.view.calculationDeleted).not.toBeCalled()
|
||||
expect(events.view.filterCreated).toBeCalledTimes(1)
|
||||
expect(events.view.filterUpdated).not.toBeCalled()
|
||||
expect(events.view.filterDeleted).not.toBeCalled()
|
||||
})
|
||||
|
||||
it("updates the table row with the new view metadata", async () => {
|
||||
|
@ -102,6 +145,100 @@ describe("/views", () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe("update", () => {
|
||||
it("updates a view with no calculation or filter changed", async () => {
|
||||
await saveView()
|
||||
jest.clearAllMocks()
|
||||
|
||||
await saveView()
|
||||
|
||||
expect(events.view.created).not.toBeCalled()
|
||||
expect(events.view.updated).toBeCalledTimes(1)
|
||||
expect(events.view.calculationCreated).not.toBeCalled()
|
||||
expect(events.view.calculationUpdated).not.toBeCalled()
|
||||
expect(events.view.calculationDeleted).not.toBeCalled()
|
||||
expect(events.view.filterCreated).not.toBeCalled()
|
||||
expect(events.view.filterUpdated).not.toBeCalled()
|
||||
expect(events.view.filterDeleted).not.toBeCalled()
|
||||
})
|
||||
|
||||
it("updates a view calculation", async () => {
|
||||
await saveView({ calculation: "sum" })
|
||||
jest.clearAllMocks()
|
||||
|
||||
await saveView({ calculation: "count" })
|
||||
|
||||
expect(events.view.created).not.toBeCalled()
|
||||
expect(events.view.updated).toBeCalledTimes(1)
|
||||
expect(events.view.calculationCreated).not.toBeCalled()
|
||||
expect(events.view.calculationUpdated).toBeCalledTimes(1)
|
||||
expect(events.view.calculationDeleted).not.toBeCalled()
|
||||
expect(events.view.filterCreated).not.toBeCalled()
|
||||
expect(events.view.filterUpdated).not.toBeCalled()
|
||||
expect(events.view.filterDeleted).not.toBeCalled()
|
||||
})
|
||||
|
||||
it("deletes a view calculation", async () => {
|
||||
await saveView({ calculation: "sum" })
|
||||
jest.clearAllMocks()
|
||||
|
||||
await saveView({ calculation: null })
|
||||
|
||||
expect(events.view.created).not.toBeCalled()
|
||||
expect(events.view.updated).toBeCalledTimes(1)
|
||||
expect(events.view.calculationCreated).not.toBeCalled()
|
||||
expect(events.view.calculationUpdated).not.toBeCalled()
|
||||
expect(events.view.calculationDeleted).toBeCalledTimes(1)
|
||||
expect(events.view.filterCreated).not.toBeCalled()
|
||||
expect(events.view.filterUpdated).not.toBeCalled()
|
||||
expect(events.view.filterDeleted).not.toBeCalled()
|
||||
})
|
||||
|
||||
it("updates a view filter", async () => {
|
||||
await saveView({ filters: [{
|
||||
value: "1",
|
||||
condition: "EQUALS",
|
||||
key: "price"
|
||||
}] })
|
||||
jest.clearAllMocks()
|
||||
|
||||
await saveView({ filters: [{
|
||||
value: "2",
|
||||
condition: "EQUALS",
|
||||
key: "price"
|
||||
}] })
|
||||
|
||||
expect(events.view.created).not.toBeCalled()
|
||||
expect(events.view.updated).toBeCalledTimes(1)
|
||||
expect(events.view.calculationCreated).not.toBeCalled()
|
||||
expect(events.view.calculationUpdated).not.toBeCalled()
|
||||
expect(events.view.calculationDeleted).not.toBeCalled()
|
||||
expect(events.view.filterCreated).not.toBeCalled()
|
||||
expect(events.view.filterUpdated).toBeCalledTimes(1)
|
||||
expect(events.view.filterDeleted).not.toBeCalled()
|
||||
})
|
||||
|
||||
it("deletes a view filter", async () => {
|
||||
await saveView({ filters: [{
|
||||
value: "1",
|
||||
condition: "EQUALS",
|
||||
key: "price"
|
||||
}] })
|
||||
jest.clearAllMocks()
|
||||
|
||||
await saveView({ filters: [] })
|
||||
|
||||
expect(events.view.created).not.toBeCalled()
|
||||
expect(events.view.updated).toBeCalledTimes(1)
|
||||
expect(events.view.calculationCreated).not.toBeCalled()
|
||||
expect(events.view.calculationUpdated).not.toBeCalled()
|
||||
expect(events.view.calculationDeleted).not.toBeCalled()
|
||||
expect(events.view.filterCreated).not.toBeCalled()
|
||||
expect(events.view.filterUpdated).not.toBeCalled()
|
||||
expect(events.view.filterDeleted).toBeCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe("fetch", () => {
|
||||
beforeEach(async () => {
|
||||
table = await config.createTable(priceTable())
|
||||
|
@ -125,10 +262,6 @@ describe("/views", () => {
|
|||
})
|
||||
|
||||
describe("query", () => {
|
||||
beforeEach(async () => {
|
||||
table = await config.createTable(priceTable())
|
||||
})
|
||||
|
||||
it("returns data for the created view", async () => {
|
||||
await config.createView({
|
||||
name: "TestView",
|
||||
|
@ -202,6 +335,7 @@ describe("/views", () => {
|
|||
.expect(200)
|
||||
expect(res.body.map).toBeDefined()
|
||||
expect(res.body.meta.tableId).toEqual(table._id)
|
||||
expect(events.view.deleted).toBeCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in New Issue