2021-06-11 19:56:30 +02:00
|
|
|
const CouchDB = require("../../../db")
|
2021-06-14 20:05:39 +02:00
|
|
|
const { makeExternalQuery } = require("./utils")
|
|
|
|
const { DataSourceOperation, SortDirection } = require("../../../constants")
|
|
|
|
|
2021-06-15 14:03:55 +02:00
|
|
|
async function getTable(appId, datasourceId, tableName) {
|
|
|
|
const db = new CouchDB(appId)
|
|
|
|
const datasource = await db.get(datasourceId)
|
|
|
|
if (!datasource || !datasource.entities) {
|
|
|
|
throw "Datasource is not configured fully."
|
|
|
|
}
|
|
|
|
return Object.values(datasource.entities).find(
|
|
|
|
entity => entity.name === tableName
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-06-15 15:56:25 +02:00
|
|
|
function inputProcessing(row, table) {
|
|
|
|
if (!row) {
|
|
|
|
return row
|
|
|
|
}
|
|
|
|
let newRow = {}
|
|
|
|
for (let key of Object.keys(table.schema)) {
|
|
|
|
// currently excludes empty strings
|
|
|
|
if (row[key]) {
|
|
|
|
newRow[key] = row[key]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newRow
|
|
|
|
}
|
|
|
|
|
|
|
|
function outputProcessing(rows, table) {
|
|
|
|
// if no rows this is what is returned? Might be PG only
|
|
|
|
if (rows[0].read === true) {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
const primary = table.primary
|
|
|
|
for (let row of rows) {
|
|
|
|
// build id array
|
|
|
|
let idParts = []
|
|
|
|
for (let field of primary) {
|
|
|
|
idParts.push(row[field])
|
|
|
|
}
|
|
|
|
row._id = idParts
|
|
|
|
}
|
|
|
|
return rows
|
|
|
|
}
|
|
|
|
|
2021-06-15 14:03:55 +02:00
|
|
|
function buildIDFilter(id, table) {
|
|
|
|
if (!id || !table) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
// if used as URL parameter it will have been joined
|
|
|
|
if (typeof id === "string") {
|
|
|
|
id = id.split(",")
|
|
|
|
}
|
|
|
|
const primary = table.primary
|
|
|
|
const equal = {}
|
|
|
|
for (let field of primary) {
|
|
|
|
// work through the ID and get the parts
|
|
|
|
equal[field] = id.shift()
|
2021-06-14 20:05:39 +02:00
|
|
|
}
|
|
|
|
return {
|
2021-06-15 14:03:55 +02:00
|
|
|
equal,
|
2021-06-14 20:05:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-14 20:07:13 +02:00
|
|
|
async function handleRequest(
|
|
|
|
appId,
|
|
|
|
operation,
|
|
|
|
tableId,
|
2021-06-15 14:03:55 +02:00
|
|
|
{ id, row, filters, sort, paginate } = {}
|
2021-06-14 20:07:13 +02:00
|
|
|
) {
|
2021-06-15 14:20:25 +02:00
|
|
|
const parts = tableId.split("_")
|
|
|
|
let tableName = parts.pop()
|
|
|
|
let datasourceId = parts.join("_")
|
2021-06-15 14:03:55 +02:00
|
|
|
const table = await getTable(appId, datasourceId, tableName)
|
|
|
|
if (!table) {
|
|
|
|
throw `Unable to process query, table "${tableName}" not defined.`
|
|
|
|
}
|
2021-06-15 15:56:25 +02:00
|
|
|
// clean up row on ingress using schema
|
|
|
|
row = inputProcessing(row, table)
|
2021-06-15 14:03:55 +02:00
|
|
|
// try and build an id filter if required
|
2021-06-15 15:56:25 +02:00
|
|
|
let idFilters = buildIDFilter(id, table)
|
|
|
|
if (operation === DataSourceOperation.DELETE && Object.keys(idFilters).length === 0) {
|
|
|
|
throw "Deletion must be filtered in someway"
|
|
|
|
}
|
2021-06-14 20:05:39 +02:00
|
|
|
let json = {
|
|
|
|
endpoint: {
|
|
|
|
datasourceId,
|
|
|
|
entityId: tableName,
|
|
|
|
operation,
|
|
|
|
},
|
2021-06-15 14:50:41 +02:00
|
|
|
resource: {
|
|
|
|
// not specifying any fields means "*"
|
|
|
|
fields: [],
|
|
|
|
},
|
2021-06-15 14:03:55 +02:00
|
|
|
filters: idFilters != null ? idFilters : filters,
|
2021-06-14 20:05:39 +02:00
|
|
|
sort,
|
|
|
|
paginate,
|
|
|
|
body: row,
|
|
|
|
}
|
2021-06-15 15:56:25 +02:00
|
|
|
// can't really use response right now
|
|
|
|
const response = await makeExternalQuery(appId, json)
|
|
|
|
// we searched for rows in someway
|
|
|
|
if (operation === DataSourceOperation.READ && Array.isArray(response)) {
|
|
|
|
return outputProcessing(response, table)
|
|
|
|
}
|
|
|
|
// append tableId back onto row if it exists
|
|
|
|
if (row) {
|
|
|
|
row.tableId = table._id
|
|
|
|
}
|
|
|
|
return { row, table }
|
2021-06-14 20:05:39 +02:00
|
|
|
}
|
2021-06-11 19:56:30 +02:00
|
|
|
|
2021-06-14 20:07:13 +02:00
|
|
|
exports.patch = async ctx => {
|
2021-06-14 20:05:39 +02:00
|
|
|
const appId = ctx.appId
|
|
|
|
const inputs = ctx.request.body
|
|
|
|
const tableId = ctx.params.tableId
|
|
|
|
const id = inputs._id
|
|
|
|
// don't save the ID to db
|
|
|
|
delete inputs._id
|
2021-06-15 14:50:41 +02:00
|
|
|
return handleRequest(appId, DataSourceOperation.UPDATE, tableId, {
|
2021-06-14 20:07:13 +02:00
|
|
|
id,
|
|
|
|
row: inputs,
|
|
|
|
})
|
2021-06-11 19:56:30 +02:00
|
|
|
}
|
|
|
|
|
2021-06-14 20:07:13 +02:00
|
|
|
exports.save = async ctx => {
|
2021-06-14 20:05:39 +02:00
|
|
|
const appId = ctx.appId
|
|
|
|
const inputs = ctx.request.body
|
|
|
|
if (inputs._id) {
|
|
|
|
return exports.patch(ctx)
|
|
|
|
}
|
|
|
|
const tableId = ctx.params.tableId
|
2021-06-15 14:50:41 +02:00
|
|
|
return handleRequest(appId, DataSourceOperation.CREATE, tableId, {
|
2021-06-14 20:07:13 +02:00
|
|
|
row: inputs,
|
|
|
|
})
|
2021-06-11 19:56:30 +02:00
|
|
|
}
|
|
|
|
|
2021-06-14 20:07:13 +02:00
|
|
|
exports.fetchView = async ctx => {
|
2021-06-15 14:03:55 +02:00
|
|
|
// there are no views in external data sources, shouldn't ever be called
|
2021-06-15 14:20:25 +02:00
|
|
|
// for now just fetch
|
|
|
|
ctx.params.tableId = ctx.params.viewName.split("all_")[1]
|
|
|
|
return exports.fetch(ctx)
|
2021-06-11 19:56:30 +02:00
|
|
|
}
|
|
|
|
|
2021-06-15 14:03:55 +02:00
|
|
|
exports.fetch = async ctx => {
|
|
|
|
const appId = ctx.appId
|
|
|
|
const tableId = ctx.params.tableId
|
2021-06-15 14:50:41 +02:00
|
|
|
return handleRequest(appId, DataSourceOperation.READ, tableId)
|
2021-06-11 19:56:30 +02:00
|
|
|
}
|
|
|
|
|
2021-06-14 20:07:13 +02:00
|
|
|
exports.find = async ctx => {
|
2021-06-15 14:03:55 +02:00
|
|
|
const appId = ctx.appId
|
|
|
|
const id = ctx.params.rowId
|
|
|
|
const tableId = ctx.params.tableId
|
2021-06-15 14:50:41 +02:00
|
|
|
return handleRequest(appId, DataSourceOperation.READ, tableId, {
|
2021-06-15 14:03:55 +02:00
|
|
|
id,
|
|
|
|
})
|
2021-06-11 19:56:30 +02:00
|
|
|
}
|
|
|
|
|
2021-06-14 20:07:13 +02:00
|
|
|
exports.destroy = async ctx => {
|
2021-06-14 20:05:39 +02:00
|
|
|
const appId = ctx.appId
|
|
|
|
const tableId = ctx.params.tableId
|
2021-06-15 14:50:41 +02:00
|
|
|
return handleRequest(appId, DataSourceOperation.DELETE, tableId, {
|
2021-06-14 20:07:13 +02:00
|
|
|
id: ctx.request.body._id,
|
|
|
|
})
|
2021-06-11 19:56:30 +02:00
|
|
|
}
|
|
|
|
|
2021-06-14 20:07:13 +02:00
|
|
|
exports.bulkDestroy = async ctx => {
|
2021-06-15 14:03:55 +02:00
|
|
|
const appId = ctx.appId
|
|
|
|
const { rows } = ctx.request.body
|
|
|
|
const tableId = ctx.params.tableId
|
|
|
|
// TODO: this can probably be optimised to a single SQL statement in the future
|
|
|
|
let promises = []
|
|
|
|
for (let row of rows) {
|
2021-06-15 14:47:08 +02:00
|
|
|
promises.push(
|
|
|
|
handleRequest(appId, DataSourceOperation.DELETE, tableId, {
|
|
|
|
id: row._id,
|
|
|
|
})
|
|
|
|
)
|
2021-06-15 14:03:55 +02:00
|
|
|
}
|
|
|
|
await Promise.all(promises)
|
2021-06-15 14:50:41 +02:00
|
|
|
return { response: { ok: true }, rows }
|
2021-06-14 20:05:39 +02:00
|
|
|
}
|
|
|
|
|
2021-06-14 20:07:13 +02:00
|
|
|
exports.search = async ctx => {
|
2021-06-14 20:05:39 +02:00
|
|
|
const appId = ctx.appId
|
|
|
|
const tableId = ctx.params.tableId
|
|
|
|
const { paginate, query, ...params } = ctx.request.body
|
|
|
|
let paginateObj = {}
|
|
|
|
if (paginate) {
|
|
|
|
paginateObj = {
|
|
|
|
limit: params.limit,
|
|
|
|
// todo: need to handle bookmarks
|
|
|
|
page: params.bookmark,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let sort
|
|
|
|
if (params.sort) {
|
2021-06-14 20:07:13 +02:00
|
|
|
const direction =
|
|
|
|
params.sortOrder === "descending"
|
|
|
|
? SortDirection.DESCENDING
|
|
|
|
: SortDirection.ASCENDING
|
2021-06-14 20:05:39 +02:00
|
|
|
sort = {
|
2021-06-14 20:07:13 +02:00
|
|
|
[params.sort]: direction,
|
2021-06-14 20:05:39 +02:00
|
|
|
}
|
|
|
|
}
|
2021-06-15 14:50:41 +02:00
|
|
|
return handleRequest(appId, DataSourceOperation.READ, tableId, {
|
2021-06-14 20:07:13 +02:00
|
|
|
filters: query,
|
|
|
|
sort,
|
|
|
|
paginate: paginateObj,
|
|
|
|
})
|
2021-06-11 19:56:30 +02:00
|
|
|
}
|
|
|
|
|
2021-06-14 20:07:13 +02:00
|
|
|
exports.validate = async ctx => {
|
2021-06-14 20:05:39 +02:00
|
|
|
// can't validate external right now - maybe in future
|
2021-06-15 14:50:41 +02:00
|
|
|
return { valid: true }
|
2021-06-11 19:56:30 +02:00
|
|
|
}
|
|
|
|
|
2021-06-14 20:07:13 +02:00
|
|
|
exports.fetchEnrichedRow = async ctx => {
|
2021-06-15 14:03:55 +02:00
|
|
|
// TODO: How does this work
|
2021-06-15 14:50:41 +02:00
|
|
|
throw "Not Implemented"
|
2021-06-11 19:56:30 +02:00
|
|
|
}
|