@budibase/server 1.2.57 → 1.2.58-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__mocks__/mongodb.ts +4 -0
- package/__mocks__/node-fetch.ts +8 -1
- package/builder/assets/index.d7a7633b.js +875 -0
- package/builder/assets/index.fdb1647e.css +6 -0
- package/builder/assets/{vendor.6bcafa60.js → vendor.67dbd8fa.js} +1 -1
- package/builder/index.html +3 -3
- package/coverage/clover.xml +510 -485
- package/coverage/coverage-final.json +18 -18
- package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
- package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
- package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
- package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
- package/coverage/lcov-report/api/controllers/component.js.html +1 -1
- package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
- package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
- package/coverage/lcov-report/api/controllers/index.html +1 -1
- package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
- package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
- package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
- package/coverage/lcov-report/api/controllers/migrations.js.html +1 -1
- package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
- package/coverage/lcov-report/api/controllers/query/index.html +1 -1
- package/coverage/lcov-report/api/controllers/query/validation.js.html +1 -1
- package/coverage/lcov-report/api/controllers/role.js.html +1 -1
- package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/index.html +25 -25
- package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +317 -191
- package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/utils.js.html +27 -18
- package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
- package/coverage/lcov-report/api/controllers/script.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/external.js.html +2 -2
- package/coverage/lcov-report/api/controllers/table/index.html +1 -1
- package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
- package/coverage/lcov-report/api/controllers/user.js.html +19 -19
- package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
- package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
- package/coverage/lcov-report/api/index.html +1 -1
- package/coverage/lcov-report/api/index.js.html +5 -5
- package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
- package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/routes/auth.js.html +1 -1
- package/coverage/lcov-report/api/routes/automation.js.html +1 -1
- package/coverage/lcov-report/api/routes/backup.js.html +1 -1
- package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
- package/coverage/lcov-report/api/routes/component.js.html +1 -1
- package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
- package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
- package/coverage/lcov-report/api/routes/dev.js.html +1 -1
- package/coverage/lcov-report/api/routes/index.html +1 -1
- package/coverage/lcov-report/api/routes/integration.js.html +1 -1
- package/coverage/lcov-report/api/routes/layout.js.html +1 -1
- package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
- package/coverage/lcov-report/api/routes/migrations.js.html +1 -1
- package/coverage/lcov-report/api/routes/permission.js.html +1 -1
- package/coverage/lcov-report/api/routes/query.js.html +1 -1
- package/coverage/lcov-report/api/routes/role.js.html +1 -1
- package/coverage/lcov-report/api/routes/routing.js.html +1 -1
- package/coverage/lcov-report/api/routes/screen.js.html +1 -1
- package/coverage/lcov-report/api/routes/script.js.html +1 -1
- package/coverage/lcov-report/api/routes/table.js.html +1 -1
- package/coverage/lcov-report/api/routes/templates.js.html +1 -1
- package/coverage/lcov-report/api/routes/user.js.html +1 -1
- package/coverage/lcov-report/api/routes/utils/index.html +1 -1
- package/coverage/lcov-report/api/routes/utils/validators.js.html +1 -1
- package/coverage/lcov-report/api/routes/view.js.html +1 -1
- package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/actions.js.html +1 -1
- package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
- package/coverage/lcov-report/automations/bullboard.js.html +9 -6
- package/coverage/lcov-report/automations/index.html +11 -11
- package/coverage/lcov-report/automations/index.js.html +1 -1
- package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
- package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
- package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
- package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
- package/coverage/lcov-report/automations/steps/index.html +1 -1
- package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
- package/coverage/lcov-report/automations/steps/loop.js.html +1 -1
- package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
- package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
- package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
- package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
- package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
- package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
- package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/triggers.js.html +1 -1
- package/coverage/lcov-report/constants/index.html +1 -1
- package/coverage/lcov-report/constants/index.js.html +30 -30
- package/coverage/lcov-report/constants/layouts.js.html +1 -1
- package/coverage/lcov-report/constants/screens.js.html +1 -1
- package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
- package/coverage/lcov-report/db/index.html +1 -1
- package/coverage/lcov-report/db/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
- package/coverage/lcov-report/db/newid.js.html +4 -4
- package/coverage/lcov-report/db/utils.js.html +69 -69
- package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
- package/coverage/lcov-report/events/index.html +1 -1
- package/coverage/lcov-report/events/index.js.html +1 -1
- package/coverage/lcov-report/events/utils.js.html +1 -1
- package/coverage/lcov-report/index.html +33 -33
- package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
- package/coverage/lcov-report/integrations/tests/index.html +1 -1
- package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
- package/coverage/lcov-report/middleware/builder.js.html +11 -11
- package/coverage/lcov-report/middleware/currentapp.js.html +33 -33
- package/coverage/lcov-report/middleware/index.html +10 -10
- package/coverage/lcov-report/middleware/joi-validator.js.html +18 -9
- package/coverage/lcov-report/middleware/publicApi.js.html +1 -1
- package/coverage/lcov-report/middleware/resourceId.js.html +12 -12
- package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
- package/coverage/lcov-report/middleware/utils.js.html +2 -2
- package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
- package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
- package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
- package/coverage/lcov-report/utilities/global.js.html +31 -31
- package/coverage/lcov-report/utilities/index.html +1 -1
- package/coverage/lcov-report/utilities/index.js.html +23 -23
- package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +2 -2
- package/coverage/lcov-report/utilities/queue/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
- package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
- package/coverage/lcov-report/utilities/security.js.html +2 -2
- package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
- package/coverage/lcov-report/utilities/usageQuota/index.html +1 -1
- package/coverage/lcov-report/utilities/usageQuota/rows.js.html +1 -1
- package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +1 -1
- package/coverage/lcov-report/utilities/users.js.html +7 -7
- package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
- package/coverage/lcov.info +824 -780
- package/dist/api/controllers/row/internalSearch.js +39 -4
- package/dist/api/controllers/row/utils.js +3 -1
- package/dist/api/controllers/static/index.js +26 -2
- package/dist/api/controllers/static/templates/preview.hbs +103 -0
- package/dist/api/controllers/table/external.js +1 -1
- package/dist/api/routes/static.js +3 -0
- package/dist/app.js +4 -3
- package/dist/automations/bullboard.js +1 -0
- package/dist/automations/logging/index.js +3 -0
- package/dist/integrations/base/query.js +27 -0
- package/dist/integrations/base/sql.js +62 -30
- package/dist/integrations/base/utils.js +13 -27
- package/dist/integrations/microsoftSqlServer.js +5 -2
- package/dist/integrations/mongodb.js +35 -8
- package/dist/integrations/mysql.js +2 -2
- package/dist/integrations/postgres.js +5 -2
- package/dist/middleware/joi-validator.js +7 -4
- package/dist/package.json +7 -7
- package/dist/threads/index.js +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utilities/fileSystem/index.js +2 -1
- package/dist/utilities/redis.js +1 -0
- package/package.json +8 -8
- package/src/api/controllers/row/internalSearch.js +45 -3
- package/src/api/controllers/row/utils.js +4 -1
- package/src/api/controllers/static/index.ts +21 -1
- package/src/api/controllers/static/templates/preview.hbs +103 -0
- package/src/api/controllers/table/external.js +1 -1
- package/src/api/routes/static.ts +12 -0
- package/src/api/routes/tests/internalSearch.spec.js +176 -0
- package/src/api/routes/tests/static.spec.js +19 -7
- package/src/app.ts +5 -3
- package/src/automations/bullboard.js +1 -0
- package/src/automations/logging/index.ts +3 -0
- package/src/integrations/base/query.ts +16 -0
- package/src/integrations/base/sql.ts +67 -30
- package/src/integrations/base/utils.ts +9 -18
- package/src/integrations/microsoftSqlServer.ts +3 -2
- package/src/integrations/mongodb.ts +35 -8
- package/src/integrations/mysql.ts +1 -1
- package/src/integrations/postgres.ts +2 -1
- package/src/integrations/tests/mongo.spec.js +218 -0
- package/src/integrations/tests/sql.spec.js +101 -11
- package/src/middleware/joi-validator.js +7 -4
- package/src/threads/index.ts +1 -0
- package/src/utilities/fileSystem/index.js +2 -0
- package/src/utilities/redis.js +1 -0
- package/builder/assets/index.08dd0c5e.css +0 -6
- package/builder/assets/index.b6eebfec.js +0 -975
|
@@ -14,7 +14,7 @@ const { join } = require("path");
|
|
|
14
14
|
const uuid = require("uuid/v4");
|
|
15
15
|
const { doWithDB, dangerousGetDB, closeDB, } = require("@budibase/backend-core/db");
|
|
16
16
|
const { ObjectStoreBuckets } = require("../../constants");
|
|
17
|
-
const { upload, retrieve, retrieveToTmp, streamUpload, deleteFolder, downloadTarball, } = require("./utilities");
|
|
17
|
+
const { upload, retrieve, retrieveToTmp, streamUpload, deleteFolder, downloadTarball, deleteFiles, } = require("./utilities");
|
|
18
18
|
const { updateClientLibrary } = require("./clientLibrary");
|
|
19
19
|
const env = require("../../environment");
|
|
20
20
|
const { USER_METDATA_PREFIX, LINK_USER_METADATA_PREFIX, TABLE_ROW_PREFIX, } = require("../../db/utils");
|
|
@@ -291,5 +291,6 @@ exports.cleanup = appIds => {
|
|
|
291
291
|
exports.upload = upload;
|
|
292
292
|
exports.retrieve = retrieve;
|
|
293
293
|
exports.retrieveToTmp = retrieveToTmp;
|
|
294
|
+
exports.deleteFiles = deleteFiles;
|
|
294
295
|
exports.TOP_LEVEL_PATH = TOP_LEVEL_PATH;
|
|
295
296
|
exports.NODE_MODULES_PATH = NODE_MODULES_PATH;
|
package/dist/utilities/redis.js
CHANGED
|
@@ -30,6 +30,7 @@ exports.shutdown = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
30
30
|
yield debounceClient.finish();
|
|
31
31
|
if (flagClient)
|
|
32
32
|
yield flagClient.finish();
|
|
33
|
+
console.log("Redis shutdown");
|
|
33
34
|
});
|
|
34
35
|
exports.doesUserHaveLock = (devAppId, user) => __awaiter(void 0, void 0, void 0, function* () {
|
|
35
36
|
const value = yield devAppClient.get(devAppId);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/server",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.58-alpha.2",
|
|
5
5
|
"description": "Budibase Web Server",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -77,11 +77,11 @@
|
|
|
77
77
|
"license": "GPL-3.0",
|
|
78
78
|
"dependencies": {
|
|
79
79
|
"@apidevtools/swagger-parser": "10.0.3",
|
|
80
|
-
"@budibase/backend-core": "
|
|
81
|
-
"@budibase/client": "
|
|
82
|
-
"@budibase/pro": "1.2.
|
|
83
|
-
"@budibase/string-templates": "
|
|
84
|
-
"@budibase/types": "
|
|
80
|
+
"@budibase/backend-core": "1.2.58-alpha.2",
|
|
81
|
+
"@budibase/client": "1.2.58-alpha.2",
|
|
82
|
+
"@budibase/pro": "1.2.58-alpha.1",
|
|
83
|
+
"@budibase/string-templates": "1.2.58-alpha.2",
|
|
84
|
+
"@budibase/types": "1.2.58-alpha.2",
|
|
85
85
|
"@bull-board/api": "3.7.0",
|
|
86
86
|
"@bull-board/koa": "3.9.4",
|
|
87
87
|
"@elastic/elasticsearch": "7.10.0",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"google-auth-library": "7.12.0",
|
|
108
108
|
"google-spreadsheet": "3.2.0",
|
|
109
109
|
"jimp": "0.16.1",
|
|
110
|
-
"joi": "17.
|
|
110
|
+
"joi": "17.6.0",
|
|
111
111
|
"js-yaml": "4.1.0",
|
|
112
112
|
"jsonschema": "1.4.0",
|
|
113
113
|
"knex": "0.95.15",
|
|
@@ -196,5 +196,5 @@
|
|
|
196
196
|
"optionalDependencies": {
|
|
197
197
|
"oracledb": "5.3.0"
|
|
198
198
|
},
|
|
199
|
-
"gitHead": "
|
|
199
|
+
"gitHead": "675fdd4b88456e4a663f8e2efb148556e0a35aab"
|
|
200
200
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { SearchIndexes } = require("../../../db/utils")
|
|
2
|
+
const { removeKeyNumbering } = require("./utils")
|
|
2
3
|
const fetch = require("node-fetch")
|
|
3
4
|
const { getCouchInfo } = require("@budibase/backend-core/db")
|
|
4
5
|
const { getAppId } = require("@budibase/backend-core/context")
|
|
@@ -20,6 +21,8 @@ class QueryBuilder {
|
|
|
20
21
|
notEmpty: {},
|
|
21
22
|
oneOf: {},
|
|
22
23
|
contains: {},
|
|
24
|
+
notContains: {},
|
|
25
|
+
containsAny: {},
|
|
23
26
|
...base,
|
|
24
27
|
}
|
|
25
28
|
this.limit = 50
|
|
@@ -125,6 +128,16 @@ class QueryBuilder {
|
|
|
125
128
|
return this
|
|
126
129
|
}
|
|
127
130
|
|
|
131
|
+
addNotContains(key, value) {
|
|
132
|
+
this.query.notContains[key] = value
|
|
133
|
+
return this
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
addContainsAny(key, value) {
|
|
137
|
+
this.query.containsAny[key] = value
|
|
138
|
+
return this
|
|
139
|
+
}
|
|
140
|
+
|
|
128
141
|
/**
|
|
129
142
|
* Preprocesses a value before going into a lucene search.
|
|
130
143
|
* Transforms strings to lowercase and wraps strings and bools in quotes.
|
|
@@ -170,11 +183,29 @@ class QueryBuilder {
|
|
|
170
183
|
return `${key}:${builder.preprocess(value, allPreProcessingOpts)}`
|
|
171
184
|
}
|
|
172
185
|
|
|
173
|
-
const contains = (key, value) => {
|
|
174
|
-
if (
|
|
186
|
+
const contains = (key, value, mode = "AND") => {
|
|
187
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
175
188
|
return null
|
|
176
189
|
}
|
|
177
|
-
|
|
190
|
+
if (!Array.isArray(value)) {
|
|
191
|
+
return `${key}:${value}`
|
|
192
|
+
}
|
|
193
|
+
let statement = `${builder.preprocess(value[0], { escape: true })}`
|
|
194
|
+
for (let i = 1; i < value.length; i++) {
|
|
195
|
+
statement += ` ${mode} ${builder.preprocess(value[i], {
|
|
196
|
+
escape: true,
|
|
197
|
+
})}`
|
|
198
|
+
}
|
|
199
|
+
return `${key}:(${statement})`
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const notContains = (key, value) => {
|
|
203
|
+
const allPrefix = allOr === "" ? "*:* AND" : ""
|
|
204
|
+
return allPrefix + "NOT " + contains(key, value)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const containsAny = (key, value) => {
|
|
208
|
+
return contains(key, value, "OR")
|
|
178
209
|
}
|
|
179
210
|
|
|
180
211
|
const oneOf = (key, value) => {
|
|
@@ -197,6 +228,8 @@ class QueryBuilder {
|
|
|
197
228
|
|
|
198
229
|
function build(structure, queryFn) {
|
|
199
230
|
for (let [key, value] of Object.entries(structure)) {
|
|
231
|
+
// check for new format - remove numbering if needed
|
|
232
|
+
key = removeKeyNumbering(key)
|
|
200
233
|
key = builder.preprocess(key.replace(/ /g, "_"), {
|
|
201
234
|
escape: true,
|
|
202
235
|
})
|
|
@@ -275,6 +308,12 @@ class QueryBuilder {
|
|
|
275
308
|
if (this.query.contains) {
|
|
276
309
|
build(this.query.contains, contains)
|
|
277
310
|
}
|
|
311
|
+
if (this.query.notContains) {
|
|
312
|
+
build(this.query.notContains, notContains)
|
|
313
|
+
}
|
|
314
|
+
if (this.query.containsAny) {
|
|
315
|
+
build(this.query.containsAny, containsAny)
|
|
316
|
+
}
|
|
278
317
|
// make sure table ID is always added as an AND
|
|
279
318
|
if (tableId) {
|
|
280
319
|
query = `(${query})`
|
|
@@ -310,6 +349,9 @@ class QueryBuilder {
|
|
|
310
349
|
}
|
|
311
350
|
}
|
|
312
351
|
|
|
352
|
+
// exported for unit testing
|
|
353
|
+
exports.QueryBuilder = QueryBuilder
|
|
354
|
+
|
|
313
355
|
/**
|
|
314
356
|
* Executes a lucene search query.
|
|
315
357
|
* @param url The query URL
|
|
@@ -3,8 +3,9 @@ const { cloneDeep } = require("lodash/fp")
|
|
|
3
3
|
const { InternalTables } = require("../../../db/utils")
|
|
4
4
|
const userController = require("../user")
|
|
5
5
|
const { FieldTypes } = require("../../../constants")
|
|
6
|
-
const { makeExternalQuery } = require("../../../integrations/base/utils")
|
|
7
6
|
const { getAppDB } = require("@budibase/backend-core/context")
|
|
7
|
+
const { makeExternalQuery } = require("../../../integrations/base/query")
|
|
8
|
+
const { removeKeyNumbering } = require("../../../integrations/base/utils")
|
|
8
9
|
|
|
9
10
|
validateJs.extend(validateJs.validators.datetime, {
|
|
10
11
|
parse: function (value) {
|
|
@@ -16,6 +17,8 @@ validateJs.extend(validateJs.validators.datetime, {
|
|
|
16
17
|
},
|
|
17
18
|
})
|
|
18
19
|
|
|
20
|
+
exports.removeKeyNumbering = removeKeyNumbering
|
|
21
|
+
|
|
19
22
|
exports.getDatasourceAndQuery = async json => {
|
|
20
23
|
const datasourceId = json.endpoint.datasourceId
|
|
21
24
|
const db = getAppDB()
|
|
@@ -12,7 +12,7 @@ const {
|
|
|
12
12
|
} = require("../../../utilities/fileSystem")
|
|
13
13
|
const env = require("../../../environment")
|
|
14
14
|
const { clientLibraryPath } = require("../../../utilities")
|
|
15
|
-
const { upload } = require("../../../utilities/fileSystem")
|
|
15
|
+
const { upload, deleteFiles } = require("../../../utilities/fileSystem")
|
|
16
16
|
const { attachmentsRelativeURL } = require("../../../utilities")
|
|
17
17
|
const { DocumentType } = require("../../../db/utils")
|
|
18
18
|
const { getAppDB, getAppId } = require("@budibase/backend-core/context")
|
|
@@ -97,6 +97,10 @@ export const uploadFile = async function (ctx: any) {
|
|
|
97
97
|
ctx.body = await Promise.all(uploads)
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
export const deleteObjects = async function (ctx: any) {
|
|
101
|
+
ctx.body = await deleteFiles(ObjectStoreBuckets.APPS, ctx.request.body.keys)
|
|
102
|
+
}
|
|
103
|
+
|
|
100
104
|
export const serveApp = async function (ctx: any) {
|
|
101
105
|
const db = getAppDB({ skip_setup: true })
|
|
102
106
|
const appInfo = await db.get(DocumentType.APP_METADATA)
|
|
@@ -124,6 +128,22 @@ export const serveApp = async function (ctx: any) {
|
|
|
124
128
|
}
|
|
125
129
|
}
|
|
126
130
|
|
|
131
|
+
export const serveBuilderPreview = async function (ctx: any) {
|
|
132
|
+
const db = getAppDB({ skip_setup: true })
|
|
133
|
+
const appInfo = await db.get(DocumentType.APP_METADATA)
|
|
134
|
+
|
|
135
|
+
if (!env.isJest()) {
|
|
136
|
+
let appId = getAppId()
|
|
137
|
+
const previewHbs = loadHandlebarsFile(`${__dirname}/templates/preview.hbs`)
|
|
138
|
+
ctx.body = await processString(previewHbs, {
|
|
139
|
+
clientLibPath: clientLibraryPath(appId, appInfo.version, ctx),
|
|
140
|
+
})
|
|
141
|
+
} else {
|
|
142
|
+
// just return the app info for jest to assert on
|
|
143
|
+
ctx.body = { ...appInfo, builderPreview: true }
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
127
147
|
export const serveClientLibrary = async function (ctx: any) {
|
|
128
148
|
return send(ctx, "budibase-client.js", {
|
|
129
149
|
root: join(NODE_MODULES_PATH, "@budibase", "client", "dist"),
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<html lang="en">
|
|
2
|
+
<head>
|
|
3
|
+
<title>Budibase Builder Preview</title>
|
|
4
|
+
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
|
|
5
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
|
6
|
+
<link
|
|
7
|
+
href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600;700&display=swap"
|
|
8
|
+
rel="stylesheet"
|
|
9
|
+
/>
|
|
10
|
+
<link
|
|
11
|
+
href="https://cdn.jsdelivr.net/npm/remixicon@2.5.0/fonts/remixicon.css"
|
|
12
|
+
rel="stylesheet"
|
|
13
|
+
/>
|
|
14
|
+
<style>
|
|
15
|
+
html, body {
|
|
16
|
+
padding: 0;
|
|
17
|
+
margin: 0;
|
|
18
|
+
}
|
|
19
|
+
html {
|
|
20
|
+
height: 100%;
|
|
21
|
+
width: 100%;
|
|
22
|
+
overflow: hidden;
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
justify-content: flex-start;
|
|
26
|
+
align-items: stretch;
|
|
27
|
+
}
|
|
28
|
+
body {
|
|
29
|
+
flex: 1 1 auto;
|
|
30
|
+
overflow: hidden;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
*,
|
|
34
|
+
*:before,
|
|
35
|
+
*:after {
|
|
36
|
+
box-sizing: border-box;
|
|
37
|
+
}
|
|
38
|
+
</style>
|
|
39
|
+
<script src='{{ clientLibPath }}'></script>
|
|
40
|
+
<script>
|
|
41
|
+
function receiveMessage(event) {
|
|
42
|
+
if (!event.data) {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Parse received message
|
|
47
|
+
// If parsing fails, just ignore and wait for the next message
|
|
48
|
+
let parsed
|
|
49
|
+
try {
|
|
50
|
+
parsed = JSON.parse(event.data)
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error("Client received invalid JSON")
|
|
53
|
+
// Ignore
|
|
54
|
+
}
|
|
55
|
+
if (!parsed || !parsed.isBudibaseEvent) {
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Extract data from message
|
|
60
|
+
const {
|
|
61
|
+
selectedComponentId,
|
|
62
|
+
layout,
|
|
63
|
+
screen,
|
|
64
|
+
appId,
|
|
65
|
+
theme,
|
|
66
|
+
customTheme,
|
|
67
|
+
previewDevice,
|
|
68
|
+
navigation,
|
|
69
|
+
hiddenComponentIds
|
|
70
|
+
} = parsed
|
|
71
|
+
|
|
72
|
+
// Set some flags so the app knows we're in the builder
|
|
73
|
+
window["##BUDIBASE_IN_BUILDER##"] = true
|
|
74
|
+
window["##BUDIBASE_APP_ID##"] = appId
|
|
75
|
+
window["##BUDIBASE_PREVIEW_LAYOUT##"] = layout
|
|
76
|
+
window["##BUDIBASE_PREVIEW_SCREEN##"] = screen
|
|
77
|
+
window["##BUDIBASE_SELECTED_COMPONENT_ID##"] = selectedComponentId
|
|
78
|
+
window["##BUDIBASE_PREVIEW_ID##"] = Math.random()
|
|
79
|
+
window["##BUDIBASE_PREVIEW_THEME##"] = theme
|
|
80
|
+
window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"] = customTheme
|
|
81
|
+
window["##BUDIBASE_PREVIEW_DEVICE##"] = previewDevice
|
|
82
|
+
window["##BUDIBASE_PREVIEW_NAVIGATION##"] = navigation
|
|
83
|
+
window["##BUDIBASE_HIDDEN_COMPONENT_IDS##"] = hiddenComponentIds
|
|
84
|
+
|
|
85
|
+
// Initialise app
|
|
86
|
+
try {
|
|
87
|
+
if (window.loadBudibase) {
|
|
88
|
+
window.loadBudibase()
|
|
89
|
+
document.documentElement.classList.add("loaded")
|
|
90
|
+
} else {
|
|
91
|
+
throw "The client library couldn't be loaded"
|
|
92
|
+
}
|
|
93
|
+
} catch (error) {
|
|
94
|
+
window.parent.postMessage({ type: "error", error })
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
window.addEventListener("message", receiveMessage)
|
|
99
|
+
window.parent.postMessage({ type: "ready" })
|
|
100
|
+
</script>
|
|
101
|
+
</head>
|
|
102
|
+
<body></body>
|
|
103
|
+
</html>
|
|
@@ -14,7 +14,7 @@ const {
|
|
|
14
14
|
FieldTypes,
|
|
15
15
|
RelationshipTypes,
|
|
16
16
|
} = require("../../../constants")
|
|
17
|
-
const { makeExternalQuery } = require("../../../integrations/base/
|
|
17
|
+
const { makeExternalQuery } = require("../../../integrations/base/query")
|
|
18
18
|
const { cloneDeep } = require("lodash/fp")
|
|
19
19
|
const csvParser = require("../../../utilities/csvParser")
|
|
20
20
|
const { handleRequest } = require("../row/external")
|
package/src/api/routes/static.ts
CHANGED
|
@@ -38,6 +38,11 @@ router
|
|
|
38
38
|
// TODO: for now this builder endpoint is not authorized/secured, will need to be
|
|
39
39
|
.get("/builder/:file*", controller.serveBuilder)
|
|
40
40
|
.post("/api/attachments/process", authorized(BUILDER), controller.uploadFile)
|
|
41
|
+
.post(
|
|
42
|
+
"/api/attachments/delete",
|
|
43
|
+
authorized(BUILDER),
|
|
44
|
+
controller.deleteObjects
|
|
45
|
+
)
|
|
41
46
|
.post("/api/beta/:feature", controller.toggleBetaUiFeature)
|
|
42
47
|
.post(
|
|
43
48
|
"/api/attachments/:tableId/upload",
|
|
@@ -45,6 +50,13 @@ router
|
|
|
45
50
|
authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
|
46
51
|
controller.uploadFile
|
|
47
52
|
)
|
|
53
|
+
.post(
|
|
54
|
+
"/api/attachments/:tableId/delete",
|
|
55
|
+
paramResource("tableId"),
|
|
56
|
+
authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
|
57
|
+
controller.deleteObjects
|
|
58
|
+
)
|
|
59
|
+
.get("/preview", authorized(BUILDER), controller.serveBuilderPreview)
|
|
48
60
|
.get("/:appId/:path*", controller.serveApp)
|
|
49
61
|
.get("/app/:appUrl/:path*", controller.serveApp)
|
|
50
62
|
.post(
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
const search = require("../../controllers/row/internalSearch")
|
|
2
|
+
// this will be mocked out for _search endpoint
|
|
3
|
+
const fetch = require("node-fetch")
|
|
4
|
+
const PARAMS = {
|
|
5
|
+
tableId: "ta_12345679abcdef",
|
|
6
|
+
version: "1",
|
|
7
|
+
bookmark: null,
|
|
8
|
+
sort: null,
|
|
9
|
+
sortOrder: "ascending",
|
|
10
|
+
sortType: "string",
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function checkLucene(resp, expected, params = PARAMS) {
|
|
14
|
+
const query = resp.rows[0].query
|
|
15
|
+
const json = JSON.parse(query)
|
|
16
|
+
if (PARAMS.sort) {
|
|
17
|
+
expect(json.sort).toBe(`${PARAMS.sort}<${PARAMS.sortType}>`)
|
|
18
|
+
}
|
|
19
|
+
if (PARAMS.bookmark) {
|
|
20
|
+
expect(json.bookmark).toBe(PARAMS.bookmark)
|
|
21
|
+
}
|
|
22
|
+
expect(json.include_docs).toBe(true)
|
|
23
|
+
expect(json.q).toBe(`(${expected}) AND tableId:"${params.tableId}"`)
|
|
24
|
+
expect(json.limit).toBe(params.limit || 50)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe("internal search", () => {
|
|
28
|
+
it("default query", async () => {
|
|
29
|
+
const response = await search.paginatedSearch({
|
|
30
|
+
}, PARAMS)
|
|
31
|
+
checkLucene(response, `*:*`)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it("test equal query", async () => {
|
|
35
|
+
const response = await search.paginatedSearch({
|
|
36
|
+
equal: {
|
|
37
|
+
"column": "1",
|
|
38
|
+
}
|
|
39
|
+
}, PARAMS)
|
|
40
|
+
checkLucene(response, `*:* AND column:"1"`)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it("test notEqual query", async () => {
|
|
44
|
+
const response = await search.paginatedSearch({
|
|
45
|
+
notEqual: {
|
|
46
|
+
"column": "1",
|
|
47
|
+
}
|
|
48
|
+
}, PARAMS)
|
|
49
|
+
checkLucene(response, `*:* AND !column:"1"`)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it("test OR query", async () => {
|
|
53
|
+
const response = await search.paginatedSearch({
|
|
54
|
+
allOr: true,
|
|
55
|
+
equal: {
|
|
56
|
+
"column": "2",
|
|
57
|
+
},
|
|
58
|
+
notEqual: {
|
|
59
|
+
"column": "1",
|
|
60
|
+
}
|
|
61
|
+
}, PARAMS)
|
|
62
|
+
checkLucene(response, `column:"2" OR !column:"1"`)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it("test AND query", async () => {
|
|
66
|
+
const response = await search.paginatedSearch({
|
|
67
|
+
equal: {
|
|
68
|
+
"column": "2",
|
|
69
|
+
},
|
|
70
|
+
notEqual: {
|
|
71
|
+
"column": "1",
|
|
72
|
+
}
|
|
73
|
+
}, PARAMS)
|
|
74
|
+
checkLucene(response, `*:* AND column:"2" AND !column:"1"`)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it("test pagination query", async () => {
|
|
78
|
+
const updatedParams = {
|
|
79
|
+
...PARAMS,
|
|
80
|
+
limit: 100,
|
|
81
|
+
bookmark: "awd",
|
|
82
|
+
sort: "column",
|
|
83
|
+
}
|
|
84
|
+
const response = await search.paginatedSearch({
|
|
85
|
+
string: {
|
|
86
|
+
"column": "2",
|
|
87
|
+
},
|
|
88
|
+
}, updatedParams)
|
|
89
|
+
checkLucene(response, `*:* AND column:2*`, updatedParams)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it("test range query", async () => {
|
|
93
|
+
const response = await search.paginatedSearch({
|
|
94
|
+
range: {
|
|
95
|
+
"column": { low: 1, high: 2 },
|
|
96
|
+
},
|
|
97
|
+
}, PARAMS)
|
|
98
|
+
checkLucene(response, `*:* AND column:[1 TO 2]`, PARAMS)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it("test empty query", async () => {
|
|
102
|
+
const response = await search.paginatedSearch({
|
|
103
|
+
empty: {
|
|
104
|
+
"column": "",
|
|
105
|
+
},
|
|
106
|
+
}, PARAMS)
|
|
107
|
+
checkLucene(response, `*:* AND !column:["" TO *]`, PARAMS)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it("test notEmpty query", async () => {
|
|
111
|
+
const response = await search.paginatedSearch({
|
|
112
|
+
notEmpty: {
|
|
113
|
+
"column": "",
|
|
114
|
+
},
|
|
115
|
+
}, PARAMS)
|
|
116
|
+
checkLucene(response, `*:* AND column:["" TO *]`, PARAMS)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it("test oneOf query", async () => {
|
|
120
|
+
const response = await search.paginatedSearch({
|
|
121
|
+
oneOf: {
|
|
122
|
+
"column": ["a", "b"],
|
|
123
|
+
},
|
|
124
|
+
}, PARAMS)
|
|
125
|
+
checkLucene(response, `*:* AND column:("a" OR "b")`, PARAMS)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it("test contains query", async () => {
|
|
129
|
+
const response = await search.paginatedSearch({
|
|
130
|
+
contains: {
|
|
131
|
+
"column": "a",
|
|
132
|
+
"colArr": [1, 2, 3],
|
|
133
|
+
},
|
|
134
|
+
}, PARAMS)
|
|
135
|
+
checkLucene(response, `*:* AND column:a AND colArr:(1 AND 2 AND 3)`, PARAMS)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it("test multiple of same column", async () => {
|
|
139
|
+
const response = await search.paginatedSearch({
|
|
140
|
+
allOr: true,
|
|
141
|
+
equal: {
|
|
142
|
+
"1:column": "a",
|
|
143
|
+
"2:column": "b",
|
|
144
|
+
"3:column": "c",
|
|
145
|
+
},
|
|
146
|
+
}, PARAMS)
|
|
147
|
+
checkLucene(response, `column:"a" OR column:"b" OR column:"c"`, PARAMS)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it("check a weird case for lucene building", async () => {
|
|
151
|
+
const response = await search.paginatedSearch({
|
|
152
|
+
equal: {
|
|
153
|
+
"1:1:column": "a",
|
|
154
|
+
},
|
|
155
|
+
}, PARAMS)
|
|
156
|
+
checkLucene(response, `*:* AND 1\\:column:"a"`, PARAMS)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it("test containsAny query", async () => {
|
|
160
|
+
const response = await search.paginatedSearch({
|
|
161
|
+
containsAny: {
|
|
162
|
+
"column": ["a", "b", "c"]
|
|
163
|
+
},
|
|
164
|
+
}, PARAMS)
|
|
165
|
+
checkLucene(response, `*:* AND column:(a OR b OR c)`, PARAMS)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it("test notContains query", async () => {
|
|
169
|
+
const response = await search.paginatedSearch({
|
|
170
|
+
notContains: {
|
|
171
|
+
"column": ["a", "b", "c"]
|
|
172
|
+
},
|
|
173
|
+
}, PARAMS)
|
|
174
|
+
checkLucene(response, `*:* AND NOT column:(a AND b AND c)`, PARAMS)
|
|
175
|
+
})
|
|
176
|
+
})
|
|
@@ -40,7 +40,6 @@ describe("/static", () => {
|
|
|
40
40
|
})
|
|
41
41
|
|
|
42
42
|
describe("/app", () => {
|
|
43
|
-
|
|
44
43
|
beforeEach(() => {
|
|
45
44
|
jest.clearAllMocks()
|
|
46
45
|
})
|
|
@@ -60,7 +59,7 @@ describe("/static", () => {
|
|
|
60
59
|
it("should serve the app by url", async () => {
|
|
61
60
|
const headers = config.defaultHeaders()
|
|
62
61
|
delete headers[constants.Headers.APP_ID]
|
|
63
|
-
|
|
62
|
+
|
|
64
63
|
const res = await request
|
|
65
64
|
.get(`/app${config.prodApp.url}`)
|
|
66
65
|
.set(headers)
|
|
@@ -82,7 +81,7 @@ describe("/static", () => {
|
|
|
82
81
|
describe("/attachments", () => {
|
|
83
82
|
describe("generateSignedUrls", () => {
|
|
84
83
|
let datasource
|
|
85
|
-
|
|
84
|
+
|
|
86
85
|
beforeEach(async () => {
|
|
87
86
|
datasource = await config.createDatasource({
|
|
88
87
|
datasource: {
|
|
@@ -93,7 +92,7 @@ describe("/static", () => {
|
|
|
93
92
|
},
|
|
94
93
|
})
|
|
95
94
|
})
|
|
96
|
-
|
|
95
|
+
|
|
97
96
|
it("should be able to generate a signed upload URL", async () => {
|
|
98
97
|
const bucket = "foo"
|
|
99
98
|
const key = "bar"
|
|
@@ -108,7 +107,7 @@ describe("/static", () => {
|
|
|
108
107
|
`https://${bucket}.s3.eu-west-1.amazonaws.com/${key}`
|
|
109
108
|
)
|
|
110
109
|
})
|
|
111
|
-
|
|
110
|
+
|
|
112
111
|
it("should handle an invalid datasource ID", async () => {
|
|
113
112
|
const res = await request
|
|
114
113
|
.post(`/api/attachments/foo/url`)
|
|
@@ -123,7 +122,7 @@ describe("/static", () => {
|
|
|
123
122
|
"The specified datasource could not be found"
|
|
124
123
|
)
|
|
125
124
|
})
|
|
126
|
-
|
|
125
|
+
|
|
127
126
|
it("should require a bucket parameter", async () => {
|
|
128
127
|
const res = await request
|
|
129
128
|
.post(`/api/attachments/${datasource._id}/url`)
|
|
@@ -136,7 +135,7 @@ describe("/static", () => {
|
|
|
136
135
|
.expect(400)
|
|
137
136
|
expect(res.body.message).toEqual("bucket and key values are required")
|
|
138
137
|
})
|
|
139
|
-
|
|
138
|
+
|
|
140
139
|
it("should require a key parameter", async () => {
|
|
141
140
|
const res = await request
|
|
142
141
|
.post(`/api/attachments/${datasource._id}/url`)
|
|
@@ -151,4 +150,17 @@ describe("/static", () => {
|
|
|
151
150
|
})
|
|
152
151
|
})
|
|
153
152
|
|
|
153
|
+
describe("/preview", () => {
|
|
154
|
+
beforeEach(() => {
|
|
155
|
+
jest.clearAllMocks()
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it("should serve the builder preview", async () => {
|
|
159
|
+
const headers = config.defaultHeaders()
|
|
160
|
+
const res = await request.get(`/preview`).set(headers).expect(200)
|
|
161
|
+
|
|
162
|
+
expect(res.body.appId).toBe(config.appId)
|
|
163
|
+
expect(res.body.builderPreview).toBe(true)
|
|
164
|
+
})
|
|
165
|
+
})
|
|
154
166
|
})
|
package/src/app.ts
CHANGED
|
@@ -83,9 +83,7 @@ server.on("close", async () => {
|
|
|
83
83
|
return
|
|
84
84
|
}
|
|
85
85
|
shuttingDown = true
|
|
86
|
-
|
|
87
|
-
console.log("Server Closed")
|
|
88
|
-
}
|
|
86
|
+
console.log("Server Closed")
|
|
89
87
|
await automations.shutdown()
|
|
90
88
|
await redis.shutdown()
|
|
91
89
|
await events.shutdown()
|
|
@@ -167,3 +165,7 @@ process.on("uncaughtException", err => {
|
|
|
167
165
|
process.on("SIGTERM", () => {
|
|
168
166
|
shutdown()
|
|
169
167
|
})
|
|
168
|
+
|
|
169
|
+
process.on("SIGINT", () => {
|
|
170
|
+
shutdown()
|
|
171
|
+
})
|
|
@@ -23,6 +23,9 @@ export async function checkAppMetadata(apps: App[]) {
|
|
|
23
23
|
for (let [key, errors] of Object.entries(metadata.automationErrors)) {
|
|
24
24
|
const updated = []
|
|
25
25
|
for (let error of errors) {
|
|
26
|
+
if (!error) {
|
|
27
|
+
continue
|
|
28
|
+
}
|
|
26
29
|
const startDate = error.split(dbUtils.SEPARATOR)[2]
|
|
27
30
|
if (startDate > maxStartDate) {
|
|
28
31
|
updated.push(error)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { QueryJson, Datasource } from "@budibase/types"
|
|
2
|
+
const { integrations } = require("../index")
|
|
3
|
+
|
|
4
|
+
export async function makeExternalQuery(
|
|
5
|
+
datasource: Datasource,
|
|
6
|
+
json: QueryJson
|
|
7
|
+
) {
|
|
8
|
+
const Integration = integrations[datasource.source]
|
|
9
|
+
// query is the opinionated function
|
|
10
|
+
if (Integration.prototype.query) {
|
|
11
|
+
const integration = new Integration(datasource.config)
|
|
12
|
+
return integration.query(json)
|
|
13
|
+
} else {
|
|
14
|
+
throw "Datasource does not support query."
|
|
15
|
+
}
|
|
16
|
+
}
|