Adding schema validation and API endpoint to data sources for query.

This commit is contained in:
mike12345567 2021-06-04 14:53:49 +01:00
parent fd2b7c415a
commit c00f1ea0bc
4 changed files with 70 additions and 8 deletions

View File

@ -64,7 +64,17 @@ exports.find = async function (ctx) {
// dynamic query functionality // dynamic query functionality
exports.query = async function (ctx) { 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 source = integrations[datasource.source]
// query is the opinionated function
if (source.query) {
ctx.body = await source.query(queryJson)
} else {
ctx.throw(400, "Datasource does not support query.")
}
} }
// TODO: merge endpoint with main datasource endpoint // TODO: merge endpoint with main datasource endpoint

View File

@ -8,7 +8,7 @@ const {
PermissionTypes, PermissionTypes,
} = require("@budibase/auth/permissions") } = require("@budibase/auth/permissions")
const Joi = require("joi") const Joi = require("joi")
const { FieldTypes } = require("../../constants") const { FieldTypes, DataSourceOperation, SortDirection } = require("../../constants")
const router = Router() const router = Router()
@ -31,6 +31,35 @@ function generatePlusDatasourceSchema() {
}).unknown(true)) }).unknown(true))
} }
function generateQueryDatasourceSchema() {
// prettier-ignore
return joiValidator.body(Joi.object({
endpoint: Joi.object({
datasourceId: Joi.string().required(),
operation: Joi.string().required().valid(...Object.values(DataSourceOperation)),
entityId: Joi.string().required(),
}).required(),
resource: Joi.object({
fields: Joi.array().items(Joi.string()).optional(),
}).optional(),
body: Joi.object().optional(),
sort: Joi.object().optional(),
filters: Joi.object({
string: Joi.object().optional(),
range: Joi.object().optional(),
equal: Joi.object().optional(),
notEqual: Joi.object().optional(),
empty: Joi.object().optional(),
notEmpty: Joi.object().optional(),
}).optional(),
paginate: Joi.object({
page: Joi.string().alphanum().optional(),
limit: Joi.number().optional(),
}).optional(),
}))
}
router router
.get("/api/datasources", authorized(BUILDER), datasourceController.fetch) .get("/api/datasources", authorized(BUILDER), datasourceController.fetch)
.get( .get(
@ -40,9 +69,16 @@ router
) )
.post( .post(
"/api/datasources/plus", "/api/datasources/plus",
authorized(PermissionTypes.TABLE, PermissionLevels.READ),
generatePlusDatasourceSchema(), generatePlusDatasourceSchema(),
datasourceController.plus datasourceController.plus
) )
.post(
"/api/datasources/query",
authorized(PermissionTypes.TABLE, PermissionLevels.READ),
generateQueryDatasourceSchema(),
datasourceController.query
)
.post("/api/datasources", authorized(BUILDER), datasourceController.save) .post("/api/datasources", authorized(BUILDER), datasourceController.save)
.delete( .delete(
"/api/datasources/:datasourceId/:revId", "/api/datasources/:datasourceId/:revId",

View File

@ -31,6 +31,19 @@ exports.AuthTypes = {
EXTERNAL: "external", EXTERNAL: "external",
} }
exports.DataSourceOperation = {
CREATE: "CREATE",
READ: "READ",
UPDATE: "UPDATE",
DELETE: "DELETE",
}
exports.SortDirection = {
ASCENDING: "ASCENDING",
DESCENDING: "DESCENDING",
}
exports.USERS_TABLE_SCHEMA = { exports.USERS_TABLE_SCHEMA = {
_id: "ta_users", _id: "ta_users",
type: "table", type: "table",

View File

@ -1,4 +1,7 @@
const { Operation, SortDirection } = require("./constants") const {
DataSourceOperation,
SortDirection,
} = require("../../constants")
const BASE_LIMIT = 5000 const BASE_LIMIT = 5000
@ -113,20 +116,20 @@ class SqlQueryBuilder {
const knex = require("knex")({ client: this._client }) const knex = require("knex")({ client: this._client })
let query let query
switch (this._operation(json)) { switch (this._operation(json)) {
case Operation.CREATE: case DataSourceOperation.CREATE:
query = buildCreate(knex, json) query = buildCreate(knex, json)
break break
case Operation.READ: case DataSourceOperation.READ:
query = buildRead(knex, json, this._limit) query = buildRead(knex, json, this._limit)
break break
case Operation.UPDATE: case DataSourceOperation.UPDATE:
query = buildUpdate(knex, json) query = buildUpdate(knex, json)
break break
case Operation.DELETE: case DataSourceOperation.DELETE:
query = buildDelete(knex, json) query = buildDelete(knex, json)
break break
default: default:
throw `Operation ${operation} type is not supported by SQL query builder` throw `Operation type is not supported by SQL query builder`
} }
return query.toSQL().toNative() return query.toSQL().toNative()
} }