@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
package/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.7",
|
|
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.7",
|
|
74
|
+
"@budibase/client": "^1.0.27-alpha.7",
|
|
75
|
+
"@budibase/string-templates": "^1.0.27-alpha.7",
|
|
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",
|
|
@@ -164,5 +165,5 @@
|
|
|
164
165
|
"optionalDependencies": {
|
|
165
166
|
"oracledb": "^5.3.0"
|
|
166
167
|
},
|
|
167
|
-
"gitHead": "
|
|
168
|
+
"gitHead": "495399ad0a598dfae8355b03e012c3db27a7a548"
|
|
168
169
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Query, QueryParameter } from "../../../../../../definitions/datasource"
|
|
2
|
+
import { URL } from "url"
|
|
2
3
|
|
|
3
4
|
export interface ImportInfo {
|
|
4
|
-
url: string
|
|
5
5
|
name: string
|
|
6
6
|
}
|
|
7
7
|
|
|
@@ -23,6 +23,7 @@ export abstract class ImportSource {
|
|
|
23
23
|
name: string,
|
|
24
24
|
method: string,
|
|
25
25
|
path: string,
|
|
26
|
+
url: URL,
|
|
26
27
|
queryString: string,
|
|
27
28
|
headers: object = {},
|
|
28
29
|
parameters: QueryParameter[] = [],
|
|
@@ -33,6 +34,7 @@ export abstract class ImportSource {
|
|
|
33
34
|
const transformer = "return data"
|
|
34
35
|
const schema = {}
|
|
35
36
|
path = this.processPath(path)
|
|
37
|
+
path = `${url.origin}/${path}`
|
|
36
38
|
queryString = this.processQuery(queryString)
|
|
37
39
|
const requestBody = JSON.stringify(body, null, 2)
|
|
38
40
|
|
|
@@ -17,8 +17,12 @@ const parseBody = (curl: any) => {
|
|
|
17
17
|
if (curl.data) {
|
|
18
18
|
const keys = Object.keys(curl.data)
|
|
19
19
|
if (keys.length) {
|
|
20
|
-
|
|
20
|
+
let key = keys[0]
|
|
21
21
|
try {
|
|
22
|
+
// filter out the dollar syntax used by curl for shell support
|
|
23
|
+
if (key.startsWith("$")) {
|
|
24
|
+
key = key.substring(1)
|
|
25
|
+
}
|
|
22
26
|
return JSON.parse(key)
|
|
23
27
|
} catch (e) {
|
|
24
28
|
// do nothing
|
|
@@ -56,16 +60,19 @@ export class Curl extends ImportSource {
|
|
|
56
60
|
return true
|
|
57
61
|
}
|
|
58
62
|
|
|
63
|
+
getUrl = (): URL => {
|
|
64
|
+
return new URL(this.curl.raw_url)
|
|
65
|
+
}
|
|
66
|
+
|
|
59
67
|
getInfo = async (): Promise<ImportInfo> => {
|
|
60
|
-
const url =
|
|
68
|
+
const url = this.getUrl()
|
|
61
69
|
return {
|
|
62
|
-
url: url.origin,
|
|
63
70
|
name: url.hostname,
|
|
64
71
|
}
|
|
65
72
|
}
|
|
66
73
|
|
|
67
74
|
getQueries = async (datasourceId: string): Promise<Query[]> => {
|
|
68
|
-
const url =
|
|
75
|
+
const url = this.getUrl()
|
|
69
76
|
const name = url.pathname
|
|
70
77
|
const path = url.pathname
|
|
71
78
|
const method = this.curl.method
|
|
@@ -83,6 +90,7 @@ export class Curl extends ImportSource {
|
|
|
83
90
|
name,
|
|
84
91
|
method,
|
|
85
92
|
path,
|
|
93
|
+
url,
|
|
86
94
|
queryString,
|
|
87
95
|
headers,
|
|
88
96
|
[],
|
|
@@ -2,6 +2,7 @@ import { ImportInfo } from "./base"
|
|
|
2
2
|
import { Query, QueryParameter } from "../../../../../definitions/datasource"
|
|
3
3
|
import { OpenAPIV2 } from "openapi-types"
|
|
4
4
|
import { OpenAPISource } from "./base/openapi"
|
|
5
|
+
import { URL } from "url"
|
|
5
6
|
|
|
6
7
|
const parameterNotRef = (
|
|
7
8
|
param: OpenAPIV2.Parameter | OpenAPIV2.ReferenceObject
|
|
@@ -55,20 +56,22 @@ export class OpenAPI2 extends OpenAPISource {
|
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
getUrl = (): URL => {
|
|
59
60
|
const scheme = this.document.schemes?.includes("https") ? "https" : "http"
|
|
60
61
|
const basePath = this.document.basePath || ""
|
|
61
62
|
const host = this.document.host || "<host>"
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
return new URL(`${scheme}://${host}${basePath}`)
|
|
64
|
+
}
|
|
64
65
|
|
|
66
|
+
getInfo = async (): Promise<ImportInfo> => {
|
|
67
|
+
const name = this.document.info.title || "Swagger Import"
|
|
65
68
|
return {
|
|
66
|
-
|
|
67
|
-
name: name,
|
|
69
|
+
name,
|
|
68
70
|
}
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
getQueries = async (datasourceId: string): Promise<Query[]> => {
|
|
74
|
+
const url = this.getUrl()
|
|
72
75
|
const queries = []
|
|
73
76
|
|
|
74
77
|
for (let [path, pathItem] of Object.entries(this.document.paths)) {
|
|
@@ -145,6 +148,7 @@ export class OpenAPI2 extends OpenAPISource {
|
|
|
145
148
|
name,
|
|
146
149
|
methodName,
|
|
147
150
|
path,
|
|
151
|
+
url,
|
|
148
152
|
queryString,
|
|
149
153
|
headers,
|
|
150
154
|
parameters,
|
|
@@ -35,7 +35,6 @@ describe("Curl Import", () => {
|
|
|
35
35
|
it("returns import info", async () => {
|
|
36
36
|
await init("get")
|
|
37
37
|
const info = await curl.getInfo()
|
|
38
|
-
expect(info.url).toBe("http://example.com")
|
|
39
38
|
expect(info.name).toBe("example.com")
|
|
40
39
|
})
|
|
41
40
|
|
|
@@ -67,8 +66,8 @@ describe("Curl Import", () => {
|
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
it("populates path", async () => {
|
|
70
|
-
await testPath("get", "")
|
|
71
|
-
await testPath("path", "paths/abc")
|
|
69
|
+
await testPath("get", "http://example.com/")
|
|
70
|
+
await testPath("path", "http://example.com/paths/abc")
|
|
72
71
|
})
|
|
73
72
|
|
|
74
73
|
const testHeaders = async (file, headers) => {
|
|
@@ -41,7 +41,6 @@ describe("OpenAPI2 Import", () => {
|
|
|
41
41
|
const testImportInfo = async (file, extension) => {
|
|
42
42
|
await init(file, extension)
|
|
43
43
|
const info = await openapi2.getInfo()
|
|
44
|
-
expect(info.url).toBe("https://petstore.swagger.io/v2")
|
|
45
44
|
expect(info.name).toBe("Swagger Petstore")
|
|
46
45
|
}
|
|
47
46
|
|
|
@@ -92,12 +91,12 @@ describe("OpenAPI2 Import", () => {
|
|
|
92
91
|
|
|
93
92
|
it("populates path", async () => {
|
|
94
93
|
const assertions = {
|
|
95
|
-
"createEntity" : "entities",
|
|
96
|
-
"getEntities" : "entities",
|
|
97
|
-
"getEntity" : "entities/{{entityId}}",
|
|
98
|
-
"updateEntity" : "entities/{{entityId}}",
|
|
99
|
-
"patchEntity" : "entities/{{entityId}}",
|
|
100
|
-
"deleteEntity" : "entities/{{entityId}}"
|
|
94
|
+
"createEntity" : "http://example.com/entities",
|
|
95
|
+
"getEntities" : "http://example.com/entities",
|
|
96
|
+
"getEntity" : "http://example.com/entities/{{entityId}}",
|
|
97
|
+
"updateEntity" : "http://example.com/entities/{{entityId}}",
|
|
98
|
+
"patchEntity" : "http://example.com/entities/{{entityId}}",
|
|
99
|
+
"deleteEntity" : "http://example.com/entities/{{entityId}}"
|
|
101
100
|
}
|
|
102
101
|
await runTests("crud", testPath, assertions)
|
|
103
102
|
})
|
|
@@ -51,30 +51,24 @@ describe("Rest Importer", () => {
|
|
|
51
51
|
await init(data)
|
|
52
52
|
const info = await restImporter.getInfo()
|
|
53
53
|
expect(info.name).toBe(assertions[key].name)
|
|
54
|
-
expect(info.url).toBe(assertions[key].url)
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
it("gets info", async () => {
|
|
58
57
|
const assertions = {
|
|
59
58
|
"oapi2CrudJson" : {
|
|
60
59
|
name: "CRUD",
|
|
61
|
-
url: "http://example.com"
|
|
62
60
|
},
|
|
63
61
|
"oapi2CrudYaml" : {
|
|
64
62
|
name: "CRUD",
|
|
65
|
-
url: "http://example.com"
|
|
66
63
|
},
|
|
67
64
|
"oapi2PetstoreJson" : {
|
|
68
65
|
name: "Swagger Petstore",
|
|
69
|
-
url: "https://petstore.swagger.io/v2"
|
|
70
66
|
},
|
|
71
67
|
"oapi2PetstoreYaml" :{
|
|
72
68
|
name: "Swagger Petstore",
|
|
73
|
-
url: "https://petstore.swagger.io/v2"
|
|
74
69
|
},
|
|
75
70
|
"curl": {
|
|
76
71
|
name: "example.com",
|
|
77
|
-
url: "http://example.com"
|
|
78
72
|
}
|
|
79
73
|
}
|
|
80
74
|
await runTest(testGetInfo, assertions)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const { processString } = require("@budibase/string-templates")
|
|
2
1
|
const CouchDB = require("../../../db")
|
|
3
2
|
const {
|
|
4
3
|
generateQueryID,
|
|
@@ -9,6 +8,7 @@ const { BaseQueryVerbs } = require("../../../constants")
|
|
|
9
8
|
const { Thread, ThreadType } = require("../../../threads")
|
|
10
9
|
const { save: saveDatasource } = require("../datasource")
|
|
11
10
|
const { RestImporter } = require("./import")
|
|
11
|
+
const { invalidateDynamicVariables } = require("../../../threads/utils")
|
|
12
12
|
|
|
13
13
|
const Runner = new Thread(ThreadType.QUERY, { timeoutMs: 10000 })
|
|
14
14
|
|
|
@@ -90,47 +90,6 @@ exports.save = async function (ctx) {
|
|
|
90
90
|
ctx.message = `Query ${query.name} saved successfully.`
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
async function enrichQueryFields(fields, parameters = {}) {
|
|
94
|
-
const enrichedQuery = {}
|
|
95
|
-
|
|
96
|
-
// enrich the fields with dynamic parameters
|
|
97
|
-
for (let key of Object.keys(fields)) {
|
|
98
|
-
if (fields[key] == null) {
|
|
99
|
-
continue
|
|
100
|
-
}
|
|
101
|
-
if (typeof fields[key] === "object") {
|
|
102
|
-
// enrich nested fields object
|
|
103
|
-
enrichedQuery[key] = await enrichQueryFields(fields[key], parameters)
|
|
104
|
-
} else if (typeof fields[key] === "string") {
|
|
105
|
-
// enrich string value as normal
|
|
106
|
-
enrichedQuery[key] = await processString(fields[key], parameters, {
|
|
107
|
-
noHelpers: true,
|
|
108
|
-
})
|
|
109
|
-
} else {
|
|
110
|
-
enrichedQuery[key] = fields[key]
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (
|
|
115
|
-
enrichedQuery.json ||
|
|
116
|
-
enrichedQuery.customData ||
|
|
117
|
-
enrichedQuery.requestBody
|
|
118
|
-
) {
|
|
119
|
-
try {
|
|
120
|
-
enrichedQuery.json = JSON.parse(
|
|
121
|
-
enrichedQuery.json ||
|
|
122
|
-
enrichedQuery.customData ||
|
|
123
|
-
enrichedQuery.requestBody
|
|
124
|
-
)
|
|
125
|
-
} catch (err) {
|
|
126
|
-
// no json found, ignore
|
|
127
|
-
}
|
|
128
|
-
delete enrichedQuery.customData
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return enrichedQuery
|
|
132
|
-
}
|
|
133
|
-
|
|
134
93
|
exports.find = async function (ctx) {
|
|
135
94
|
const db = new CouchDB(ctx.appId)
|
|
136
95
|
const query = enrichQueries(await db.get(ctx.params.queryId))
|
|
@@ -146,16 +105,20 @@ exports.preview = async function (ctx) {
|
|
|
146
105
|
const db = new CouchDB(ctx.appId)
|
|
147
106
|
|
|
148
107
|
const datasource = await db.get(ctx.request.body.datasourceId)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const
|
|
108
|
+
// preview may not have a queryId as it hasn't been saved, but if it does
|
|
109
|
+
// this stops dynamic variables from calling the same query
|
|
110
|
+
const { fields, parameters, queryVerb, transformer, queryId } =
|
|
111
|
+
ctx.request.body
|
|
152
112
|
|
|
153
113
|
try {
|
|
154
114
|
const { rows, keys, info, extra } = await Runner.run({
|
|
115
|
+
appId: ctx.appId,
|
|
155
116
|
datasource,
|
|
156
117
|
queryVerb,
|
|
157
|
-
|
|
118
|
+
fields,
|
|
119
|
+
parameters,
|
|
158
120
|
transformer,
|
|
121
|
+
queryId,
|
|
159
122
|
})
|
|
160
123
|
|
|
161
124
|
ctx.body = {
|
|
@@ -169,33 +132,63 @@ exports.preview = async function (ctx) {
|
|
|
169
132
|
}
|
|
170
133
|
}
|
|
171
134
|
|
|
172
|
-
|
|
135
|
+
async function execute(ctx, opts = { rowsOnly: false }) {
|
|
173
136
|
const db = new CouchDB(ctx.appId)
|
|
174
137
|
|
|
175
138
|
const query = await db.get(ctx.params.queryId)
|
|
176
139
|
const datasource = await db.get(query.datasourceId)
|
|
177
140
|
|
|
178
|
-
const enrichedQuery = await enrichQueryFields(
|
|
179
|
-
query.fields,
|
|
180
|
-
ctx.request.body.parameters
|
|
181
|
-
)
|
|
182
|
-
|
|
183
141
|
// call the relevant CRUD method on the integration class
|
|
184
142
|
try {
|
|
185
|
-
const { rows } = await Runner.run({
|
|
143
|
+
const { rows, extra } = await Runner.run({
|
|
144
|
+
appId: ctx.appId,
|
|
186
145
|
datasource,
|
|
187
146
|
queryVerb: query.queryVerb,
|
|
188
|
-
|
|
147
|
+
fields: query.fields,
|
|
148
|
+
parameters: ctx.request.body.parameters,
|
|
189
149
|
transformer: query.transformer,
|
|
150
|
+
queryId: ctx.params.queryId,
|
|
190
151
|
})
|
|
191
|
-
|
|
152
|
+
if (opts && opts.rowsOnly) {
|
|
153
|
+
ctx.body = rows
|
|
154
|
+
} else {
|
|
155
|
+
ctx.body = { data: rows, ...extra }
|
|
156
|
+
}
|
|
192
157
|
} catch (err) {
|
|
193
158
|
ctx.throw(400, err)
|
|
194
159
|
}
|
|
195
160
|
}
|
|
196
161
|
|
|
162
|
+
exports.executeV1 = async function (ctx) {
|
|
163
|
+
return execute(ctx, { rowsOnly: true })
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
exports.executeV2 = async function (ctx) {
|
|
167
|
+
return execute(ctx, { rowsOnly: false })
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const removeDynamicVariables = async (db, queryId) => {
|
|
171
|
+
const query = await db.get(queryId)
|
|
172
|
+
const datasource = await db.get(query.datasourceId)
|
|
173
|
+
const dynamicVariables = datasource.config.dynamicVariables
|
|
174
|
+
|
|
175
|
+
if (dynamicVariables) {
|
|
176
|
+
// delete dynamic variables from the datasource
|
|
177
|
+
const newVariables = dynamicVariables.filter(dv => dv.queryId !== queryId)
|
|
178
|
+
datasource.config.dynamicVariables = newVariables
|
|
179
|
+
await db.put(datasource)
|
|
180
|
+
|
|
181
|
+
// invalidate the deleted variables
|
|
182
|
+
const variablesToDelete = dynamicVariables.filter(
|
|
183
|
+
dv => dv.queryId === queryId
|
|
184
|
+
)
|
|
185
|
+
await invalidateDynamicVariables(variablesToDelete)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
197
189
|
exports.destroy = async function (ctx) {
|
|
198
190
|
const db = new CouchDB(ctx.appId)
|
|
191
|
+
await removeDynamicVariables(db, ctx.params.queryId)
|
|
199
192
|
await db.remove(ctx.params.queryId, ctx.params.revId)
|
|
200
193
|
ctx.message = `Query deleted.`
|
|
201
194
|
ctx.status = 200
|
|
@@ -36,6 +36,7 @@ exports.generateQueryPreviewValidation = () => {
|
|
|
36
36
|
extra: Joi.object().optional(),
|
|
37
37
|
datasourceId: Joi.string().required(),
|
|
38
38
|
transformer: Joi.string().optional(),
|
|
39
|
-
parameters: Joi.object({}).required().unknown(true)
|
|
39
|
+
parameters: Joi.object({}).required().unknown(true),
|
|
40
|
+
queryId: Joi.string().optional(),
|
|
40
41
|
}))
|
|
41
42
|
}
|
package/src/api/routes/query.js
CHANGED
|
@@ -41,11 +41,18 @@ router
|
|
|
41
41
|
authorized(PermissionTypes.QUERY, PermissionLevels.READ),
|
|
42
42
|
queryController.find
|
|
43
43
|
)
|
|
44
|
+
// DEPRECATED - use new query endpoint for future work
|
|
44
45
|
.post(
|
|
45
46
|
"/api/queries/:queryId",
|
|
46
47
|
paramResource("queryId"),
|
|
47
48
|
authorized(PermissionTypes.QUERY, PermissionLevels.WRITE),
|
|
48
|
-
queryController.
|
|
49
|
+
queryController.executeV1
|
|
50
|
+
)
|
|
51
|
+
.post(
|
|
52
|
+
"/api/v2/queries/:queryId",
|
|
53
|
+
paramResource("queryId"),
|
|
54
|
+
authorized(PermissionTypes.QUERY, PermissionLevels.WRITE),
|
|
55
|
+
queryController.executeV2
|
|
49
56
|
)
|
|
50
57
|
.delete(
|
|
51
58
|
"/api/queries/:queryId/:revId",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Mock out postgres for this
|
|
2
2
|
jest.mock("pg")
|
|
3
|
+
jest.mock("node-fetch")
|
|
3
4
|
|
|
4
5
|
// Mock isProdAppID to we can later mock the implementation and pretend we are
|
|
5
6
|
// using prod app IDs
|
|
@@ -10,6 +11,7 @@ authDb.isProdAppID = mockIsProdAppID
|
|
|
10
11
|
|
|
11
12
|
const setup = require("./utilities")
|
|
12
13
|
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
|
14
|
+
const { checkCacheForDynamicVariable } = require("../../../threads/utils")
|
|
13
15
|
const { basicQuery, basicDatasource } = setup.structures
|
|
14
16
|
|
|
15
17
|
describe("/queries", () => {
|
|
@@ -226,4 +228,90 @@ describe("/queries", () => {
|
|
|
226
228
|
.expect(400)
|
|
227
229
|
})
|
|
228
230
|
})
|
|
231
|
+
|
|
232
|
+
describe("test variables", () => {
|
|
233
|
+
async function restDatasource(cfg) {
|
|
234
|
+
return await config.createDatasource({
|
|
235
|
+
datasource: {
|
|
236
|
+
...basicDatasource().datasource,
|
|
237
|
+
source: "REST",
|
|
238
|
+
config: cfg || {},
|
|
239
|
+
},
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function dynamicVariableDatasource() {
|
|
244
|
+
const datasource = await restDatasource()
|
|
245
|
+
const basedOnQuery = await config.createQuery({
|
|
246
|
+
...basicQuery(datasource._id),
|
|
247
|
+
fields: {
|
|
248
|
+
path: "www.google.com",
|
|
249
|
+
},
|
|
250
|
+
})
|
|
251
|
+
await config.updateDatasource({
|
|
252
|
+
...datasource,
|
|
253
|
+
config: {
|
|
254
|
+
dynamicVariables: [
|
|
255
|
+
{ queryId: basedOnQuery._id, name: "variable3", value: "{{ data.0.[value] }}" }
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
})
|
|
259
|
+
return { datasource, query: basedOnQuery }
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async function preview(datasource, fields) {
|
|
263
|
+
return await request
|
|
264
|
+
.post(`/api/queries/preview`)
|
|
265
|
+
.send({
|
|
266
|
+
datasourceId: datasource._id,
|
|
267
|
+
parameters: {},
|
|
268
|
+
fields,
|
|
269
|
+
queryVerb: "read",
|
|
270
|
+
})
|
|
271
|
+
.set(config.defaultHeaders())
|
|
272
|
+
.expect("Content-Type", /json/)
|
|
273
|
+
.expect(200)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
it("should work with static variables", async () => {
|
|
277
|
+
const datasource = await restDatasource({
|
|
278
|
+
staticVariables: {
|
|
279
|
+
variable: "google",
|
|
280
|
+
variable2: "1",
|
|
281
|
+
},
|
|
282
|
+
})
|
|
283
|
+
const res = await preview(datasource, {
|
|
284
|
+
path: "www.{{ variable }}.com",
|
|
285
|
+
queryString: "test={{ variable2 }}",
|
|
286
|
+
})
|
|
287
|
+
// these responses come from the mock
|
|
288
|
+
expect(res.body.schemaFields).toEqual(["url", "opts", "value"])
|
|
289
|
+
expect(res.body.rows[0].url).toEqual("http://www.google.com?test=1")
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it("should work with dynamic variables", async () => {
|
|
293
|
+
const { datasource } = await dynamicVariableDatasource()
|
|
294
|
+
const res = await preview(datasource, {
|
|
295
|
+
path: "www.google.com",
|
|
296
|
+
queryString: "test={{ variable3 }}",
|
|
297
|
+
})
|
|
298
|
+
expect(res.body.schemaFields).toEqual(["url", "opts", "value"])
|
|
299
|
+
expect(res.body.rows[0].url).toContain("doctype html")
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
it("check that it automatically retries on fail with cached dynamics", async () => {
|
|
303
|
+
const { datasource, query: base } = await dynamicVariableDatasource()
|
|
304
|
+
// preview once to cache
|
|
305
|
+
await preview(datasource, { path: "www.google.com", queryString: "test={{ variable3 }}" })
|
|
306
|
+
// check its in cache
|
|
307
|
+
const contents = await checkCacheForDynamicVariable(base._id, "variable3")
|
|
308
|
+
expect(contents.rows.length).toEqual(1)
|
|
309
|
+
const res = await preview(datasource, {
|
|
310
|
+
path: "www.failonce.com",
|
|
311
|
+
queryString: "test={{ variable3 }}",
|
|
312
|
+
})
|
|
313
|
+
expect(res.body.schemaFields).toEqual(["fails", "url", "opts"])
|
|
314
|
+
expect(res.body.rows[0].fails).toEqual(1)
|
|
315
|
+
})
|
|
316
|
+
})
|
|
229
317
|
})
|
|
@@ -35,6 +35,11 @@ exports.definition = {
|
|
|
35
35
|
type: "object",
|
|
36
36
|
description: "The response from the datasource execution",
|
|
37
37
|
},
|
|
38
|
+
info: {
|
|
39
|
+
type: "object",
|
|
40
|
+
description:
|
|
41
|
+
"Some query types may return extra data, like headers from a REST query",
|
|
42
|
+
},
|
|
38
43
|
success: {
|
|
39
44
|
type: "boolean",
|
|
40
45
|
description: "Whether the action was successful",
|
|
@@ -68,13 +73,16 @@ exports.run = async function ({ inputs, appId, emitter }) {
|
|
|
68
73
|
|
|
69
74
|
try {
|
|
70
75
|
await queryController.execute(ctx)
|
|
76
|
+
const { data, ...rest } = ctx.body
|
|
71
77
|
return {
|
|
72
|
-
response:
|
|
78
|
+
response: data,
|
|
79
|
+
info: rest,
|
|
73
80
|
success: true,
|
|
74
81
|
}
|
|
75
82
|
} catch (err) {
|
|
76
83
|
return {
|
|
77
84
|
success: false,
|
|
85
|
+
info: {},
|
|
78
86
|
response: automationUtils.getError(err),
|
|
79
87
|
}
|
|
80
88
|
}
|
|
@@ -240,6 +240,16 @@ export interface RestConfig {
|
|
|
240
240
|
[key: string]: any
|
|
241
241
|
}
|
|
242
242
|
authConfigs: AuthConfig[]
|
|
243
|
+
staticVariables: {
|
|
244
|
+
[key: string]: string
|
|
245
|
+
}
|
|
246
|
+
dynamicVariables: [
|
|
247
|
+
{
|
|
248
|
+
name: string
|
|
249
|
+
queryId: string
|
|
250
|
+
value: string
|
|
251
|
+
}
|
|
252
|
+
]
|
|
243
253
|
}
|
|
244
254
|
|
|
245
255
|
export interface Query {
|
package/src/integrations/rest.ts
CHANGED
|
@@ -48,7 +48,10 @@ module RestModule {
|
|
|
48
48
|
const { performance } = require("perf_hooks")
|
|
49
49
|
const FormData = require("form-data")
|
|
50
50
|
const { URLSearchParams } = require("url")
|
|
51
|
-
const {
|
|
51
|
+
const {
|
|
52
|
+
parseStringPromise: xmlParser,
|
|
53
|
+
Builder: XmlBuilder,
|
|
54
|
+
} = require("xml2js")
|
|
52
55
|
|
|
53
56
|
const SCHEMA: Integration = {
|
|
54
57
|
docs: "https://github.com/node-fetch/node-fetch",
|
|
@@ -168,7 +171,7 @@ module RestModule {
|
|
|
168
171
|
getUrl(path: string, queryString: string): string {
|
|
169
172
|
const main = `${path}?${queryString}`
|
|
170
173
|
let complete = main
|
|
171
|
-
if (this.config.url && !main.startsWith(
|
|
174
|
+
if (this.config.url && !main.startsWith("http")) {
|
|
172
175
|
complete = !this.config.url ? main : `${this.config.url}/${main}`
|
|
173
176
|
}
|
|
174
177
|
if (!complete.startsWith("http")) {
|
|
@@ -211,7 +214,7 @@ module RestModule {
|
|
|
211
214
|
break
|
|
212
215
|
case BodyTypes.XML:
|
|
213
216
|
if (object != null) {
|
|
214
|
-
string =
|
|
217
|
+
string = new XmlBuilder().buildObject(object)
|
|
215
218
|
}
|
|
216
219
|
input.body = string
|
|
217
220
|
input.headers["Content-Type"] = "application/xml"
|
|
@@ -316,6 +316,16 @@ class TestConfiguration {
|
|
|
316
316
|
return this.datasource
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
+
async updateDatasource(datasource) {
|
|
320
|
+
const response = await this._req(
|
|
321
|
+
datasource,
|
|
322
|
+
{ datasourceId: datasource._id },
|
|
323
|
+
controllers.datasource.update
|
|
324
|
+
)
|
|
325
|
+
this.datasource = response.datasource
|
|
326
|
+
return this.datasource
|
|
327
|
+
}
|
|
328
|
+
|
|
319
329
|
async createQuery(config = null) {
|
|
320
330
|
if (!this.datasource && !config) {
|
|
321
331
|
throw "No data source created for query."
|