From 091b537e2d4347736fb3394326133794eaba8c15 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 4 Jun 2021 14:53:49 +0100 Subject: [PATCH] Adding schema validation and API endpoint to data sources for query. --- .../server/src/api/controllers/datasource.js | 12 +++++- packages/server/src/api/routes/datasource.js | 38 ++++++++++++++++++- packages/server/src/constants/index.js | 13 +++++++ packages/server/src/integrations/base/sql.js | 15 +++++--- 4 files changed, 70 insertions(+), 8 deletions(-) diff --git a/packages/server/src/api/controllers/datasource.js b/packages/server/src/api/controllers/datasource.js index 7af7222cc5..5e5151f76e 100644 --- a/packages/server/src/api/controllers/datasource.js +++ b/packages/server/src/api/controllers/datasource.js @@ -64,7 +64,17 @@ 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 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 diff --git a/packages/server/src/api/routes/datasource.js b/packages/server/src/api/routes/datasource.js index 928fdcfca9..7c3066ac29 100644 --- a/packages/server/src/api/routes/datasource.js +++ b/packages/server/src/api/routes/datasource.js @@ -8,7 +8,7 @@ const { PermissionTypes, } = require("@budibase/auth/permissions") const Joi = require("joi") -const { FieldTypes } = require("../../constants") +const { FieldTypes, DataSourceOperation, SortDirection } = require("../../constants") const router = Router() @@ -31,6 +31,35 @@ function generatePlusDatasourceSchema() { }).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 .get("/api/datasources", authorized(BUILDER), datasourceController.fetch) .get( @@ -40,9 +69,16 @@ router ) .post( "/api/datasources/plus", + authorized(PermissionTypes.TABLE, PermissionLevels.READ), generatePlusDatasourceSchema(), datasourceController.plus ) + .post( + "/api/datasources/query", + authorized(PermissionTypes.TABLE, PermissionLevels.READ), + generateQueryDatasourceSchema(), + datasourceController.query + ) .post("/api/datasources", authorized(BUILDER), datasourceController.save) .delete( "/api/datasources/:datasourceId/:revId", diff --git a/packages/server/src/constants/index.js b/packages/server/src/constants/index.js index e15a5de82d..ced7577de8 100644 --- a/packages/server/src/constants/index.js +++ b/packages/server/src/constants/index.js @@ -31,6 +31,19 @@ exports.AuthTypes = { EXTERNAL: "external", } +exports.DataSourceOperation = { + CREATE: "CREATE", + READ: "READ", + UPDATE: "UPDATE", + DELETE: "DELETE", +} + +exports.SortDirection = { + ASCENDING: "ASCENDING", + DESCENDING: "DESCENDING", +} + + exports.USERS_TABLE_SCHEMA = { _id: "ta_users", type: "table", diff --git a/packages/server/src/integrations/base/sql.js b/packages/server/src/integrations/base/sql.js index 0d18f04e66..61d18656ce 100644 --- a/packages/server/src/integrations/base/sql.js +++ b/packages/server/src/integrations/base/sql.js @@ -1,4 +1,7 @@ -const { Operation, SortDirection } = require("./constants") +const { + DataSourceOperation, + SortDirection, +} = require("../../constants") const BASE_LIMIT = 5000 @@ -113,20 +116,20 @@ class SqlQueryBuilder { const knex = require("knex")({ client: this._client }) let query switch (this._operation(json)) { - case Operation.CREATE: + case DataSourceOperation.CREATE: query = buildCreate(knex, json) break - case Operation.READ: + case DataSourceOperation.READ: query = buildRead(knex, json, this._limit) break - case Operation.UPDATE: + case DataSourceOperation.UPDATE: query = buildUpdate(knex, json) break - case Operation.DELETE: + case DataSourceOperation.DELETE: query = buildDelete(knex, json) break 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() }