Some of the functionality required for external SQL rows API.
This commit is contained in:
parent
146a72f61d
commit
ce8d06df06
|
@ -44,7 +44,7 @@ export const updateRow = async row => {
|
|||
return
|
||||
}
|
||||
const res = await API.patch({
|
||||
url: `/api/${row.tableId}/rows/${row._id}`,
|
||||
url: `/api/${row.tableId}/rows`,
|
||||
body: row,
|
||||
})
|
||||
res.error
|
||||
|
|
|
@ -45,7 +45,7 @@ export const searchTable = async ({
|
|||
}
|
||||
}
|
||||
const res = await API.post({
|
||||
url: `/api/search/${tableId}/rows`,
|
||||
url: `/api/${tableId}/search`,
|
||||
body: {
|
||||
query,
|
||||
bookmark,
|
||||
|
|
|
@ -7,6 +7,7 @@ const {
|
|||
} = require("../../db/utils")
|
||||
const { integrations } = require("../../integrations")
|
||||
const plusIntegrations = require("../../integrations/plus")
|
||||
const { makeExternalQuery } = require("./row/utils")
|
||||
|
||||
exports.fetch = async function (ctx) {
|
||||
const database = new CouchDB(ctx.appId)
|
||||
|
@ -77,15 +78,9 @@ exports.find = async function (ctx) {
|
|||
// dynamic query functionality
|
||||
exports.query = async function (ctx) {
|
||||
const queryJson = ctx.request.body
|
||||
const datasourceId = queryJson.endpoint.datasourceId
|
||||
const database = new CouchDB(ctx.appId)
|
||||
const datasource = await database.get(datasourceId)
|
||||
const Integration = integrations[datasource.source]
|
||||
// query is the opinionated function
|
||||
if (Integration.prototype.query) {
|
||||
const integration = new Integration(datasource.config)
|
||||
ctx.body = await integration.query(queryJson)
|
||||
} else {
|
||||
ctx.throw(400, "Datasource does not support query.")
|
||||
try {
|
||||
ctx.body = await makeExternalQuery(ctx.appId, queryJson)
|
||||
} catch (err) {
|
||||
ctx.throw(400, err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,115 @@
|
|||
const CouchDB = require("../../../db")
|
||||
const { makeExternalQuery } = require("./utils")
|
||||
const { DataSourceOperation, SortDirection } = require("../../../constants")
|
||||
|
||||
async function buildIDFilter(id) {
|
||||
if (!id) {
|
||||
return {}
|
||||
}
|
||||
// TODO: work out how to use the schema to get filter
|
||||
return {
|
||||
equal: {
|
||||
id: id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRequest(appId, operation, tableId, { id, row, filters, sort, paginate }) {
|
||||
let [datasourceId, tableName] = tableId.split("/")
|
||||
let idFilter = buildIDFilter(id)
|
||||
let json = {
|
||||
endpoint: {
|
||||
datasourceId,
|
||||
entityId: tableName,
|
||||
operation,
|
||||
},
|
||||
filters: {
|
||||
...filters,
|
||||
...idFilter,
|
||||
},
|
||||
sort,
|
||||
paginate,
|
||||
body: row,
|
||||
}
|
||||
return makeExternalQuery(appId, json)
|
||||
}
|
||||
|
||||
exports.patch = async (ctx) => {
|
||||
ctx.body = {}
|
||||
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
|
||||
ctx.body = await handleRequest(appId, DataSourceOperation.UPDATE, tableId, { id, row: inputs })
|
||||
}
|
||||
|
||||
exports.save = async (ctx) => {
|
||||
ctx.body = {}
|
||||
const appId = ctx.appId
|
||||
const inputs = ctx.request.body
|
||||
if (inputs._id) {
|
||||
return exports.patch(ctx)
|
||||
}
|
||||
const tableId = ctx.params.tableId
|
||||
ctx.body = await handleRequest(appId, DataSourceOperation.CREATE, tableId, { row: inputs })
|
||||
}
|
||||
|
||||
exports.fetchView = async (ctx) => {
|
||||
ctx.body = {}
|
||||
// TODO: don't know what this does for external
|
||||
}
|
||||
|
||||
exports.fetchTableRows = async (ctx) => {
|
||||
ctx.body = {}
|
||||
// TODO: this is a basic read?
|
||||
}
|
||||
|
||||
exports.find = async (ctx) => {
|
||||
ctx.body = {}
|
||||
// TODO: single find
|
||||
}
|
||||
|
||||
exports.destroy = async (ctx) => {
|
||||
ctx.body = {}
|
||||
const appId = ctx.appId
|
||||
const tableId = ctx.params.tableId
|
||||
ctx.body = await handleRequest(appId, DataSourceOperation.DELETE, tableId, { id: ctx.request.body._id })
|
||||
}
|
||||
|
||||
exports.bulkDestroy = async (ctx) => {
|
||||
ctx.body = {}
|
||||
// TODO: iterate through rows, build a large OR filter?
|
||||
}
|
||||
|
||||
exports.search = async (ctx) => {
|
||||
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) {
|
||||
sort = {
|
||||
[params.sort]: params.sortOrder === "descending" ? SortDirection.DESCENDING : SortDirection.ASCENDING
|
||||
}
|
||||
}
|
||||
ctx.body = await handleRequest(appId, DataSourceOperation.READ, tableId,
|
||||
{
|
||||
filters: query,
|
||||
sort,
|
||||
paginate: paginateObj,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
exports.validate = async (ctx) => {
|
||||
ctx.body = {}
|
||||
// can't validate external right now - maybe in future
|
||||
ctx.body = { valid: true }
|
||||
}
|
||||
|
||||
exports.fetchEnrichedRow = async (ctx) => {
|
||||
// TODO: should this join?
|
||||
const appId = ctx.appId
|
||||
ctx.body = {}
|
||||
}
|
||||
|
|
|
@ -95,6 +95,16 @@ exports.destroy = async function (ctx) {
|
|||
ctx.body = response
|
||||
}
|
||||
|
||||
exports.search = async ctx => {
|
||||
const tableId = getTableId(ctx)
|
||||
try {
|
||||
ctx.body = await pickApi(tableId).search(ctx)
|
||||
} catch (err) {
|
||||
ctx.throw(400, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
exports.validate = async function (ctx) {
|
||||
const tableId = getTableId(ctx)
|
||||
try {
|
||||
|
|
|
@ -15,6 +15,7 @@ const {
|
|||
const { FieldTypes } = require("../../../constants")
|
||||
const { isEqual } = require("lodash")
|
||||
const { validate, findRow } = require("./utils")
|
||||
const { fullSearch, paginatedSearch } = require("./internalSearch")
|
||||
|
||||
const TABLE_VIEW_BEGINS_WITH = `all${SEPARATOR}${DocumentTypes.TABLE}${SEPARATOR}`
|
||||
|
||||
|
@ -32,7 +33,7 @@ exports.patch = async ctx => {
|
|||
const isUserTable = tableId === InternalTables.USER_METADATA
|
||||
let dbRow
|
||||
try {
|
||||
dbRow = await db.get(ctx.params.rowId)
|
||||
dbRow = await db.get(inputs._id)
|
||||
} catch (err) {
|
||||
if (isUserTable) {
|
||||
// don't include the rev, it'll be the global rev
|
||||
|
@ -96,7 +97,6 @@ exports.save = async function (ctx) {
|
|||
|
||||
// if the row obj had an _id then it will have been retrieved
|
||||
if (inputs._id && inputs._rev) {
|
||||
ctx.params.rowId = inputs._id
|
||||
return exports.patch(ctx)
|
||||
}
|
||||
|
||||
|
@ -281,6 +281,29 @@ exports.bulkDestroy = async ctx => {
|
|||
return { response: { ok: true }, rows }
|
||||
}
|
||||
|
||||
exports.search = async ctx => {
|
||||
const appId = ctx.appId
|
||||
const { tableId } = ctx.params
|
||||
const db = new CouchDB(appId)
|
||||
const { paginate, query, ...params } = ctx.request.body
|
||||
params.tableId = tableId
|
||||
|
||||
let response
|
||||
if (paginate) {
|
||||
response = await paginatedSearch(appId, query, params)
|
||||
} else {
|
||||
response = await fullSearch(appId, query, params)
|
||||
}
|
||||
|
||||
// Enrich search results with relationships
|
||||
if (response.rows && response.rows.length) {
|
||||
const table = await db.get(tableId)
|
||||
response.rows = await outputProcessing(appId, table, response.rows)
|
||||
}
|
||||
|
||||
ctx.body = response
|
||||
}
|
||||
|
||||
exports.validate = async (ctx) => {
|
||||
return validate({
|
||||
appId: ctx.appId,
|
||||
|
|
|
@ -4,6 +4,7 @@ const CouchDB = require("../../../db")
|
|||
const { InternalTables } = require("../../../db/utils")
|
||||
const userController = require("../user")
|
||||
const { FieldTypes } = require("../../../constants")
|
||||
const { integrations } = require("../../../integrations")
|
||||
|
||||
validateJs.extend(validateJs.validators.datetime, {
|
||||
parse: function (value) {
|
||||
|
@ -15,6 +16,20 @@ validateJs.extend(validateJs.validators.datetime, {
|
|||
},
|
||||
})
|
||||
|
||||
exports.makeExternalQuery = async (appId, json) => {
|
||||
const datasourceId = json.endpoint.datasourceId
|
||||
const database = new CouchDB(ctx.appId)
|
||||
const datasource = await database.get(datasourceId)
|
||||
const Integration = integrations[datasource.source]
|
||||
// query is the opinionated function
|
||||
if (Integration.prototype.query) {
|
||||
const integration = new Integration(datasource.config)
|
||||
return integration.query(json)
|
||||
} else {
|
||||
throw "Datasource does not support query."
|
||||
}
|
||||
}
|
||||
|
||||
exports.findRow = async (ctx, db, tableId, rowId) => {
|
||||
let row
|
||||
// TODO remove special user case in future
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
const { fullSearch, paginatedSearch } = require("./utils")
|
||||
const CouchDB = require("../../../db")
|
||||
const { outputProcessing } = require("../../../utilities/rowProcessor")
|
||||
|
||||
exports.rowSearch = async ctx => {
|
||||
const appId = ctx.appId
|
||||
const { tableId } = ctx.params
|
||||
const db = new CouchDB(appId)
|
||||
const { paginate, query, ...params } = ctx.request.body
|
||||
params.tableId = tableId
|
||||
|
||||
let response
|
||||
if (paginate) {
|
||||
response = await paginatedSearch(appId, query, params)
|
||||
} else {
|
||||
response = await fullSearch(appId, query, params)
|
||||
}
|
||||
|
||||
// Enrich search results with relationships
|
||||
if (response.rows && response.rows.length) {
|
||||
const table = await db.get(tableId)
|
||||
response.rows = await outputProcessing(appId, table, response.rows)
|
||||
}
|
||||
|
||||
ctx.body = response
|
||||
}
|
|
@ -23,7 +23,6 @@ const queryRoutes = require("./query")
|
|||
const hostingRoutes = require("./hosting")
|
||||
const backupRoutes = require("./backup")
|
||||
const devRoutes = require("./dev")
|
||||
const searchRoutes = require("./search")
|
||||
|
||||
exports.mainRoutes = [
|
||||
authRoutes,
|
||||
|
@ -52,7 +51,6 @@ exports.mainRoutes = [
|
|||
// this could be breaking as koa may recognise other routes as this
|
||||
tableRoutes,
|
||||
rowRoutes,
|
||||
searchRoutes,
|
||||
]
|
||||
|
||||
exports.staticRoutes = staticRoutes
|
||||
|
|
|
@ -32,6 +32,13 @@ router
|
|||
authorized(PermissionTypes.TABLE, PermissionLevels.READ),
|
||||
rowController.find
|
||||
)
|
||||
.post(
|
||||
"/api/:tableId/search",
|
||||
paramResource("tableId"),
|
||||
authorized(PermissionTypes.TABLE, PermissionLevels.READ),
|
||||
rowController.search
|
||||
)
|
||||
|
||||
.post(
|
||||
"/api/:tableId/rows",
|
||||
paramResource("tableId"),
|
||||
|
@ -40,8 +47,8 @@ router
|
|||
rowController.save
|
||||
)
|
||||
.patch(
|
||||
"/api/:tableId/rows/:rowId",
|
||||
paramSubResource("tableId", "rowId"),
|
||||
"/api/:tableId/rows",
|
||||
paramResource("tableId"),
|
||||
authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
||||
rowController.patch
|
||||
)
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
const Router = require("@koa/router")
|
||||
const controller = require("../controllers/search")
|
||||
const {
|
||||
PermissionTypes,
|
||||
PermissionLevels,
|
||||
} = require("@budibase/auth/permissions")
|
||||
const authorized = require("../../middleware/authorized")
|
||||
const { paramResource } = require("../../middleware/resourceId")
|
||||
|
||||
const router = Router()
|
||||
|
||||
router.post(
|
||||
"/api/search/:tableId/rows",
|
||||
paramResource("tableId"),
|
||||
authorized(PermissionTypes.TABLE, PermissionLevels.READ),
|
||||
controller.rowSearch
|
||||
)
|
||||
|
||||
module.exports = router
|
|
@ -201,7 +201,7 @@ describe("/rows", () => {
|
|||
const existing = await config.createRow()
|
||||
|
||||
const res = await request
|
||||
.patch(`/api/${table._id}/rows/${existing._id}`)
|
||||
.patch(`/api/${table._id}/rows`)
|
||||
.send({
|
||||
_id: existing._id,
|
||||
_rev: existing._rev,
|
||||
|
@ -225,7 +225,7 @@ describe("/rows", () => {
|
|||
it("should throw an error when given improper types", async () => {
|
||||
const existing = await config.createRow()
|
||||
await request
|
||||
.patch(`/api/${table._id}/rows/${existing._id}`)
|
||||
.patch(`/api/${table._id}/rows`)
|
||||
.send({
|
||||
_id: existing._id,
|
||||
_rev: existing._rev,
|
||||
|
|
Loading…
Reference in New Issue