budibase/packages/server/src/api/controllers/row/index.ts

204 lines
5.1 KiB
TypeScript

import { quotas } from "@budibase/pro"
import * as internal from "./internal"
import * as external from "./external"
import { isExternalTable } from "../../../integrations/utils"
import { Ctx } from "@budibase/types"
import * as utils from "./utils"
import { gridSocket } from "../../../websockets"
import sdk from "../../../sdk"
import * as exporters from "../view/exporters"
import { apiFileReturn } from "../../../utilities/fileSystem"
function pickApi(tableId: any) {
if (isExternalTable(tableId)) {
return external
}
return internal
}
export async function patch(ctx: any): Promise<any> {
const appId = ctx.appId
const tableId = utils.getTableId(ctx)
const body = ctx.request.body
// if it doesn't have an _id then its save
if (body && !body._id) {
return save(ctx)
}
try {
const { row, table } = await quotas.addQuery(
() => pickApi(tableId).patch(ctx),
{
datasourceId: tableId,
}
)
if (!row) {
ctx.throw(404, "Row not found")
}
ctx.status = 200
ctx.eventEmitter &&
ctx.eventEmitter.emitRow(`row:update`, appId, row, table)
ctx.message = `${table.name} updated successfully.`
ctx.body = row
gridSocket?.emitRowUpdate(ctx, row)
} catch (err) {
ctx.throw(400, err)
}
}
export const save = async (ctx: any) => {
const appId = ctx.appId
const tableId = utils.getTableId(ctx)
const body = ctx.request.body
// if it has an ID already then its a patch
if (body && body._id) {
return patch(ctx)
}
const { row, table, squashed } = await quotas.addRow(() =>
quotas.addQuery(() => pickApi(tableId).save(ctx), {
datasourceId: tableId,
})
)
ctx.status = 200
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:save`, appId, row, table)
ctx.message = `${table.name} saved successfully`
// prefer squashed for response
ctx.body = row || squashed
gridSocket?.emitRowUpdate(ctx, row || squashed)
}
export async function fetchView(ctx: any) {
const tableId = utils.getTableId(ctx)
const viewName = decodeURIComponent(ctx.params.viewName)
const { calculation, group, field } = ctx.query
ctx.body = await quotas.addQuery(
() =>
sdk.rows.fetchView(tableId, viewName, {
calculation,
group,
field,
}),
{
datasourceId: tableId,
}
)
}
export async function fetch(ctx: any) {
const tableId = utils.getTableId(ctx)
ctx.body = await quotas.addQuery(() => sdk.rows.fetch(tableId), {
datasourceId: tableId,
})
}
export async function find(ctx: any) {
const tableId = utils.getTableId(ctx)
ctx.body = await quotas.addQuery(() => pickApi(tableId).find(ctx), {
datasourceId: tableId,
})
}
export async function destroy(ctx: any) {
const appId = ctx.appId
const inputs = ctx.request.body
const tableId = utils.getTableId(ctx)
let response, row
if (inputs.rows) {
let { rows } = await quotas.addQuery(
() => pickApi(tableId).bulkDestroy(ctx),
{
datasourceId: tableId,
}
)
await quotas.removeRows(rows.length)
response = rows
for (let row of rows) {
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row)
gridSocket?.emitRowDeletion(ctx, row._id)
}
} else {
let resp = await quotas.addQuery(() => pickApi(tableId).destroy(ctx), {
datasourceId: tableId,
})
await quotas.removeRow()
response = resp.response
row = resp.row
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row)
gridSocket?.emitRowDeletion(ctx, row._id)
}
ctx.status = 200
// for automations include the row that was deleted
ctx.row = row || {}
ctx.body = response
}
export async function search(ctx: any) {
const tableId = utils.getTableId(ctx)
const searchParams = {
...ctx.request.body,
tableId,
}
ctx.status = 200
ctx.body = await quotas.addQuery(() => sdk.rows.search(searchParams), {
datasourceId: tableId,
})
}
export async function validate(ctx: Ctx) {
const tableId = utils.getTableId(ctx)
// external tables are hard to validate currently
if (isExternalTable(tableId)) {
ctx.body = { valid: true }
} else {
ctx.body = await utils.validate({
row: ctx.request.body,
tableId,
})
}
}
export async function fetchEnrichedRow(ctx: any) {
const tableId = utils.getTableId(ctx)
ctx.body = await quotas.addQuery(
() => pickApi(tableId).fetchEnrichedRow(ctx),
{
datasourceId: tableId,
}
)
}
export const exportRows = async (ctx: any) => {
const tableId = utils.getTableId(ctx)
const format = ctx.query.format
const { rows, columns, query } = ctx.request.body
if (typeof format !== "string" || !exporters.isFormat(format)) {
ctx.throw(
400,
`Format ${format} not valid. Valid values: ${Object.values(
exporters.Format
)}`
)
}
ctx.body = await quotas.addQuery(
async () => {
const { fileName, content } = await sdk.rows.exportRows({
tableId,
format,
rowIds: rows,
columns,
query,
})
ctx.attachment(fileName)
return apiFileReturn(content)
},
{
datasourceId: tableId,
}
)
}