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