@budibase/server 1.0.27-alpha.3 → 1.0.27-alpha.7
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__/node-fetch.ts +18 -0
- package/builder/assets/{index.2dc0d283.js → index.02c16fdb.js} +245 -240
- package/builder/assets/{index.5522fcd5.css → index.0dce895b.css} +1 -1
- package/builder/index.html +2 -2
- package/coverage/clover.xml +423 -319
- package/coverage/coverage-final.json +22 -22
- package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
- package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/controllers/application.js.html +54 -54
- 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 +49 -49
- package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
- package/coverage/lcov-report/api/controllers/hosting.js.html +1 -1
- package/coverage/lcov-report/api/controllers/index.html +20 -20
- 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/permission.js.html +1 -1
- package/coverage/lcov-report/api/controllers/query/index.html +19 -19
- package/coverage/lcov-report/api/controllers/query/index.js.html +100 -121
- package/coverage/lcov-report/api/controllers/query/validation.js.html +6 -3
- 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 +1 -1
- package/coverage/lcov-report/api/controllers/row/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
- 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/external.js.html +1 -1
- 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/table/internal.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
- package/coverage/lcov-report/api/controllers/user.js.html +1 -1
- 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/application.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/hosting.js.html +1 -1
- package/coverage/lcov-report/api/routes/index.html +1 -1
- package/coverage/lcov-report/api/routes/index.js.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/permission.js.html +1 -1
- package/coverage/lcov-report/api/routes/query.js.html +24 -3
- 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/row.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/static.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/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 +1 -1
- package/coverage/lcov-report/automations/index.html +1 -1
- 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/createRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
- package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeQuery.js.html +31 -7
- 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 +11 -11
- package/coverage/lcov-report/automations/steps/integromat.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/automations/utils.js.html +5 -5
- package/coverage/lcov-report/constants/index.html +1 -1
- package/coverage/lcov-report/constants/index.js.html +1 -1
- 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/client.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 +2 -2
- package/coverage/lcov-report/db/utils.js.html +18 -18
- 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 +46 -46
- 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/authorized.js.html +10 -10
- package/coverage/lcov-report/middleware/builder.js.html +8 -8
- package/coverage/lcov-report/middleware/currentapp.js.html +28 -28
- package/coverage/lcov-report/middleware/index.html +1 -1
- package/coverage/lcov-report/middleware/joi-validator.js.html +9 -9
- package/coverage/lcov-report/middleware/resourceId.js.html +17 -17
- package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
- package/coverage/lcov-report/middleware/usageQuota.js.html +1 -1
- package/coverage/lcov-report/middleware/utils.js.html +2 -2
- package/coverage/lcov-report/threads/automation.js.html +1 -1
- package/coverage/lcov-report/threads/index.html +31 -31
- package/coverage/lcov-report/threads/index.js.html +11 -11
- package/coverage/lcov-report/threads/query.js.html +358 -91
- package/coverage/lcov-report/threads/utils.js.html +356 -11
- package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
- 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 +13 -13
- package/coverage/lcov-report/utilities/index.html +1 -1
- package/coverage/lcov-report/utilities/index.js.html +9 -9
- 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 +1 -1
- package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
- package/coverage/lcov-report/utilities/users.js.html +1 -1
- package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
- package/coverage/lcov.info +723 -545
- package/dist/api/controllers/query/import/sources/base/index.js +2 -1
- package/dist/api/controllers/query/import/sources/curl.js +11 -5
- package/dist/api/controllers/query/import/sources/openapi2.js +8 -5
- package/dist/api/controllers/query/index.js +45 -47
- package/dist/api/controllers/query/validation.js +2 -1
- package/dist/api/routes/query.js +3 -1
- package/dist/automations/steps/executeQuery.js +8 -1
- package/dist/integrations/rest.js +3 -3
- package/dist/package.json +5 -4
- package/dist/tests/utilities/TestConfiguration.js +7 -0
- package/dist/threads/query.js +145 -52
- package/dist/threads/utils.js +105 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -5
- package/src/api/controllers/query/import/sources/base/index.ts +3 -1
- package/src/api/controllers/query/import/sources/curl.ts +12 -4
- package/src/api/controllers/query/import/sources/openapi2.ts +9 -5
- package/src/api/controllers/query/import/sources/tests/curl/curl.spec.js +2 -3
- package/src/api/controllers/query/import/sources/tests/openapi2/openapi2.spec.js +6 -7
- package/src/api/controllers/query/import/tests/index.spec.js +0 -6
- package/src/api/controllers/query/index.js +48 -55
- package/src/api/controllers/query/validation.js +2 -1
- package/src/api/routes/query.js +8 -1
- package/src/api/routes/tests/query.spec.js +88 -0
- package/src/automations/steps/executeQuery.js +9 -1
- package/src/definitions/datasource.ts +10 -0
- package/src/integrations/rest.ts +6 -3
- package/src/tests/utilities/TestConfiguration.js +10 -0
- package/src/threads/query.js +145 -56
- package/src/threads/utils.js +115 -0
|
@@ -11,12 +11,13 @@ var MethodToVerb;
|
|
|
11
11
|
})(MethodToVerb || (MethodToVerb = {}));
|
|
12
12
|
class ImportSource {
|
|
13
13
|
constructor() {
|
|
14
|
-
this.constructQuery = (datasourceId, name, method, path, queryString, headers = {}, parameters = [], body = undefined) => {
|
|
14
|
+
this.constructQuery = (datasourceId, name, method, path, url, queryString, headers = {}, parameters = [], body = undefined) => {
|
|
15
15
|
const readable = true;
|
|
16
16
|
const queryVerb = this.verbFromMethod(method);
|
|
17
17
|
const transformer = "return data";
|
|
18
18
|
const schema = {};
|
|
19
19
|
path = this.processPath(path);
|
|
20
|
+
path = `${url.origin}/${path}`;
|
|
20
21
|
queryString = this.processQuery(queryString);
|
|
21
22
|
const requestBody = JSON.stringify(body, null, 2);
|
|
22
23
|
const query = {
|
|
@@ -26,8 +26,12 @@ const parseBody = (curl) => {
|
|
|
26
26
|
if (curl.data) {
|
|
27
27
|
const keys = Object.keys(curl.data);
|
|
28
28
|
if (keys.length) {
|
|
29
|
-
|
|
29
|
+
let key = keys[0];
|
|
30
30
|
try {
|
|
31
|
+
// filter out the dollar syntax used by curl for shell support
|
|
32
|
+
if (key.startsWith("$")) {
|
|
33
|
+
key = key.substring(1);
|
|
34
|
+
}
|
|
31
35
|
return JSON.parse(key);
|
|
32
36
|
}
|
|
33
37
|
catch (e) {
|
|
@@ -63,15 +67,17 @@ class Curl extends base_1.ImportSource {
|
|
|
63
67
|
}
|
|
64
68
|
return true;
|
|
65
69
|
});
|
|
70
|
+
this.getUrl = () => {
|
|
71
|
+
return new url_1.URL(this.curl.raw_url);
|
|
72
|
+
};
|
|
66
73
|
this.getInfo = () => __awaiter(this, void 0, void 0, function* () {
|
|
67
|
-
const url =
|
|
74
|
+
const url = this.getUrl();
|
|
68
75
|
return {
|
|
69
|
-
url: url.origin,
|
|
70
76
|
name: url.hostname,
|
|
71
77
|
};
|
|
72
78
|
});
|
|
73
79
|
this.getQueries = (datasourceId) => __awaiter(this, void 0, void 0, function* () {
|
|
74
|
-
const url =
|
|
80
|
+
const url = this.getUrl();
|
|
75
81
|
const name = url.pathname;
|
|
76
82
|
const path = url.pathname;
|
|
77
83
|
const method = this.curl.method;
|
|
@@ -82,7 +88,7 @@ class Curl extends base_1.ImportSource {
|
|
|
82
88
|
if (cookieHeader) {
|
|
83
89
|
headers["Cookie"] = cookieHeader;
|
|
84
90
|
}
|
|
85
|
-
const query = this.constructQuery(datasourceId, name, method, path, queryString, headers, [], requestBody);
|
|
91
|
+
const query = this.constructQuery(datasourceId, name, method, path, url, queryString, headers, [], requestBody);
|
|
86
92
|
return [query];
|
|
87
93
|
});
|
|
88
94
|
}
|
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.OpenAPI2 = void 0;
|
|
13
13
|
const openapi_types_1 = require("openapi-types");
|
|
14
14
|
const openapi_1 = require("./base/openapi");
|
|
15
|
+
const url_1 = require("url");
|
|
15
16
|
const parameterNotRef = (param) => {
|
|
16
17
|
// all refs are deferenced by parser library
|
|
17
18
|
return true;
|
|
@@ -53,19 +54,21 @@ class OpenAPI2 extends openapi_1.OpenAPISource {
|
|
|
53
54
|
return false;
|
|
54
55
|
}
|
|
55
56
|
});
|
|
56
|
-
this.
|
|
57
|
+
this.getUrl = () => {
|
|
57
58
|
var _a;
|
|
58
59
|
const scheme = ((_a = this.document.schemes) === null || _a === void 0 ? void 0 : _a.includes("https")) ? "https" : "http";
|
|
59
60
|
const basePath = this.document.basePath || "";
|
|
60
61
|
const host = this.document.host || "<host>";
|
|
61
|
-
|
|
62
|
+
return new url_1.URL(`${scheme}://${host}${basePath}`);
|
|
63
|
+
};
|
|
64
|
+
this.getInfo = () => __awaiter(this, void 0, void 0, function* () {
|
|
62
65
|
const name = this.document.info.title || "Swagger Import";
|
|
63
66
|
return {
|
|
64
|
-
|
|
65
|
-
name: name,
|
|
67
|
+
name,
|
|
66
68
|
};
|
|
67
69
|
});
|
|
68
70
|
this.getQueries = (datasourceId) => __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
const url = this.getUrl();
|
|
69
72
|
const queries = [];
|
|
70
73
|
for (let [path, pathItem] of Object.entries(this.document.paths)) {
|
|
71
74
|
// parameters that apply to every operation in the path
|
|
@@ -128,7 +131,7 @@ class OpenAPI2 extends openapi_1.OpenAPISource {
|
|
|
128
131
|
}
|
|
129
132
|
}
|
|
130
133
|
}
|
|
131
|
-
const query = this.constructQuery(datasourceId, name, methodName, path, queryString, headers, parameters, requestBody);
|
|
134
|
+
const query = this.constructQuery(datasourceId, name, methodName, path, url, queryString, headers, parameters, requestBody);
|
|
132
135
|
queries.push(query);
|
|
133
136
|
}
|
|
134
137
|
}
|
|
@@ -8,13 +8,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
-
const { processString } = require("@budibase/string-templates");
|
|
12
11
|
const CouchDB = require("../../../db");
|
|
13
12
|
const { generateQueryID, getQueryParams, isProdAppID, } = require("../../../db/utils");
|
|
14
13
|
const { BaseQueryVerbs } = require("../../../constants");
|
|
15
14
|
const { Thread, ThreadType } = require("../../../threads");
|
|
16
15
|
const { save: saveDatasource } = require("../datasource");
|
|
17
16
|
const { RestImporter } = require("./import");
|
|
17
|
+
const { invalidateDynamicVariables } = require("../../../threads/utils");
|
|
18
18
|
const Runner = new Thread(ThreadType.QUERY, { timeoutMs: 10000 });
|
|
19
19
|
// simple function to append "readable" to all read queries
|
|
20
20
|
function enrichQueries(input) {
|
|
@@ -81,44 +81,6 @@ exports.save = function (ctx) {
|
|
|
81
81
|
ctx.message = `Query ${query.name} saved successfully.`;
|
|
82
82
|
});
|
|
83
83
|
};
|
|
84
|
-
function enrichQueryFields(fields, parameters = {}) {
|
|
85
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
86
|
-
const enrichedQuery = {};
|
|
87
|
-
// enrich the fields with dynamic parameters
|
|
88
|
-
for (let key of Object.keys(fields)) {
|
|
89
|
-
if (fields[key] == null) {
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
if (typeof fields[key] === "object") {
|
|
93
|
-
// enrich nested fields object
|
|
94
|
-
enrichedQuery[key] = yield enrichQueryFields(fields[key], parameters);
|
|
95
|
-
}
|
|
96
|
-
else if (typeof fields[key] === "string") {
|
|
97
|
-
// enrich string value as normal
|
|
98
|
-
enrichedQuery[key] = yield processString(fields[key], parameters, {
|
|
99
|
-
noHelpers: true,
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
enrichedQuery[key] = fields[key];
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
if (enrichedQuery.json ||
|
|
107
|
-
enrichedQuery.customData ||
|
|
108
|
-
enrichedQuery.requestBody) {
|
|
109
|
-
try {
|
|
110
|
-
enrichedQuery.json = JSON.parse(enrichedQuery.json ||
|
|
111
|
-
enrichedQuery.customData ||
|
|
112
|
-
enrichedQuery.requestBody);
|
|
113
|
-
}
|
|
114
|
-
catch (err) {
|
|
115
|
-
// no json found, ignore
|
|
116
|
-
}
|
|
117
|
-
delete enrichedQuery.customData;
|
|
118
|
-
}
|
|
119
|
-
return enrichedQuery;
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
84
|
exports.find = function (ctx) {
|
|
123
85
|
return __awaiter(this, void 0, void 0, function* () {
|
|
124
86
|
const db = new CouchDB(ctx.appId);
|
|
@@ -135,14 +97,18 @@ exports.preview = function (ctx) {
|
|
|
135
97
|
return __awaiter(this, void 0, void 0, function* () {
|
|
136
98
|
const db = new CouchDB(ctx.appId);
|
|
137
99
|
const datasource = yield db.get(ctx.request.body.datasourceId);
|
|
138
|
-
|
|
139
|
-
|
|
100
|
+
// preview may not have a queryId as it hasn't been saved, but if it does
|
|
101
|
+
// this stops dynamic variables from calling the same query
|
|
102
|
+
const { fields, parameters, queryVerb, transformer, queryId } = ctx.request.body;
|
|
140
103
|
try {
|
|
141
104
|
const { rows, keys, info, extra } = yield Runner.run({
|
|
105
|
+
appId: ctx.appId,
|
|
142
106
|
datasource,
|
|
143
107
|
queryVerb,
|
|
144
|
-
|
|
108
|
+
fields,
|
|
109
|
+
parameters,
|
|
145
110
|
transformer,
|
|
111
|
+
queryId,
|
|
146
112
|
});
|
|
147
113
|
ctx.body = {
|
|
148
114
|
rows,
|
|
@@ -156,30 +122,62 @@ exports.preview = function (ctx) {
|
|
|
156
122
|
}
|
|
157
123
|
});
|
|
158
124
|
};
|
|
159
|
-
|
|
125
|
+
function execute(ctx, opts = { rowsOnly: false }) {
|
|
160
126
|
return __awaiter(this, void 0, void 0, function* () {
|
|
161
127
|
const db = new CouchDB(ctx.appId);
|
|
162
128
|
const query = yield db.get(ctx.params.queryId);
|
|
163
129
|
const datasource = yield db.get(query.datasourceId);
|
|
164
|
-
const enrichedQuery = yield enrichQueryFields(query.fields, ctx.request.body.parameters);
|
|
165
130
|
// call the relevant CRUD method on the integration class
|
|
166
131
|
try {
|
|
167
|
-
const { rows } = yield Runner.run({
|
|
132
|
+
const { rows, extra } = yield Runner.run({
|
|
133
|
+
appId: ctx.appId,
|
|
168
134
|
datasource,
|
|
169
135
|
queryVerb: query.queryVerb,
|
|
170
|
-
|
|
136
|
+
fields: query.fields,
|
|
137
|
+
parameters: ctx.request.body.parameters,
|
|
171
138
|
transformer: query.transformer,
|
|
139
|
+
queryId: ctx.params.queryId,
|
|
172
140
|
});
|
|
173
|
-
|
|
141
|
+
if (opts && opts.rowsOnly) {
|
|
142
|
+
ctx.body = rows;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
ctx.body = Object.assign({ data: rows }, extra);
|
|
146
|
+
}
|
|
174
147
|
}
|
|
175
148
|
catch (err) {
|
|
176
149
|
ctx.throw(400, err);
|
|
177
150
|
}
|
|
178
151
|
});
|
|
152
|
+
}
|
|
153
|
+
exports.executeV1 = function (ctx) {
|
|
154
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
155
|
+
return execute(ctx, { rowsOnly: true });
|
|
156
|
+
});
|
|
179
157
|
};
|
|
158
|
+
exports.executeV2 = function (ctx) {
|
|
159
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
160
|
+
return execute(ctx, { rowsOnly: false });
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
const removeDynamicVariables = (db, queryId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
164
|
+
const query = yield db.get(queryId);
|
|
165
|
+
const datasource = yield db.get(query.datasourceId);
|
|
166
|
+
const dynamicVariables = datasource.config.dynamicVariables;
|
|
167
|
+
if (dynamicVariables) {
|
|
168
|
+
// delete dynamic variables from the datasource
|
|
169
|
+
const newVariables = dynamicVariables.filter(dv => dv.queryId !== queryId);
|
|
170
|
+
datasource.config.dynamicVariables = newVariables;
|
|
171
|
+
yield db.put(datasource);
|
|
172
|
+
// invalidate the deleted variables
|
|
173
|
+
const variablesToDelete = dynamicVariables.filter(dv => dv.queryId === queryId);
|
|
174
|
+
yield invalidateDynamicVariables(variablesToDelete);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
180
177
|
exports.destroy = function (ctx) {
|
|
181
178
|
return __awaiter(this, void 0, void 0, function* () {
|
|
182
179
|
const db = new CouchDB(ctx.appId);
|
|
180
|
+
yield removeDynamicVariables(db, ctx.params.queryId);
|
|
183
181
|
yield db.remove(ctx.params.queryId, ctx.params.revId);
|
|
184
182
|
ctx.message = `Query deleted.`;
|
|
185
183
|
ctx.status = 200;
|
|
@@ -32,6 +32,7 @@ exports.generateQueryPreviewValidation = () => {
|
|
|
32
32
|
extra: Joi.object().optional(),
|
|
33
33
|
datasourceId: Joi.string().required(),
|
|
34
34
|
transformer: Joi.string().optional(),
|
|
35
|
-
parameters: Joi.object({}).required().unknown(true)
|
|
35
|
+
parameters: Joi.object({}).required().unknown(true),
|
|
36
|
+
queryId: Joi.string().optional(),
|
|
36
37
|
}));
|
|
37
38
|
};
|
package/dist/api/routes/query.js
CHANGED
|
@@ -12,6 +12,8 @@ router
|
|
|
12
12
|
.post("/api/queries/import", authorized(BUILDER), queryController.import)
|
|
13
13
|
.post("/api/queries/preview", bodyResource("datasourceId"), authorized(BUILDER), generateQueryPreviewValidation(), queryController.preview)
|
|
14
14
|
.get("/api/queries/:queryId", paramResource("queryId"), authorized(PermissionTypes.QUERY, PermissionLevels.READ), queryController.find)
|
|
15
|
-
|
|
15
|
+
// DEPRECATED - use new query endpoint for future work
|
|
16
|
+
.post("/api/queries/:queryId", paramResource("queryId"), authorized(PermissionTypes.QUERY, PermissionLevels.WRITE), queryController.executeV1)
|
|
17
|
+
.post("/api/v2/queries/:queryId", paramResource("queryId"), authorized(PermissionTypes.QUERY, PermissionLevels.WRITE), queryController.executeV2)
|
|
16
18
|
.delete("/api/queries/:queryId/:revId", paramResource("queryId"), authorized(BUILDER), queryController.destroy);
|
|
17
19
|
module.exports = router;
|
|
@@ -55,6 +55,10 @@ exports.definition = {
|
|
|
55
55
|
type: "object",
|
|
56
56
|
description: "The response from the datasource execution",
|
|
57
57
|
},
|
|
58
|
+
info: {
|
|
59
|
+
type: "object",
|
|
60
|
+
description: "Some query types may return extra data, like headers from a REST query",
|
|
61
|
+
},
|
|
58
62
|
success: {
|
|
59
63
|
type: "boolean",
|
|
60
64
|
description: "Whether the action was successful",
|
|
@@ -85,14 +89,17 @@ exports.run = function ({ inputs, appId, emitter }) {
|
|
|
85
89
|
});
|
|
86
90
|
try {
|
|
87
91
|
yield queryController.execute(ctx);
|
|
92
|
+
const _b = ctx.body, { data } = _b, rest = __rest(_b, ["data"]);
|
|
88
93
|
return {
|
|
89
|
-
response:
|
|
94
|
+
response: data,
|
|
95
|
+
info: rest,
|
|
90
96
|
success: true,
|
|
91
97
|
};
|
|
92
98
|
}
|
|
93
99
|
catch (err) {
|
|
94
100
|
return {
|
|
95
101
|
success: false,
|
|
102
|
+
info: {},
|
|
96
103
|
response: automationUtils.getError(err),
|
|
97
104
|
};
|
|
98
105
|
}
|
|
@@ -47,7 +47,7 @@ var RestModule;
|
|
|
47
47
|
const { performance } = require("perf_hooks");
|
|
48
48
|
const FormData = require("form-data");
|
|
49
49
|
const { URLSearchParams } = require("url");
|
|
50
|
-
const { parseStringPromise: xmlParser, Builder: XmlBuilder } = require("xml2js");
|
|
50
|
+
const { parseStringPromise: xmlParser, Builder: XmlBuilder, } = require("xml2js");
|
|
51
51
|
const SCHEMA = {
|
|
52
52
|
docs: "https://github.com/node-fetch/node-fetch",
|
|
53
53
|
description: "With the REST API datasource, you can connect, query and pull data from multiple REST APIs. You can then use the retrieved data to build apps.",
|
|
@@ -159,7 +159,7 @@ var RestModule;
|
|
|
159
159
|
getUrl(path, queryString) {
|
|
160
160
|
const main = `${path}?${queryString}`;
|
|
161
161
|
let complete = main;
|
|
162
|
-
if (this.config.url && !main.startsWith(
|
|
162
|
+
if (this.config.url && !main.startsWith("http")) {
|
|
163
163
|
complete = !this.config.url ? main : `${this.config.url}/${main}`;
|
|
164
164
|
}
|
|
165
165
|
if (!complete.startsWith("http")) {
|
|
@@ -202,7 +202,7 @@ var RestModule;
|
|
|
202
202
|
break;
|
|
203
203
|
case BodyTypes.XML:
|
|
204
204
|
if (object != null) {
|
|
205
|
-
string =
|
|
205
|
+
string = new XmlBuilder().buildObject(object);
|
|
206
206
|
}
|
|
207
207
|
input.body = string;
|
|
208
208
|
input.headers["Content-Type"] = "application/xml";
|
package/dist/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/server",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "1.0.27-alpha.
|
|
4
|
+
"version": "1.0.27-alpha.6",
|
|
5
5
|
"description": "Budibase Web Server",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -70,9 +70,9 @@
|
|
|
70
70
|
"license": "GPL-3.0",
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"@apidevtools/swagger-parser": "^10.0.3",
|
|
73
|
-
"@budibase/auth": "^1.0.27-alpha.
|
|
74
|
-
"@budibase/client": "^1.0.27-alpha.
|
|
75
|
-
"@budibase/string-templates": "^1.0.27-alpha.
|
|
73
|
+
"@budibase/auth": "^1.0.27-alpha.6",
|
|
74
|
+
"@budibase/client": "^1.0.27-alpha.6",
|
|
75
|
+
"@budibase/string-templates": "^1.0.27-alpha.6",
|
|
76
76
|
"@bull-board/api": "^3.7.0",
|
|
77
77
|
"@bull-board/koa": "^3.7.0",
|
|
78
78
|
"@elastic/elasticsearch": "7.10.0",
|
|
@@ -122,6 +122,7 @@
|
|
|
122
122
|
"pouchdb-replication-stream": "1.2.9",
|
|
123
123
|
"server-destroy": "1.0.1",
|
|
124
124
|
"svelte": "^3.38.2",
|
|
125
|
+
"swagger-parser": "^10.0.3",
|
|
125
126
|
"to-json-schema": "0.2.5",
|
|
126
127
|
"uuid": "3.3.2",
|
|
127
128
|
"validate.js": "0.13.1",
|
|
@@ -294,6 +294,13 @@ class TestConfiguration {
|
|
|
294
294
|
return this.datasource;
|
|
295
295
|
});
|
|
296
296
|
}
|
|
297
|
+
updateDatasource(datasource) {
|
|
298
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
299
|
+
const response = yield this._req(datasource, { datasourceId: datasource._id }, controllers.datasource.update);
|
|
300
|
+
this.datasource = response.datasource;
|
|
301
|
+
return this.datasource;
|
|
302
|
+
});
|
|
303
|
+
}
|
|
297
304
|
createQuery(config = null) {
|
|
298
305
|
return __awaiter(this, void 0, void 0, function* () {
|
|
299
306
|
if (!this.datasource && !config) {
|
package/dist/threads/query.js
CHANGED
|
@@ -8,63 +8,156 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
-
require("./utils")
|
|
11
|
+
const threadUtils = require("./utils");
|
|
12
|
+
threadUtils.threadSetup();
|
|
12
13
|
const ScriptRunner = require("../utilities/scriptRunner");
|
|
13
14
|
const { integrations } = require("../integrations");
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
const { processStringSync } = require("@budibase/string-templates");
|
|
16
|
+
const CouchDB = require("../db");
|
|
17
|
+
class QueryRunner {
|
|
18
|
+
constructor(input, flags = { noRecursiveQuery: false }) {
|
|
19
|
+
this.appId = input.appId;
|
|
20
|
+
this.datasource = input.datasource;
|
|
21
|
+
this.queryVerb = input.queryVerb;
|
|
22
|
+
this.fields = input.fields;
|
|
23
|
+
this.parameters = input.parameters;
|
|
24
|
+
this.transformer = input.transformer;
|
|
25
|
+
this.queryId = input.queryId;
|
|
26
|
+
this.noRecursiveQuery = flags.noRecursiveQuery;
|
|
27
|
+
this.cachedVariables = [];
|
|
28
|
+
// allows the response from a query to be stored throughout this
|
|
29
|
+
// execution so that if it needs to be re-used for another variable
|
|
30
|
+
// it can be
|
|
31
|
+
this.queryResponse = {};
|
|
32
|
+
this.hasRerun = false;
|
|
33
|
+
}
|
|
34
|
+
execute() {
|
|
35
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
36
|
+
let { datasource, fields, queryVerb, transformer } = this;
|
|
37
|
+
// pre-query, make sure datasource variables are added to parameters
|
|
38
|
+
const parameters = yield this.addDatasourceVariables();
|
|
39
|
+
const query = threadUtils.enrichQueryFields(fields, parameters);
|
|
40
|
+
const Integration = integrations[datasource.source];
|
|
41
|
+
if (!Integration) {
|
|
42
|
+
throw "Integration type does not exist.";
|
|
43
|
+
}
|
|
44
|
+
const integration = new Integration(datasource.config);
|
|
45
|
+
let output = threadUtils.formatResponse(yield integration[queryVerb](query));
|
|
46
|
+
let rows = output, info = undefined, extra = undefined;
|
|
47
|
+
if (threadUtils.hasExtraData(output)) {
|
|
48
|
+
rows = output.data;
|
|
49
|
+
info = output.info;
|
|
50
|
+
extra = output.extra;
|
|
51
|
+
}
|
|
52
|
+
// transform as required
|
|
53
|
+
if (transformer) {
|
|
54
|
+
const runner = new ScriptRunner(transformer, { data: rows });
|
|
55
|
+
rows = runner.execute();
|
|
56
|
+
}
|
|
57
|
+
// if the request fails we retry once, invalidating the cached value
|
|
58
|
+
if (info &&
|
|
59
|
+
info.code >= 400 &&
|
|
60
|
+
this.cachedVariables.length > 0 &&
|
|
61
|
+
!this.hasRerun) {
|
|
62
|
+
this.hasRerun = true;
|
|
63
|
+
// invalidate the cache value
|
|
64
|
+
yield threadUtils.invalidateDynamicVariables(this.cachedVariables);
|
|
65
|
+
return this.execute();
|
|
66
|
+
}
|
|
67
|
+
// needs to an array for next step
|
|
68
|
+
if (!Array.isArray(rows)) {
|
|
69
|
+
rows = [rows];
|
|
70
|
+
}
|
|
71
|
+
// map into JSON if just raw primitive here
|
|
72
|
+
if (rows.find(row => typeof row !== "object")) {
|
|
73
|
+
rows = rows.map(value => ({ value }));
|
|
74
|
+
}
|
|
75
|
+
// get all the potential fields in the schema
|
|
76
|
+
let keys = rows.flatMap(Object.keys);
|
|
77
|
+
if (integration.end) {
|
|
78
|
+
integration.end();
|
|
79
|
+
}
|
|
80
|
+
return { rows, keys, info, extra };
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
runAnotherQuery(queryId, parameters) {
|
|
84
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
85
|
+
const db = new CouchDB(this.appId);
|
|
86
|
+
const query = yield db.get(queryId);
|
|
87
|
+
const datasource = yield db.get(query.datasourceId);
|
|
88
|
+
return new QueryRunner({
|
|
89
|
+
appId: this.appId,
|
|
90
|
+
datasource,
|
|
91
|
+
queryVerb: query.queryVerb,
|
|
92
|
+
fields: query.fields,
|
|
93
|
+
parameters,
|
|
94
|
+
transformer: query.transformer,
|
|
95
|
+
}, { noRecursiveQuery: true }).execute();
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
getDynamicVariable(variable) {
|
|
99
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
100
|
+
let { parameters } = this;
|
|
101
|
+
const queryId = variable.queryId, name = variable.name;
|
|
102
|
+
let value = yield threadUtils.checkCacheForDynamicVariable(queryId, name);
|
|
103
|
+
if (!value) {
|
|
104
|
+
value = this.queryResponse[queryId]
|
|
105
|
+
? this.queryResponse[queryId]
|
|
106
|
+
: yield this.runAnotherQuery(queryId, parameters);
|
|
107
|
+
// store incase this query is to be called again
|
|
108
|
+
this.queryResponse[queryId] = value;
|
|
109
|
+
yield threadUtils.storeDynamicVariable(queryId, name, value);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.cachedVariables.push({ queryId, name });
|
|
113
|
+
}
|
|
114
|
+
return value;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
addDatasourceVariables() {
|
|
118
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
119
|
+
let { datasource, parameters, fields } = this;
|
|
120
|
+
if (!datasource || !datasource.config) {
|
|
121
|
+
return parameters;
|
|
122
|
+
}
|
|
123
|
+
const staticVars = datasource.config.staticVariables || {};
|
|
124
|
+
const dynamicVars = datasource.config.dynamicVariables || [];
|
|
125
|
+
for (let [key, value] of Object.entries(staticVars)) {
|
|
126
|
+
if (!parameters[key]) {
|
|
127
|
+
parameters[key] = value;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (!this.noRecursiveQuery) {
|
|
131
|
+
// need to see if this uses any variables
|
|
132
|
+
const stringFields = JSON.stringify(fields);
|
|
133
|
+
const foundVars = dynamicVars.filter(variable => {
|
|
134
|
+
// don't allow a query to use its own dynamic variable (loop)
|
|
135
|
+
if (variable.queryId === this.queryId) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
// look for {{ variable }} but allow spaces between handlebars
|
|
139
|
+
const regex = new RegExp(`{{[ ]*${variable.name}[ ]*}}`);
|
|
140
|
+
return regex.test(stringFields);
|
|
141
|
+
});
|
|
142
|
+
const dynamics = foundVars.map(dynVar => this.getDynamicVariable(dynVar));
|
|
143
|
+
const responses = yield Promise.all(dynamics);
|
|
144
|
+
for (let i = 0; i < foundVars.length; i++) {
|
|
145
|
+
const variable = foundVars[i];
|
|
146
|
+
parameters[variable.name] = processStringSync(variable.value, {
|
|
147
|
+
data: responses[i].rows,
|
|
148
|
+
info: responses[i].extra,
|
|
149
|
+
});
|
|
150
|
+
// make sure its known that this uses dynamic variables in case it fails
|
|
151
|
+
this.hasDynamicVariables = true;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return parameters;
|
|
155
|
+
});
|
|
22
156
|
}
|
|
23
|
-
return resp;
|
|
24
|
-
}
|
|
25
|
-
function hasExtraData(response) {
|
|
26
|
-
return (typeof response === "object" &&
|
|
27
|
-
!Array.isArray(response) &&
|
|
28
|
-
response.data != null &&
|
|
29
|
-
response.info != null);
|
|
30
|
-
}
|
|
31
|
-
function runAndTransform(datasource, queryVerb, query, transformer) {
|
|
32
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
33
|
-
const Integration = integrations[datasource.source];
|
|
34
|
-
if (!Integration) {
|
|
35
|
-
throw "Integration type does not exist.";
|
|
36
|
-
}
|
|
37
|
-
const integration = new Integration(datasource.config);
|
|
38
|
-
let output = formatResponse(yield integration[queryVerb](query));
|
|
39
|
-
let rows = output, info = undefined, extra = undefined;
|
|
40
|
-
if (hasExtraData(output)) {
|
|
41
|
-
rows = output.data;
|
|
42
|
-
info = output.info;
|
|
43
|
-
extra = output.extra;
|
|
44
|
-
}
|
|
45
|
-
// transform as required
|
|
46
|
-
if (transformer) {
|
|
47
|
-
const runner = new ScriptRunner(transformer, { data: rows });
|
|
48
|
-
rows = runner.execute();
|
|
49
|
-
}
|
|
50
|
-
// needs to an array for next step
|
|
51
|
-
if (!Array.isArray(rows)) {
|
|
52
|
-
rows = [rows];
|
|
53
|
-
}
|
|
54
|
-
// map into JSON if just raw primitive here
|
|
55
|
-
if (rows.find(row => typeof row !== "object")) {
|
|
56
|
-
rows = rows.map(value => ({ value }));
|
|
57
|
-
}
|
|
58
|
-
// get all the potential fields in the schema
|
|
59
|
-
let keys = rows.flatMap(Object.keys);
|
|
60
|
-
if (integration.end) {
|
|
61
|
-
integration.end();
|
|
62
|
-
}
|
|
63
|
-
return { rows, keys, info, extra };
|
|
64
|
-
});
|
|
65
157
|
}
|
|
66
158
|
module.exports = (input, callback) => {
|
|
67
|
-
|
|
159
|
+
const Runner = new QueryRunner(input);
|
|
160
|
+
Runner.execute()
|
|
68
161
|
.then(response => {
|
|
69
162
|
callback(null, response);
|
|
70
163
|
})
|