@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.
Files changed (199) hide show
  1. package/__mocks__/node-fetch.ts +18 -0
  2. package/builder/assets/{index.2dc0d283.js → index.02c16fdb.js} +245 -240
  3. package/builder/assets/{index.5522fcd5.css → index.0dce895b.css} +1 -1
  4. package/builder/index.html +2 -2
  5. package/coverage/clover.xml +423 -319
  6. package/coverage/coverage-final.json +22 -22
  7. package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
  8. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  9. package/coverage/lcov-report/api/controllers/application.js.html +54 -54
  10. package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
  11. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  12. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/component.js.html +1 -1
  15. package/coverage/lcov-report/api/controllers/datasource.js.html +49 -49
  16. package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/hosting.js.html +1 -1
  18. package/coverage/lcov-report/api/controllers/index.html +20 -20
  19. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
  23. package/coverage/lcov-report/api/controllers/query/index.html +19 -19
  24. package/coverage/lcov-report/api/controllers/query/index.js.html +100 -121
  25. package/coverage/lcov-report/api/controllers/query/validation.js.html +6 -3
  26. package/coverage/lcov-report/api/controllers/role.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
  29. package/coverage/lcov-report/api/controllers/row/index.html +1 -1
  30. package/coverage/lcov-report/api/controllers/row/index.js.html +1 -1
  31. package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
  32. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
  33. package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
  34. package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
  35. package/coverage/lcov-report/api/controllers/script.js.html +1 -1
  36. package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
  37. package/coverage/lcov-report/api/controllers/table/index.html +1 -1
  38. package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
  39. package/coverage/lcov-report/api/controllers/table/internal.js.html +1 -1
  40. package/coverage/lcov-report/api/controllers/table/utils.js.html +1 -1
  41. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  42. package/coverage/lcov-report/api/controllers/user.js.html +1 -1
  43. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  44. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  45. package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
  46. package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
  47. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
  48. package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
  49. package/coverage/lcov-report/api/index.html +1 -1
  50. package/coverage/lcov-report/api/index.js.html +5 -5
  51. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/index.html +1 -1
  64. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/query.js.html +24 -3
  70. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/static.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  81. package/coverage/lcov-report/automations/actions.js.html +1 -1
  82. package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
  83. package/coverage/lcov-report/automations/bullboard.js.html +1 -1
  84. package/coverage/lcov-report/automations/index.html +1 -1
  85. package/coverage/lcov-report/automations/index.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  87. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  89. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/executeQuery.js.html +31 -7
  92. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/index.html +11 -11
  95. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  96. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
  98. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  99. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  100. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  101. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  102. package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
  103. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  104. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  105. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  106. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  107. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  108. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  109. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  110. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  111. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  112. package/coverage/lcov-report/automations/triggers.js.html +1 -1
  113. package/coverage/lcov-report/automations/utils.js.html +5 -5
  114. package/coverage/lcov-report/constants/index.html +1 -1
  115. package/coverage/lcov-report/constants/index.js.html +1 -1
  116. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  117. package/coverage/lcov-report/constants/screens.js.html +1 -1
  118. package/coverage/lcov-report/db/client.js.html +1 -1
  119. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  120. package/coverage/lcov-report/db/index.html +1 -1
  121. package/coverage/lcov-report/db/index.js.html +1 -1
  122. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
  123. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  124. package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
  125. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  126. package/coverage/lcov-report/db/newid.js.html +2 -2
  127. package/coverage/lcov-report/db/utils.js.html +18 -18
  128. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  129. package/coverage/lcov-report/events/index.html +1 -1
  130. package/coverage/lcov-report/events/index.js.html +1 -1
  131. package/coverage/lcov-report/events/utils.js.html +1 -1
  132. package/coverage/lcov-report/index.html +46 -46
  133. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  134. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  135. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  136. package/coverage/lcov-report/middleware/authorized.js.html +10 -10
  137. package/coverage/lcov-report/middleware/builder.js.html +8 -8
  138. package/coverage/lcov-report/middleware/currentapp.js.html +28 -28
  139. package/coverage/lcov-report/middleware/index.html +1 -1
  140. package/coverage/lcov-report/middleware/joi-validator.js.html +9 -9
  141. package/coverage/lcov-report/middleware/resourceId.js.html +17 -17
  142. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  143. package/coverage/lcov-report/middleware/usageQuota.js.html +1 -1
  144. package/coverage/lcov-report/middleware/utils.js.html +2 -2
  145. package/coverage/lcov-report/threads/automation.js.html +1 -1
  146. package/coverage/lcov-report/threads/index.html +31 -31
  147. package/coverage/lcov-report/threads/index.js.html +11 -11
  148. package/coverage/lcov-report/threads/query.js.html +358 -91
  149. package/coverage/lcov-report/threads/utils.js.html +356 -11
  150. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  151. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  152. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  153. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  154. package/coverage/lcov-report/utilities/global.js.html +13 -13
  155. package/coverage/lcov-report/utilities/index.html +1 -1
  156. package/coverage/lcov-report/utilities/index.js.html +9 -9
  157. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +2 -2
  158. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  159. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  160. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  161. package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
  162. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
  163. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
  164. package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
  165. package/coverage/lcov-report/utilities/security.js.html +1 -1
  166. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  167. package/coverage/lcov-report/utilities/users.js.html +1 -1
  168. package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
  169. package/coverage/lcov.info +723 -545
  170. package/dist/api/controllers/query/import/sources/base/index.js +2 -1
  171. package/dist/api/controllers/query/import/sources/curl.js +11 -5
  172. package/dist/api/controllers/query/import/sources/openapi2.js +8 -5
  173. package/dist/api/controllers/query/index.js +45 -47
  174. package/dist/api/controllers/query/validation.js +2 -1
  175. package/dist/api/routes/query.js +3 -1
  176. package/dist/automations/steps/executeQuery.js +8 -1
  177. package/dist/integrations/rest.js +3 -3
  178. package/dist/package.json +5 -4
  179. package/dist/tests/utilities/TestConfiguration.js +7 -0
  180. package/dist/threads/query.js +145 -52
  181. package/dist/threads/utils.js +105 -0
  182. package/dist/tsconfig.tsbuildinfo +1 -1
  183. package/package.json +6 -5
  184. package/src/api/controllers/query/import/sources/base/index.ts +3 -1
  185. package/src/api/controllers/query/import/sources/curl.ts +12 -4
  186. package/src/api/controllers/query/import/sources/openapi2.ts +9 -5
  187. package/src/api/controllers/query/import/sources/tests/curl/curl.spec.js +2 -3
  188. package/src/api/controllers/query/import/sources/tests/openapi2/openapi2.spec.js +6 -7
  189. package/src/api/controllers/query/import/tests/index.spec.js +0 -6
  190. package/src/api/controllers/query/index.js +48 -55
  191. package/src/api/controllers/query/validation.js +2 -1
  192. package/src/api/routes/query.js +8 -1
  193. package/src/api/routes/tests/query.spec.js +88 -0
  194. package/src/automations/steps/executeQuery.js +9 -1
  195. package/src/definitions/datasource.ts +10 -0
  196. package/src/integrations/rest.ts +6 -3
  197. package/src/tests/utilities/TestConfiguration.js +10 -0
  198. package/src/threads/query.js +145 -56
  199. 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.3",
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.3",
74
- "@budibase/client": "^1.0.27-alpha.3",
75
- "@budibase/string-templates": "^1.0.27-alpha.3",
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": "7bf288606c6c895ea86c02dbaa82bd54e7a23025"
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
- const key = keys[0]
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 = new URL(this.curl.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 = new URL(this.curl.raw_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
- getInfo = async (): Promise<ImportInfo> => {
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
- const url = `${scheme}://${host}${basePath}`
63
- const name = this.document.info.title || "Swagger Import"
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
- url: url,
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
- const { fields, parameters, queryVerb, transformer } = ctx.request.body
151
- const enrichedQuery = await enrichQueryFields(fields, parameters)
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
- query: enrichedQuery,
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
- exports.execute = async function (ctx) {
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
- query: enrichedQuery,
147
+ fields: query.fields,
148
+ parameters: ctx.request.body.parameters,
189
149
  transformer: query.transformer,
150
+ queryId: ctx.params.queryId,
190
151
  })
191
- ctx.body = rows
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
  }
@@ -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.execute
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: ctx.body,
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 {
@@ -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 { parseStringPromise: xmlParser, Builder: XmlBuilder } = require("xml2js")
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(this.config.url)) {
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 = (new XmlBuilder()).buildObject(object)
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."