forked from tsai/budibase
7 changed files with 280 additions and 152 deletions
@ -0,0 +1,38 @@ |
|||||
|
const CouchDB = require("../../../db") |
||||
|
|
||||
|
exports.patch = async (ctx) => { |
||||
|
ctx.body = {} |
||||
|
} |
||||
|
|
||||
|
exports.save = async (ctx) => { |
||||
|
ctx.body = {} |
||||
|
} |
||||
|
|
||||
|
exports.fetchView = async (ctx) => { |
||||
|
ctx.body = {} |
||||
|
} |
||||
|
|
||||
|
exports.fetchTableRows = async (ctx) => { |
||||
|
ctx.body = {} |
||||
|
} |
||||
|
|
||||
|
exports.find = async (ctx) => { |
||||
|
ctx.body = {} |
||||
|
} |
||||
|
|
||||
|
exports.destroy = async (ctx) => { |
||||
|
ctx.body = {} |
||||
|
} |
||||
|
|
||||
|
exports.bulkDestroy = async (ctx) => { |
||||
|
ctx.body = {} |
||||
|
} |
||||
|
|
||||
|
exports.validate = async (ctx) => { |
||||
|
ctx.body = {} |
||||
|
} |
||||
|
|
||||
|
exports.fetchEnrichedRow = async (ctx) => { |
||||
|
// TODO: should this join?
|
||||
|
ctx.body = {} |
||||
|
} |
||||
@ -0,0 +1,114 @@ |
|||||
|
const internal = require("./internal") |
||||
|
const external = require("./external") |
||||
|
|
||||
|
function pickApi(tableId) { |
||||
|
// TODO: go to external
|
||||
|
return internal |
||||
|
} |
||||
|
|
||||
|
function getTableId(ctx) { |
||||
|
if (ctx.request.body && ctx.request.body.tableId) { |
||||
|
return ctx.request.body.tableId |
||||
|
} |
||||
|
if (ctx.params && ctx.params.tableId) { |
||||
|
return ctx.params.tableId |
||||
|
} |
||||
|
if (ctx.params && ctx.params.viewName) { |
||||
|
return ctx.params.viewName |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.patch = async ctx => { |
||||
|
const appId = ctx.appId |
||||
|
const tableId = getTableId(ctx) |
||||
|
try { |
||||
|
const { row, table } = await pickApi(tableId).patch(ctx) |
||||
|
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:update`, appId, row, table) |
||||
|
ctx.message = `${table.name} updated successfully` |
||||
|
ctx.body = row |
||||
|
} catch (err) { |
||||
|
ctx.throw(400, err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.save = async function (ctx) { |
||||
|
// TODO: this used to handle bulk delete, need to update builder/client
|
||||
|
const appId = ctx.appId |
||||
|
const tableId = getTableId(ctx) |
||||
|
try { |
||||
|
const { row, table } = await pickApi(tableId).save(ctx) |
||||
|
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:save`, appId, row, table) |
||||
|
ctx.message = `${table.name} saved successfully` |
||||
|
ctx.body = row |
||||
|
} catch (err) { |
||||
|
ctx.throw(400, err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.fetchView = async function (ctx) { |
||||
|
const tableId = getTableId(ctx) |
||||
|
try { |
||||
|
ctx.body = await pickApi(tableId).fetchView(ctx) |
||||
|
} catch (err) { |
||||
|
ctx.throw(400, err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.fetchTableRows = async function (ctx) { |
||||
|
const tableId = getTableId(ctx) |
||||
|
try { |
||||
|
ctx.body = await pickApi(tableId).fetchTableRows(ctx) |
||||
|
} catch (err) { |
||||
|
ctx.throw(400, err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.find = async function (ctx) { |
||||
|
const tableId = getTableId(ctx) |
||||
|
try { |
||||
|
ctx.body = await pickApi(tableId).find(ctx) |
||||
|
} catch (err) { |
||||
|
ctx.throw(400, err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.destroy = async function (ctx) { |
||||
|
const appId = ctx.appId |
||||
|
const inputs = ctx.request.body |
||||
|
const tableId = getTableId(ctx) |
||||
|
let response, row |
||||
|
if (inputs.rows) { |
||||
|
let { rows } = await pickApi(tableId).bulkDestroy(ctx) |
||||
|
response = rows |
||||
|
for (let row of rows) { |
||||
|
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row) |
||||
|
} |
||||
|
} else { |
||||
|
let resp = await pickApi(tableId).destroy(ctx) |
||||
|
response = resp.response |
||||
|
row = resp.row |
||||
|
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row) |
||||
|
} |
||||
|
ctx.status = 200 |
||||
|
// for automations include the row that was deleted
|
||||
|
ctx.row = row || {} |
||||
|
ctx.body = response |
||||
|
} |
||||
|
|
||||
|
exports.validate = async function (ctx) { |
||||
|
const tableId = getTableId(ctx) |
||||
|
try { |
||||
|
ctx.body = await pickApi(tableId).validate(ctx) |
||||
|
} catch (err) { |
||||
|
ctx.throw(400, err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.fetchEnrichedRow = async function (ctx) { |
||||
|
const tableId = getTableId(ctx) |
||||
|
try { |
||||
|
ctx.body = await pickApi(tableId).fetchEnrichedRow(ctx) |
||||
|
} catch (err) { |
||||
|
ctx.throw(400, err) |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
const validateJs = require("validate.js") |
||||
|
const { cloneDeep } = require("lodash/fp") |
||||
|
const CouchDB = require("../../../db") |
||||
|
const { InternalTables } = require("../../../db/utils") |
||||
|
const userController = require("../user") |
||||
|
const { FieldTypes } = require("../../../constants") |
||||
|
|
||||
|
validateJs.extend(validateJs.validators.datetime, { |
||||
|
parse: function (value) { |
||||
|
return new Date(value).getTime() |
||||
|
}, |
||||
|
// Input is a unix timestamp
|
||||
|
format: function (value) { |
||||
|
return new Date(value).toISOString() |
||||
|
}, |
||||
|
}) |
||||
|
|
||||
|
exports.findRow = async (ctx, db, tableId, rowId) => { |
||||
|
let row |
||||
|
// TODO remove special user case in future
|
||||
|
if (tableId === InternalTables.USER_METADATA) { |
||||
|
ctx.params = { |
||||
|
id: rowId, |
||||
|
} |
||||
|
await userController.findMetadata(ctx) |
||||
|
row = ctx.body |
||||
|
} else { |
||||
|
row = await db.get(rowId) |
||||
|
} |
||||
|
if (row.tableId !== tableId) { |
||||
|
throw "Supplied tableId does not match the rows tableId" |
||||
|
} |
||||
|
return row |
||||
|
} |
||||
|
|
||||
|
exports.validate = async ({ appId, tableId, row, table }) => { |
||||
|
if (!table) { |
||||
|
const db = new CouchDB(appId) |
||||
|
table = await db.get(tableId) |
||||
|
} |
||||
|
const errors = {} |
||||
|
for (let fieldName of Object.keys(table.schema)) { |
||||
|
const constraints = cloneDeep(table.schema[fieldName].constraints) |
||||
|
// special case for options, need to always allow unselected (null)
|
||||
|
if ( |
||||
|
table.schema[fieldName].type === FieldTypes.OPTIONS && |
||||
|
constraints.inclusion |
||||
|
) { |
||||
|
constraints.inclusion.push(null) |
||||
|
} |
||||
|
const res = validateJs.single(row[fieldName], constraints) |
||||
|
if (res) errors[fieldName] = res |
||||
|
} |
||||
|
return { valid: Object.keys(errors).length === 0, errors } |
||||
|
} |
||||
Loading…
Reference in new issue