View/Table/Row Import/Export events
This commit is contained in:
parent
2c682d3507
commit
06d70266cd
|
@ -103,6 +103,7 @@ exports.Events = {
|
||||||
|
|
||||||
// ROW
|
// ROW
|
||||||
// ROW_CREATED: "row:created",
|
// ROW_CREATED: "row:created",
|
||||||
|
ROW_IMPORT: "row:import",
|
||||||
|
|
||||||
// COMPONENT
|
// COMPONENT
|
||||||
COMPONENT_CREATED: "component:created",
|
COMPONENT_CREATED: "component:created",
|
||||||
|
|
|
@ -9,7 +9,8 @@ const layout = require("./layout")
|
||||||
const org = require("./org")
|
const org = require("./org")
|
||||||
const query = require("./query")
|
const query = require("./query")
|
||||||
const role = require("./role")
|
const role = require("./role")
|
||||||
const row = require("./screen")
|
const screen = require("./screen")
|
||||||
|
const row = require("./row")
|
||||||
const table = require("./table")
|
const table = require("./table")
|
||||||
const serve = require("./serve")
|
const serve = require("./serve")
|
||||||
const user = require("./user")
|
const user = require("./user")
|
||||||
|
@ -27,6 +28,7 @@ module.exports = {
|
||||||
org,
|
org,
|
||||||
query,
|
query,
|
||||||
role,
|
role,
|
||||||
|
screen,
|
||||||
row,
|
row,
|
||||||
table,
|
table,
|
||||||
serve,
|
serve,
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
// const events = require("../events")
|
const events = require("../events")
|
||||||
// const { Events } = require("../constants")
|
const { Events } = require("../constants")
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
// exports.created = () => {
|
// exports.created = () => {
|
||||||
// const properties = {}
|
// const properties = {}
|
||||||
// events.processEvent(Events.ROW_CREATED, properties)
|
// events.processEvent(Events.ROW_CREATED, properties)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
exports.import = (table, format, count) => {
|
||||||
|
const properties = {}
|
||||||
|
events.processEvent(Events.ROW_IMPORT, properties)
|
||||||
|
}
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
const events = require("../events")
|
const events = require("../events")
|
||||||
const { Events } = require("../constants")
|
const { Events } = require("../constants")
|
||||||
|
|
||||||
exports.created = () => {
|
/* eslint-disable */
|
||||||
|
|
||||||
|
exports.created = table => {
|
||||||
const properties = {}
|
const properties = {}
|
||||||
events.processEvent(Events.TABLE_CREATED, properties)
|
events.processEvent(Events.TABLE_CREATED, properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.updated = () => {
|
exports.updated = table => {
|
||||||
const properties = {}
|
const properties = {}
|
||||||
events.processEvent(Events.TABLE_UPDATED, properties)
|
events.processEvent(Events.TABLE_UPDATED, properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.deleted = () => {
|
exports.deleted = table => {
|
||||||
const properties = {}
|
const properties = {}
|
||||||
events.processEvent(Events.TABLE_DELETED, properties)
|
events.processEvent(Events.TABLE_DELETED, properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
exports.exported = (table, format) => {
|
||||||
exports.exported = () => {
|
|
||||||
const properties = {}
|
const properties = {}
|
||||||
events.processEvent(Events.TABLE_EXPORTED, properties)
|
events.processEvent(Events.TABLE_EXPORTED, properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
exports.imported = (table, format) => {
|
||||||
exports.imported = () => {
|
|
||||||
const properties = {}
|
const properties = {}
|
||||||
events.processEvent(Events.TABLE_IMPORTED, properties)
|
events.processEvent(Events.TABLE_IMPORTED, properties)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
const events = require("../events")
|
const events = require("../events")
|
||||||
const { Events } = require("../constants")
|
const { Events } = require("../constants")
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
exports.created = () => {
|
exports.created = () => {
|
||||||
const properties = {}
|
const properties = {}
|
||||||
events.processEvent(Events.VIEW_CREATED, properties)
|
events.processEvent(Events.VIEW_CREATED, properties)
|
||||||
|
@ -18,8 +20,7 @@ exports.deleted = () => {
|
||||||
events.processEvent(Events.VIEW_DELETED, properties)
|
events.processEvent(Events.VIEW_DELETED, properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
exports.exported = (table, format) => {
|
||||||
exports.exported = () => {
|
|
||||||
const properties = {}
|
const properties = {}
|
||||||
events.processEvent(Events.VIEW_EXPORTED, properties)
|
events.processEvent(Events.VIEW_EXPORTED, properties)
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,9 @@ jest.mock("../../../events", () => {
|
||||||
assigned: jest.fn(),
|
assigned: jest.fn(),
|
||||||
unassigned: jest.fn(),
|
unassigned: jest.fn(),
|
||||||
},
|
},
|
||||||
row: {},
|
row: {
|
||||||
|
import: jest.fn(),
|
||||||
|
},
|
||||||
screen: {
|
screen: {
|
||||||
created: jest.fn(),
|
created: jest.fn(),
|
||||||
deleted: jest.fn(),
|
deleted: jest.fn(),
|
||||||
|
@ -103,7 +105,9 @@ jest.mock("../../../events", () => {
|
||||||
imported: jest.fn(),
|
imported: jest.fn(),
|
||||||
permissionUpdated: jest.fn(),
|
permissionUpdated: jest.fn(),
|
||||||
},
|
},
|
||||||
view: {},
|
view: {
|
||||||
|
exported: jest.fn(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ const { cloneDeep } = require("lodash/fp")
|
||||||
const csvParser = require("../../../utilities/csvParser")
|
const csvParser = require("../../../utilities/csvParser")
|
||||||
const { handleRequest } = require("../row/external")
|
const { handleRequest } = require("../row/external")
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
const { events } = require("@budibase/backend-core")
|
||||||
|
|
||||||
async function makeTableRequest(
|
async function makeTableRequest(
|
||||||
datasource,
|
datasource,
|
||||||
|
@ -314,5 +315,6 @@ exports.bulkImport = async function (ctx) {
|
||||||
await handleRequest(DataSourceOperation.BULK_CREATE, table._id, {
|
await handleRequest(DataSourceOperation.BULK_CREATE, table._id, {
|
||||||
rows,
|
rows,
|
||||||
})
|
})
|
||||||
|
events.row.import(table, "csv", rows.length)
|
||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,12 +56,17 @@ exports.find = async function (ctx) {
|
||||||
exports.save = async function (ctx) {
|
exports.save = async function (ctx) {
|
||||||
const appId = ctx.appId
|
const appId = ctx.appId
|
||||||
const table = ctx.request.body
|
const table = ctx.request.body
|
||||||
|
const importFormat =
|
||||||
|
table.dataImport && table.dataImport.csvString ? "csv" : undefined
|
||||||
const savedTable = await pickApi({ table }).save(ctx)
|
const savedTable = await pickApi({ table }).save(ctx)
|
||||||
if (!table._id) {
|
if (!table._id) {
|
||||||
events.table.created(savedTable)
|
events.table.created(savedTable)
|
||||||
} else {
|
} else {
|
||||||
events.table.updated(savedTable)
|
events.table.updated(savedTable)
|
||||||
}
|
}
|
||||||
|
if (importFormat) {
|
||||||
|
events.table.imported(savedTable, importFormat)
|
||||||
|
}
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
ctx.message = `Table ${table.name} saved successfully.`
|
ctx.message = `Table ${table.name} saved successfully.`
|
||||||
ctx.eventEmitter &&
|
ctx.eventEmitter &&
|
||||||
|
|
|
@ -26,7 +26,8 @@ import { getViews, saveView } from "../view/utils"
|
||||||
import viewTemplate from "../view/viewBuilder"
|
import viewTemplate from "../view/viewBuilder"
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
import { cloneDeep } from "lodash/fp"
|
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) {
|
export async function clearColumns(table: any, columnNames: any) {
|
||||||
const db = getAppDB()
|
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))
|
await quotas.addRows(finalData.length, () => db.bulkDocs(finalData))
|
||||||
let response = await db.put(table)
|
events.row.import(table, "csv", finalData.length)
|
||||||
table._rev = response._rev
|
|
||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +245,7 @@ class TableSaveFunctions {
|
||||||
// after saving
|
// after saving
|
||||||
async after(table: any) {
|
async after(table: any) {
|
||||||
table = await handleSearchIndexes(table)
|
table = await handleSearchIndexes(table)
|
||||||
table = await handleDataImport(this.user, table, this.dataImport)
|
await handleDataImport(this.user, table, this.dataImport)
|
||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ const { fetchView } = require("../row")
|
||||||
const { getTable } = require("../table/utils")
|
const { getTable } = require("../table/utils")
|
||||||
const { FieldTypes } = require("../../../constants")
|
const { FieldTypes } = require("../../../constants")
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
const { events } = require("@budibase/backend-core")
|
||||||
|
const { DocumentTypes } = require("../../../db/utils")
|
||||||
|
|
||||||
exports.fetch = async ctx => {
|
exports.fetch = async ctx => {
|
||||||
ctx.body = await getViews()
|
ctx.body = await getViews()
|
||||||
|
@ -79,9 +81,9 @@ exports.exportView = async ctx => {
|
||||||
let rows = ctx.body
|
let rows = ctx.body
|
||||||
|
|
||||||
let schema = view && view.meta && view.meta.schema
|
let schema = view && view.meta && view.meta.schema
|
||||||
if (!schema) {
|
|
||||||
const tableId = ctx.params.tableId || view.meta.tableId
|
const tableId = ctx.params.tableId || view.meta.tableId
|
||||||
const table = await getTable(tableId)
|
const table = await getTable(tableId)
|
||||||
|
if (!schema) {
|
||||||
schema = table.schema
|
schema = table.schema
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,4 +118,10 @@ exports.exportView = async ctx => {
|
||||||
// send down the file
|
// send down the file
|
||||||
ctx.attachment(filename)
|
ctx.attachment(filename)
|
||||||
ctx.body = apiFileReturn(exporter(headers, rows))
|
ctx.body = apiFileReturn(exporter(headers, rows))
|
||||||
|
|
||||||
|
if (viewName.startsWith(DocumentTypes.TABLE)) {
|
||||||
|
events.table.exported(table, format)
|
||||||
|
} else {
|
||||||
|
events.view.exported(table, format)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,37 +14,56 @@ describe("/tables", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("create", () => {
|
describe("create", () => {
|
||||||
it("returns a success message when the table is successfully created", async () => {
|
|
||||||
const res = await request
|
beforeEach(() => {
|
||||||
.post(`/api/tables`)
|
jest.clearAllMocks()
|
||||||
.send({
|
|
||||||
name: "TestTable",
|
|
||||||
key: "name",
|
|
||||||
schema: {
|
|
||||||
name: {type: "string"}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const createTable = (table) => {
|
||||||
|
if (!table) {
|
||||||
|
table = basicTable()
|
||||||
|
}
|
||||||
|
return request
|
||||||
|
.post(`/api/tables`)
|
||||||
|
.send(table)
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.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.res.statusMessage).toEqual("Table TestTable saved successfully.")
|
||||||
expect(res.body.name).toEqual("TestTable")
|
expect(res.body.name).toEqual("TestTable")
|
||||||
expect(events.table.created).toBeCalledTimes(1)
|
expect(events.table.created).toBeCalledTimes(1)
|
||||||
expect(events.table.created).toBeCalledWith(res.body)
|
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 () => {
|
it("should apply authorization to endpoint", async () => {
|
||||||
await checkBuilderEndpoint({
|
await checkBuilderEndpoint({
|
||||||
config,
|
config,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: `/api/tables`,
|
url: `/api/tables`,
|
||||||
body: {
|
body: basicTable()
|
||||||
name: "TestTable",
|
|
||||||
key: "name",
|
|
||||||
schema: {
|
|
||||||
name: {type: "string"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -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", () => {
|
describe("fetch", () => {
|
||||||
let testTable
|
let testTable
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
|
const { events } = require("@budibase/backend-core")
|
||||||
|
|
||||||
function priceTable() {
|
function priceTable() {
|
||||||
return {
|
return {
|
||||||
|
@ -205,33 +206,77 @@ describe("/views", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("exportView", () => {
|
describe("exportView", () => {
|
||||||
it("should be able to export a view", async () => {
|
|
||||||
await config.createTable(priceTable())
|
beforeEach(() => {
|
||||||
await config.createRow()
|
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()
|
const view = await config.createView()
|
||||||
let res = await request
|
table = await config.getTable(table._id)
|
||||||
.get(`/api/views/export?view=${view.name}&format=json`)
|
|
||||||
.set(config.defaultHeaders())
|
let res = await exportView(view.name, "json")
|
||||||
.expect(200)
|
|
||||||
let error
|
assertJsonExport(res)
|
||||||
try {
|
expect(events.view.exported).toBeCalledTimes(1)
|
||||||
const obj = JSON.parse(res.text)
|
expect(events.view.exported).toBeCalledWith(table, "json")
|
||||||
expect(obj.length).toBe(1)
|
})
|
||||||
} catch (err) {
|
|
||||||
error = err
|
it("should be able to export a view as CSV", async () => {
|
||||||
}
|
let table = await setupExport()
|
||||||
expect(error).toBeUndefined()
|
const view = await config.createView()
|
||||||
res = await request
|
table = await config.getTable(table._id)
|
||||||
.get(`/api/views/export?view=${view.name}&format=csv`)
|
|
||||||
.set(config.defaultHeaders())
|
let res = await exportView(view.name, "csv")
|
||||||
.expect(200)
|
|
||||||
// this shouldn't be JSON
|
assertCSVExport(res)
|
||||||
try {
|
expect(events.view.exported).toBeCalledTimes(1)
|
||||||
JSON.parse(res.text)
|
expect(events.view.exported).toBeCalledWith(table, "csv")
|
||||||
} catch (err) {
|
|
||||||
error = err
|
|
||||||
}
|
|
||||||
expect(error).toBeDefined()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue