View/Table/Row Import/Export events

This commit is contained in:
Rory Powell 2022-04-11 22:32:12 +01:00
parent 5ab9f1a9c5
commit 6db5c62e48
12 changed files with 180 additions and 62 deletions

View File

@ -103,6 +103,7 @@ exports.Events = {
// ROW
// ROW_CREATED: "row:created",
ROW_IMPORT: "row:import",
// COMPONENT
COMPONENT_CREATED: "component:created",

View File

@ -9,7 +9,8 @@ const layout = require("./layout")
const org = require("./org")
const query = require("./query")
const role = require("./role")
const row = require("./screen")
const screen = require("./screen")
const row = require("./row")
const table = require("./table")
const serve = require("./serve")
const user = require("./user")
@ -27,6 +28,7 @@ module.exports = {
org,
query,
role,
screen,
row,
table,
serve,

View File

@ -1,7 +1,14 @@
// const events = require("../events")
// const { Events } = require("../constants")
const events = require("../events")
const { Events } = require("../constants")
/* eslint-disable */
// exports.created = () => {
// const properties = {}
// events.processEvent(Events.ROW_CREATED, properties)
// }
exports.import = (table, format, count) => {
const properties = {}
events.processEvent(Events.ROW_IMPORT, properties)
}

View File

@ -1,29 +1,29 @@
const events = require("../events")
const { Events } = require("../constants")
exports.created = () => {
/* eslint-disable */
exports.created = table => {
const properties = {}
events.processEvent(Events.TABLE_CREATED, properties)
}
exports.updated = () => {
exports.updated = table => {
const properties = {}
events.processEvent(Events.TABLE_UPDATED, properties)
}
exports.deleted = () => {
exports.deleted = table => {
const properties = {}
events.processEvent(Events.TABLE_DELETED, properties)
}
// TODO
exports.exported = () => {
exports.exported = (table, format) => {
const properties = {}
events.processEvent(Events.TABLE_EXPORTED, properties)
}
// TODO
exports.imported = () => {
exports.imported = (table, format) => {
const properties = {}
events.processEvent(Events.TABLE_IMPORTED, properties)
}

View File

@ -1,6 +1,8 @@
const events = require("../events")
const { Events } = require("../constants")
/* eslint-disable */
exports.created = () => {
const properties = {}
events.processEvent(Events.VIEW_CREATED, properties)
@ -18,8 +20,7 @@ exports.deleted = () => {
events.processEvent(Events.VIEW_DELETED, properties)
}
// TODO
exports.exported = () => {
exports.exported = (table, format) => {
const properties = {}
events.processEvent(Events.VIEW_EXPORTED, properties)
}

View File

@ -69,7 +69,9 @@ jest.mock("../../../events", () => {
assigned: jest.fn(),
unassigned: jest.fn(),
},
row: {},
row: {
import: jest.fn(),
},
screen: {
created: jest.fn(),
deleted: jest.fn(),
@ -103,7 +105,9 @@ jest.mock("../../../events", () => {
imported: jest.fn(),
permissionUpdated: jest.fn(),
},
view: {},
view: {
exported: jest.fn(),
},
}
})

View File

@ -19,6 +19,7 @@ const { cloneDeep } = require("lodash/fp")
const csvParser = require("../../../utilities/csvParser")
const { handleRequest } = require("../row/external")
const { getAppDB } = require("@budibase/backend-core/context")
const { events } = require("@budibase/backend-core")
async function makeTableRequest(
datasource,
@ -314,5 +315,6 @@ exports.bulkImport = async function (ctx) {
await handleRequest(DataSourceOperation.BULK_CREATE, table._id, {
rows,
})
events.row.import(table, "csv", rows.length)
return table
}

View File

@ -56,12 +56,17 @@ exports.find = async function (ctx) {
exports.save = async function (ctx) {
const appId = ctx.appId
const table = ctx.request.body
const importFormat =
table.dataImport && table.dataImport.csvString ? "csv" : undefined
const savedTable = await pickApi({ table }).save(ctx)
if (!table._id) {
events.table.created(savedTable)
} else {
events.table.updated(savedTable)
}
if (importFormat) {
events.table.imported(savedTable, importFormat)
}
ctx.status = 200
ctx.message = `Table ${table.name} saved successfully.`
ctx.eventEmitter &&

View File

@ -26,7 +26,8 @@ import { getViews, saveView } from "../view/utils"
import viewTemplate from "../view/viewBuilder"
const { getAppDB } = require("@budibase/backend-core/context")
import { cloneDeep } from "lodash/fp"
import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro"
import { quotas } from "@budibase/pro"
import { events } from "@budibase/backend-core"
export async function clearColumns(table: any, columnNames: any) {
const db = getAppDB()
@ -148,8 +149,7 @@ export async function handleDataImport(user: any, table: any, dataImport: any) {
}
await quotas.addRows(finalData.length, () => db.bulkDocs(finalData))
let response = await db.put(table)
table._rev = response._rev
events.row.import(table, "csv", finalData.length)
return table
}
@ -245,7 +245,7 @@ class TableSaveFunctions {
// after saving
async after(table: any) {
table = await handleSearchIndexes(table)
table = await handleDataImport(this.user, table, this.dataImport)
await handleDataImport(this.user, table, this.dataImport)
return table
}

View File

@ -6,6 +6,8 @@ const { fetchView } = require("../row")
const { getTable } = require("../table/utils")
const { FieldTypes } = require("../../../constants")
const { getAppDB } = require("@budibase/backend-core/context")
const { events } = require("@budibase/backend-core")
const { DocumentTypes } = require("../../../db/utils")
exports.fetch = async ctx => {
ctx.body = await getViews()
@ -79,9 +81,9 @@ exports.exportView = async ctx => {
let rows = ctx.body
let schema = view && view.meta && view.meta.schema
const tableId = ctx.params.tableId || view.meta.tableId
const table = await getTable(tableId)
if (!schema) {
const tableId = ctx.params.tableId || view.meta.tableId
const table = await getTable(tableId)
schema = table.schema
}
@ -116,4 +118,10 @@ exports.exportView = async ctx => {
// send down the file
ctx.attachment(filename)
ctx.body = apiFileReturn(exporter(headers, rows))
if (viewName.startsWith(DocumentTypes.TABLE)) {
events.table.exported(table, format)
} else {
events.view.exported(table, format)
}
}

View File

@ -14,37 +14,56 @@ describe("/tables", () => {
})
describe("create", () => {
it("returns a success message when the table is successfully created", async () => {
const res = await request
beforeEach(() => {
jest.clearAllMocks()
})
const createTable = (table) => {
if (!table) {
table = basicTable()
}
return request
.post(`/api/tables`)
.send({
name: "TestTable",
key: "name",
schema: {
name: {type: "string"}
}
})
.send(table)
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
}
it("returns a success message when the table is successfully created", async () => {
const res = await createTable()
expect(res.res.statusMessage).toEqual("Table TestTable saved successfully.")
expect(res.body.name).toEqual("TestTable")
expect(events.table.created).toBeCalledTimes(1)
expect(events.table.created).toBeCalledWith(res.body)
})
it("creates a table via data import CSV", async () => {
const table = basicTable()
table.dataImport = {
csvString: "\"name\",\"description\"\n\"test-name\",\"test-desc\"",
}
table.dataImport.schema = table.schema
const res = await createTable(table)
expect(events.table.created).toBeCalledTimes(1)
expect(events.table.created).toBeCalledWith(res.body)
expect(events.table.imported).toBeCalledTimes(1)
expect(events.table.imported).toBeCalledWith(res.body, "csv")
expect(events.row.import).toBeCalledTimes(1)
expect(events.row.import).toBeCalledWith(res.body, "csv", 1)
})
it("should apply authorization to endpoint", async () => {
await checkBuilderEndpoint({
config,
method: "POST",
url: `/api/tables`,
body: {
name: "TestTable",
key: "name",
schema: {
name: {type: "string"}
}
}
body: basicTable()
})
})
})
@ -124,6 +143,30 @@ describe("/tables", () => {
})
})
describe("import", () => {
it("imports rows successfully", async () => {
const table = await config.createTable()
const importRequest = {
dataImport: {
csvString: "\"name\",\"description\"\n\"test-name\",\"test-desc\"",
schema: table.schema
}
}
jest.clearAllMocks()
await request
.post(`/api/tables/${table._id}/import`)
.send(importRequest)
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
expect(events.table.created).not.toHaveBeenCalled()
expect(events.row.import).toBeCalledTimes(1)
expect(events.row.import).toBeCalledWith(table, "csv", 1)
})
})
describe("fetch", () => {
let testTable

View File

@ -1,4 +1,5 @@
const setup = require("./utilities")
const { events } = require("@budibase/backend-core")
function priceTable() {
return {
@ -205,33 +206,77 @@ describe("/views", () => {
})
describe("exportView", () => {
it("should be able to export a view", async () => {
await config.createTable(priceTable())
await config.createRow()
beforeEach(() => {
jest.clearAllMocks()
})
const setupExport = async () => {
const table = await config.createTable()
await config.createRow({ name: "test-name", description: "test-desc" })
return table
}
const exportView = async (viewName, format) => {
return request
.get(`/api/views/export?view=${viewName}&format=${format}`)
.set(config.defaultHeaders())
.expect(200)
}
const assertJsonExport = (res) => {
const rows = JSON.parse(res.text)
expect(rows.length).toBe(1)
expect(rows[0].name).toBe("test-name")
expect(rows[0].description).toBe("test-desc")
}
const assertCSVExport = (res) => {
expect(res.text).toBe("\"name\",\"description\"\n\"test-name\",\"test-desc\"")
}
it("should be able to export a table as JSON", async () => {
const table = await setupExport()
const res = await exportView(table._id, "json")
assertJsonExport(res)
expect(events.table.exported).toBeCalledTimes(1)
expect(events.table.exported).toBeCalledWith(table, "json")
})
it("should be able to export a table as CSV", async () => {
const table = await setupExport()
const res = await exportView(table._id, "csv")
assertCSVExport(res)
expect(events.table.exported).toBeCalledTimes(1)
expect(events.table.exported).toBeCalledWith(table, "csv")
})
it("should be able to export a view as JSON", async () => {
let table = await setupExport()
const view = await config.createView()
let res = await request
.get(`/api/views/export?view=${view.name}&format=json`)
.set(config.defaultHeaders())
.expect(200)
let error
try {
const obj = JSON.parse(res.text)
expect(obj.length).toBe(1)
} catch (err) {
error = err
}
expect(error).toBeUndefined()
res = await request
.get(`/api/views/export?view=${view.name}&format=csv`)
.set(config.defaultHeaders())
.expect(200)
// this shouldn't be JSON
try {
JSON.parse(res.text)
} catch (err) {
error = err
}
expect(error).toBeDefined()
table = await config.getTable(table._id)
let res = await exportView(view.name, "json")
assertJsonExport(res)
expect(events.view.exported).toBeCalledTimes(1)
expect(events.view.exported).toBeCalledWith(table, "json")
})
it("should be able to export a view as CSV", async () => {
let table = await setupExport()
const view = await config.createView()
table = await config.getTable(table._id)
let res = await exportView(view.name, "csv")
assertCSVExport(res)
expect(events.view.exported).toBeCalledTimes(1)
expect(events.view.exported).toBeCalledWith(table, "csv")
})
})
})