@budibase/server 0.9.185-alpha.1 → 0.9.185-alpha.13

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 (234) hide show
  1. package/Dockerfile +1 -1
  2. package/builder/assets/bb-space-man.5be51a45.svg +290 -0
  3. package/builder/assets/index.4a7c5636.css +1 -0
  4. package/builder/assets/index.dc495306.js +1317 -0
  5. package/builder/assets/vendor.470042a4.js +152 -0
  6. package/builder/assets/vendor.5e1fe644.css +1 -0
  7. package/builder/index.html +4 -4
  8. package/coverage/clover.xml +689 -598
  9. package/coverage/coverage-final.json +89 -87
  10. package/coverage/lcov-report/api/controllers/analytics.js.html +2 -2
  11. package/coverage/lcov-report/api/controllers/apikeys.js.html +2 -2
  12. package/coverage/lcov-report/api/controllers/application.js.html +43 -13
  13. package/coverage/lcov-report/api/controllers/auth.js.html +7 -7
  14. package/coverage/lcov-report/api/controllers/automation.js.html +3 -3
  15. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  16. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/component.js.html +4 -4
  18. package/coverage/lcov-report/api/controllers/datasource.js.html +129 -21
  19. package/coverage/lcov-report/api/controllers/dev.js.html +16 -7
  20. package/coverage/lcov-report/api/controllers/hosting.js.html +10 -10
  21. package/coverage/lcov-report/api/controllers/index.html +83 -83
  22. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  23. package/coverage/lcov-report/api/controllers/layout.js.html +3 -3
  24. package/coverage/lcov-report/api/controllers/metadata.js.html +3 -3
  25. package/coverage/lcov-report/api/controllers/permission.js.html +3 -3
  26. package/coverage/lcov-report/api/controllers/query.js.html +48 -210
  27. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  28. package/coverage/lcov-report/api/controllers/routing.js.html +3 -3
  29. package/coverage/lcov-report/api/controllers/row/external.js.html +2 -2
  30. package/coverage/lcov-report/api/controllers/row/index.html +10 -10
  31. package/coverage/lcov-report/api/controllers/row/index.js.html +2 -2
  32. package/coverage/lcov-report/api/controllers/row/internal.js.html +9 -9
  33. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +2 -2
  34. package/coverage/lcov-report/api/controllers/row/utils.js.html +2 -2
  35. package/coverage/lcov-report/api/controllers/screen.js.html +2 -2
  36. package/coverage/lcov-report/api/controllers/script.js.html +3 -3
  37. package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
  38. package/coverage/lcov-report/api/controllers/table/index.html +11 -11
  39. package/coverage/lcov-report/api/controllers/table/index.js.html +3 -3
  40. package/coverage/lcov-report/api/controllers/table/internal.js.html +2 -2
  41. package/coverage/lcov-report/api/controllers/table/utils.js.html +8 -8
  42. package/coverage/lcov-report/api/controllers/templates.js.html +4 -4
  43. package/coverage/lcov-report/api/controllers/user.js.html +33 -18
  44. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  45. package/coverage/lcov-report/api/controllers/view/index.html +9 -9
  46. package/coverage/lcov-report/api/controllers/view/index.js.html +5 -5
  47. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  48. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +3 -3
  49. package/coverage/lcov-report/api/controllers/webhook.js.html +4 -4
  50. package/coverage/lcov-report/api/index.html +1 -1
  51. package/coverage/lcov-report/api/index.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/dev.js.html +2 -2
  63. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/index.html +4 -4
  65. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/query.js.html +5 -2
  71. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/static.js.html +2 -2
  77. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  81. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  82. package/coverage/lcov-report/automations/actions.js.html +2 -2
  83. package/coverage/lcov-report/automations/automationUtils.js.html +3 -3
  84. package/coverage/lcov-report/automations/bullboard.js.html +71 -20
  85. package/coverage/lcov-report/automations/index.html +33 -48
  86. package/coverage/lcov-report/automations/index.js.html +2 -2
  87. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  89. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/discord.js.html +3 -3
  92. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/executeScript.js.html +3 -3
  94. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/index.html +14 -14
  96. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +3 -3
  98. package/coverage/lcov-report/automations/steps/queryRows.js.html +3 -3
  99. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  100. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  101. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  102. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  103. package/coverage/lcov-report/automations/steps/utils.js.html +7 -7
  104. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  105. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  106. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  107. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  108. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  109. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  110. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  111. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  112. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  113. package/coverage/lcov-report/automations/triggers.js.html +22 -10
  114. package/coverage/lcov-report/automations/utils.js.html +42 -12
  115. package/coverage/lcov-report/constants/index.html +5 -5
  116. package/coverage/lcov-report/constants/index.js.html +25 -4
  117. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  118. package/coverage/lcov-report/constants/screens.js.html +1 -1
  119. package/coverage/lcov-report/db/client.js.html +2 -2
  120. package/coverage/lcov-report/db/inMemoryView.js.html +2 -2
  121. package/coverage/lcov-report/db/index.html +2 -2
  122. package/coverage/lcov-report/db/index.js.html +1 -1
  123. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +6 -6
  124. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  125. package/coverage/lcov-report/db/linkedRows/index.js.html +5 -5
  126. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +6 -6
  127. package/coverage/lcov-report/db/newid.js.html +1 -1
  128. package/coverage/lcov-report/db/utils.js.html +1 -1
  129. package/coverage/lcov-report/events/AutomationEmitter.js.html +6 -6
  130. package/coverage/lcov-report/events/index.html +6 -6
  131. package/coverage/lcov-report/events/index.js.html +1 -1
  132. package/coverage/lcov-report/events/utils.js.html +5 -5
  133. package/coverage/lcov-report/index.html +83 -68
  134. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  135. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  136. package/coverage/lcov-report/middleware/appInfo.js.html +2 -2
  137. package/coverage/lcov-report/middleware/authorized.js.html +4 -4
  138. package/coverage/lcov-report/middleware/builder.js.html +16 -7
  139. package/coverage/lcov-report/middleware/currentapp.js.html +6 -6
  140. package/coverage/lcov-report/middleware/index.html +21 -21
  141. package/coverage/lcov-report/middleware/joi-validator.js.html +2 -2
  142. package/coverage/lcov-report/middleware/resourceId.js.html +18 -18
  143. package/coverage/lcov-report/middleware/selfhost.js.html +2 -2
  144. package/coverage/lcov-report/middleware/usageQuota.js.html +4 -4
  145. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  146. package/coverage/lcov-report/{automations/thread.js.html → threads/automation.js.html} +83 -32
  147. package/coverage/lcov-report/threads/index.html +141 -0
  148. package/coverage/lcov-report/threads/index.js.html +260 -0
  149. package/coverage/lcov-report/threads/query.js.html +269 -0
  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 +2 -2
  154. package/coverage/lcov-report/utilities/global.js.html +2 -2
  155. package/coverage/lcov-report/utilities/index.html +21 -21
  156. package/coverage/lcov-report/utilities/index.js.html +12 -9
  157. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +30 -9
  158. package/coverage/lcov-report/utilities/queue/index.html +14 -14
  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 +4 -4
  162. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +2 -2
  163. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +3 -3
  164. package/coverage/lcov-report/utilities/scriptRunner.js.html +16 -7
  165. package/coverage/lcov-report/utilities/security.js.html +3 -3
  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 +5 -5
  169. package/coverage/lcov.info +1136 -972
  170. package/dist/api/controllers/application.js +11 -1
  171. package/dist/api/controllers/datasource.js +31 -7
  172. package/dist/api/controllers/deploy/index.js +2 -0
  173. package/dist/api/controllers/dev.js +4 -1
  174. package/dist/api/controllers/query.js +27 -57
  175. package/dist/api/controllers/user.js +10 -6
  176. package/dist/api/routes/query.js +1 -1
  177. package/dist/app.js +2 -11
  178. package/dist/automations/bullboard.js +37 -10
  179. package/dist/automations/triggers.js +7 -3
  180. package/dist/automations/utils.js +11 -2
  181. package/dist/constants/index.js +6 -0
  182. package/dist/integrations/base/IntegrationBase.js +2 -0
  183. package/dist/integrations/base/sql.js +36 -8
  184. package/dist/integrations/mysql.js +3 -1
  185. package/dist/integrations/utils.js +7 -1
  186. package/dist/middleware/builder.js +4 -1
  187. package/dist/package.json +7 -6
  188. package/dist/{automations/thread.js → threads/automation.js} +23 -7
  189. package/dist/threads/index.js +58 -0
  190. package/dist/threads/query.js +61 -0
  191. package/dist/tsconfig.tsbuildinfo +1 -1
  192. package/dist/utilities/index.js +1 -0
  193. package/dist/utilities/queue/inMemoryQueue.js +8 -0
  194. package/dist/utilities/scriptRunner.js +4 -1
  195. package/package.json +8 -7
  196. package/scripts/likeCypress.ts +33 -0
  197. package/src/api/controllers/application.js +11 -1
  198. package/src/api/controllers/datasource.js +45 -9
  199. package/src/api/controllers/deploy/index.js +2 -0
  200. package/src/api/controllers/dev.js +4 -1
  201. package/src/api/controllers/query.js +26 -80
  202. package/src/api/controllers/user.js +15 -10
  203. package/src/api/routes/query.js +1 -0
  204. package/src/app.ts +2 -10
  205. package/src/automations/bullboard.js +28 -11
  206. package/src/automations/tests/automation.spec.js +3 -4
  207. package/src/automations/triggers.js +7 -3
  208. package/src/automations/utils.js +12 -2
  209. package/src/constants/index.js +7 -0
  210. package/src/integrations/airtable.ts +2 -1
  211. package/src/integrations/arangodb.ts +2 -1
  212. package/src/integrations/base/IntegrationBase.ts +6 -0
  213. package/src/integrations/base/datasourcePlus.ts +2 -1
  214. package/src/integrations/base/sql.ts +38 -9
  215. package/src/integrations/couchdb.ts +2 -1
  216. package/src/integrations/dynamodb.ts +2 -1
  217. package/src/integrations/elasticsearch.ts +2 -1
  218. package/src/integrations/mongodb.ts +2 -1
  219. package/src/integrations/mysql.ts +1 -1
  220. package/src/integrations/rest.ts +2 -1
  221. package/src/integrations/s3.ts +2 -1
  222. package/src/integrations/utils.ts +6 -1
  223. package/src/middleware/builder.js +4 -1
  224. package/src/{automations/thread.js → threads/automation.js} +24 -7
  225. package/src/threads/index.js +60 -0
  226. package/src/threads/query.js +63 -0
  227. package/src/utilities/index.js +1 -0
  228. package/src/utilities/queue/inMemoryQueue.js +7 -0
  229. package/src/utilities/scriptRunner.js +4 -1
  230. package/tsconfig.json +1 -1
  231. package/builder/assets/index.4feb6e69.js +0 -201
  232. package/builder/assets/index.a79ec0ef.css +0 -29
  233. package/builder/assets/vendor.278a305c.js +0 -9
  234. package/builder/assets/vendor.d75d5eb7.css +0 -1
@@ -52,6 +52,7 @@ exports.objectStoreUrl = () => {
52
52
  * via a specific endpoint (under /api/assets/client).
53
53
  * @param {string} appId In production we need the appId to look up the correct bucket, as the
54
54
  * version of the client lib may differ between apps.
55
+ * @param {string} version The version to retrieve.
55
56
  * @return {string} The URL to be inserted into appPackage response or server rendered
56
57
  * app index file.
57
58
  */
@@ -93,5 +93,13 @@ class InMemoryQueue {
93
93
  getRepeatableJobs() {
94
94
  return [];
95
95
  }
96
+ /**
97
+ * Implemented for tests
98
+ */
99
+ clean() {
100
+ return __awaiter(this, void 0, void 0, function* () {
101
+ return [];
102
+ });
103
+ }
96
104
  }
97
105
  module.exports = InMemoryQueue;
@@ -1,10 +1,13 @@
1
1
  "use strict";
2
2
  const fetch = require("node-fetch");
3
3
  const { VM, VMScript } = require("vm2");
4
+ const JS_TIMEOUT_MS = 1000;
4
5
  class ScriptRunner {
5
6
  constructor(script, context) {
6
7
  const code = `let fn = () => {\n${script}\n}; results.out = fn();`;
7
- this.vm = new VM();
8
+ this.vm = new VM({
9
+ timeout: JS_TIMEOUT_MS,
10
+ });
8
11
  this.results = { out: "" };
9
12
  this.vm.setGlobals(context);
10
13
  this.vm.setGlobal("fetch", fetch);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/server",
3
3
  "email": "hi@budibase.com",
4
- "version": "0.9.185-alpha.1",
4
+ "version": "0.9.185-alpha.13",
5
5
  "description": "Budibase Web Server",
6
6
  "main": "src/index.js",
7
7
  "repository": {
@@ -68,9 +68,11 @@
68
68
  "author": "Budibase",
69
69
  "license": "AGPL-3.0-or-later",
70
70
  "dependencies": {
71
- "@budibase/auth": "^0.9.185-alpha.1",
72
- "@budibase/client": "^0.9.185-alpha.1",
73
- "@budibase/string-templates": "^0.9.185-alpha.1",
71
+ "@budibase/auth": "^0.9.185-alpha.13",
72
+ "@budibase/client": "^0.9.185-alpha.13",
73
+ "@budibase/string-templates": "^0.9.185-alpha.13",
74
+ "@bull-board/api": "^3.7.0",
75
+ "@bull-board/koa": "^3.7.0",
74
76
  "@elastic/elasticsearch": "7.10.0",
75
77
  "@koa/router": "8.0.0",
76
78
  "@sendgrid/mail": "7.1.1",
@@ -80,7 +82,6 @@
80
82
  "aws-sdk": "^2.767.0",
81
83
  "bcryptjs": "2.4.3",
82
84
  "bull": "^3.22.4",
83
- "bull-board": "^2.0.1",
84
85
  "chmodr": "1.2.0",
85
86
  "csvtojson": "2.0.10",
86
87
  "dotenv": "8.2.0",
@@ -120,6 +121,7 @@
120
121
  "uuid": "3.3.2",
121
122
  "validate.js": "0.13.1",
122
123
  "vm2": "^3.9.3",
124
+ "worker-farm": "^1.7.0",
123
125
  "yargs": "13.2.4",
124
126
  "zlib": "1.0.5"
125
127
  },
@@ -138,7 +140,6 @@
138
140
  "copyfiles": "^2.4.1",
139
141
  "docker-compose": "^0.23.6",
140
142
  "eslint": "^6.8.0",
141
- "express": "^4.17.1",
142
143
  "jest": "^27.0.5",
143
144
  "nodemon": "^2.0.4",
144
145
  "prettier": "^2.3.1",
@@ -149,5 +150,5 @@
149
150
  "typescript": "^4.3.5",
150
151
  "update-dotenv": "^1.1.1"
151
152
  },
152
- "gitHead": "38f0e8d665b376bdc4459b004592cdf2bd834d48"
153
+ "gitHead": "95fcaeea0609b133eecff0faae3e06bb3d4c4cfa"
153
154
  }
@@ -0,0 +1,33 @@
1
+ /******************************************************
2
+ * This script just makes it easy to re-create *
3
+ * a cypress like environment for testing the backend *
4
+ ******************************************************/
5
+ const path = require("path")
6
+ const tmpdir = path.join(require("os").tmpdir(), ".budibase")
7
+
8
+ const MAIN_PORT = "10001"
9
+ const WORKER_PORT = "10002"
10
+
11
+ // @ts-ignore
12
+ process.env.PORT = MAIN_PORT
13
+ process.env.BUDIBASE_API_KEY = "6BE826CB-6B30-4AEC-8777-2E90464633DE"
14
+ process.env.NODE_ENV = "cypress"
15
+ process.env.ENABLE_ANALYTICS = "false"
16
+ process.env.JWT_SECRET = "budibase"
17
+ process.env.COUCH_URL = `leveldb://${tmpdir}/.data/`
18
+ process.env.SELF_HOSTED = "1"
19
+ process.env.WORKER_URL = `http://localhost:${WORKER_PORT}/`
20
+ process.env.MINIO_URL = `http://localhost:${MAIN_PORT}/`
21
+ process.env.MINIO_ACCESS_KEY = "budibase"
22
+ process.env.MINIO_SECRET_KEY = "budibase"
23
+ process.env.COUCH_DB_USER = "budibase"
24
+ process.env.COUCH_DB_PASSWORD = "budibase"
25
+ process.env.INTERNAL_API_KEY = "budibase"
26
+ process.env.ALLOW_DEV_AUTOMATIONS = "1"
27
+
28
+ // don't make this a variable or top level require
29
+ // it will cause environment module to be loaded prematurely
30
+ const server = require("../src/app")
31
+ process.env.PORT = WORKER_PORT
32
+ const worker = require("../../worker/src/index")
33
+ process.env.PORT = MAIN_PORT
@@ -45,6 +45,8 @@ const {
45
45
  } = require("../../utilities/fileSystem/clientLibrary")
46
46
  const { getTenantId, isMultiTenant } = require("@budibase/auth/tenancy")
47
47
  const { syncGlobalUsers } = require("./user")
48
+ const { app: appCache } = require("@budibase/auth/cache")
49
+ const { cleanupAutomations } = require("../../automations/utils")
48
50
 
49
51
  const URL_REGEX_SLASH = /\/|\\/g
50
52
 
@@ -254,6 +256,7 @@ exports.create = async ctx => {
254
256
  await createApp(appId)
255
257
  }
256
258
 
259
+ await appCache.invalidateAppMetadata(appId, newApplication)
257
260
  ctx.status = 200
258
261
  ctx.body = newApplication
259
262
  }
@@ -317,8 +320,12 @@ exports.delete = async ctx => {
317
320
  if (!env.isTest() && !ctx.query.unpublish) {
318
321
  await deleteApp(ctx.params.appId)
319
322
  }
323
+ if (ctx.query && ctx.query.unpublish) {
324
+ await cleanupAutomations(ctx.params.appId)
325
+ }
320
326
  // make sure the app/role doesn't stick around after the app has been deleted
321
327
  await removeAppFromUserRoles(ctx, ctx.params.appId)
328
+ await appCache.invalidateAppMetadata(ctx.params.appId)
322
329
 
323
330
  ctx.status = 200
324
331
  ctx.body = result
@@ -387,7 +394,10 @@ const updateAppPackage = async (ctx, appPackage, appId) => {
387
394
  // Redis, shouldn't ever store it
388
395
  delete newAppPackage.lockedBy
389
396
 
390
- return await db.put(newAppPackage)
397
+ const response = await db.put(newAppPackage)
398
+ // remove any cached metadata, so that it will be updated
399
+ await appCache.invalidateAppMetadata(appId)
400
+ return response
391
401
  }
392
402
 
393
403
  const createEmptyAppPackage = async (ctx, app) => {
@@ -7,7 +7,7 @@ const {
7
7
  BudibaseInternalDB,
8
8
  getTableParams,
9
9
  } = require("../../db/utils")
10
- const { BuildSchemaErrors } = require("../../constants")
10
+ const { BuildSchemaErrors, InvalidColumns } = require("../../constants")
11
11
  const { integrations } = require("../../integrations")
12
12
  const { getDatasourceAndQuery } = require("./row/utils")
13
13
 
@@ -119,8 +119,16 @@ exports.destroy = async function (ctx) {
119
119
  const db = new CouchDB(ctx.appId)
120
120
 
121
121
  // Delete all queries for the datasource
122
- const rows = await db.allDocs(getQueryParams(ctx.params.datasourceId, null))
123
- await db.bulkDocs(rows.rows.map(row => ({ ...row.doc, _deleted: true })))
122
+ const queries = await db.allDocs(
123
+ getQueryParams(ctx.params.datasourceId, null)
124
+ )
125
+ await db.bulkDocs(
126
+ queries.rows.map(row => ({
127
+ _id: row.id,
128
+ _rev: row.value.rev,
129
+ _deleted: true,
130
+ }))
131
+ )
124
132
 
125
133
  // delete the datasource
126
134
  await db.remove(ctx.params.datasourceId, ctx.params.revId)
@@ -144,6 +152,23 @@ exports.query = async function (ctx) {
144
152
  }
145
153
  }
146
154
 
155
+ function getErrorTables(errors, errorType) {
156
+ return Object.entries(errors)
157
+ .filter(entry => entry[1] === errorType)
158
+ .map(([name]) => name)
159
+ }
160
+
161
+ function updateError(error, newError, tables) {
162
+ if (!error) {
163
+ error = ""
164
+ }
165
+ if (error.length > 0) {
166
+ error += "\n"
167
+ }
168
+ error += `${newError} ${tables.join(", ")}`
169
+ return error
170
+ }
171
+
147
172
  const buildSchemaHelper = async datasource => {
148
173
  const Connector = integrations[datasource.source]
149
174
 
@@ -168,12 +193,23 @@ const buildSchemaHelper = async datasource => {
168
193
  const errors = connector.schemaErrors
169
194
  let error = null
170
195
  if (errors && Object.keys(errors).length > 0) {
171
- const noKeyTables = Object.entries(errors)
172
- .filter(entry => entry[1] === BuildSchemaErrors.NO_KEY)
173
- .map(([name]) => name)
174
- error = `No primary key constraint found for the following: ${noKeyTables.join(
175
- ", "
176
- )}`
196
+ const noKey = getErrorTables(errors, BuildSchemaErrors.NO_KEY)
197
+ const invalidCol = getErrorTables(errors, BuildSchemaErrors.INVALID_COLUMN)
198
+ if (noKey.length) {
199
+ error = updateError(
200
+ error,
201
+ "No primary key constraint found for the following:",
202
+ noKey
203
+ )
204
+ }
205
+ if (invalidCol.length) {
206
+ const invalidCols = Object.values(InvalidColumns).join(", ")
207
+ error = updateError(
208
+ error,
209
+ `Cannot use columns ${invalidCols} found in following:`,
210
+ invalidCol
211
+ )
212
+ }
177
213
  }
178
214
  return { tables: connector.tables, error }
179
215
  }
@@ -6,6 +6,7 @@ const {
6
6
  disableAllCrons,
7
7
  enableCronTrigger,
8
8
  } = require("../../../automations/utils")
9
+ const { app: appCache } = require("@budibase/auth/cache")
9
10
 
10
11
  // the max time we can wait for an invalidation to complete before considering it failed
11
12
  const MAX_PENDING_TIME_MS = 30 * 60000
@@ -103,6 +104,7 @@ async function deployApp(deployment) {
103
104
  appDoc.appId = productionAppId
104
105
  appDoc.instance._id = productionAppId
105
106
  await db.put(appDoc)
107
+ await appCache.invalidateAppMetadata(productionAppId)
106
108
  console.log("New app doc written successfully.")
107
109
  await initDeployedApp(productionAppId)
108
110
  console.log("Deployed app initialised, setting deployment to successful")
@@ -6,6 +6,7 @@ const { request } = require("../../utilities/workerRequests")
6
6
  const { clearLock } = require("../../utilities/redis")
7
7
  const { Replication } = require("@budibase/auth").db
8
8
  const { DocumentTypes } = require("../../db/utils")
9
+ const { app: appCache } = require("@budibase/auth/cache")
9
10
 
10
11
  async function redirect(ctx, method, path = "global") {
11
12
  const { devPath } = ctx.params
@@ -24,7 +25,8 @@ async function redirect(ctx, method, path = "global") {
24
25
  )
25
26
  )
26
27
  if (response.status !== 200) {
27
- ctx.throw(response.status, response.statusText)
28
+ const err = await response.text()
29
+ ctx.throw(400, err)
28
30
  }
29
31
  const cookie = response.headers.get("set-cookie")
30
32
  if (cookie) {
@@ -106,6 +108,7 @@ exports.revert = async ctx => {
106
108
  appDoc.appId = appId
107
109
  appDoc.instance._id = appId
108
110
  await db.put(appDoc)
111
+ await appCache.invalidateAppMetadata(appId)
109
112
  ctx.body = {
110
113
  message: "Reverted changes successfully.",
111
114
  }
@@ -1,10 +1,11 @@
1
1
  const { processString } = require("@budibase/string-templates")
2
2
  const CouchDB = require("../../db")
3
3
  const { generateQueryID, getQueryParams } = require("../../db/utils")
4
- const { integrations } = require("../../integrations")
5
4
  const { BaseQueryVerbs } = require("../../constants")
6
5
  const env = require("../../environment")
7
- const ScriptRunner = require("../../utilities/scriptRunner")
6
+ const { Thread, ThreadType } = require("../../threads")
7
+
8
+ const Runner = new Thread(ThreadType.QUERY, { timeoutMs: 10000 })
8
9
 
9
10
  // simple function to append "readable" to all read queries
10
11
  function enrichQueries(input) {
@@ -18,47 +19,6 @@ function enrichQueries(input) {
18
19
  return wasArray ? queries : queries[0]
19
20
  }
20
21
 
21
- function formatResponse(resp) {
22
- if (typeof resp === "string") {
23
- try {
24
- resp = JSON.parse(resp)
25
- } catch (err) {
26
- resp = { response: resp }
27
- }
28
- }
29
- return resp
30
- }
31
-
32
- async function runAndTransform(
33
- integration,
34
- queryVerb,
35
- enrichedQuery,
36
- transformer
37
- ) {
38
- let rows = formatResponse(await integration[queryVerb](enrichedQuery))
39
-
40
- // transform as required
41
- if (transformer) {
42
- const runner = new ScriptRunner(transformer, { data: rows })
43
- rows = runner.execute()
44
- }
45
-
46
- // needs to an array for next step
47
- if (!Array.isArray(rows)) {
48
- rows = [rows]
49
- }
50
-
51
- // map into JSON if just raw primitive here
52
- if (rows.find(row => typeof row !== "object")) {
53
- rows = rows.map(value => ({ value }))
54
- }
55
-
56
- // get all the potential fields in the schema
57
- let keys = rows.flatMap(Object.keys)
58
-
59
- return { rows, keys }
60
- }
61
-
62
22
  exports.fetch = async function (ctx) {
63
23
  const db = new CouchDB(ctx.appId)
64
24
 
@@ -143,30 +103,23 @@ exports.preview = async function (ctx) {
143
103
 
144
104
  const datasource = await db.get(ctx.request.body.datasourceId)
145
105
 
146
- const Integration = integrations[datasource.source]
147
-
148
- if (!Integration) {
149
- ctx.throw(400, "Integration type does not exist.")
150
- }
151
-
152
106
  const { fields, parameters, queryVerb, transformer } = ctx.request.body
153
107
  const enrichedQuery = await enrichQueryFields(fields, parameters)
154
- const integration = new Integration(datasource.config)
155
108
 
156
- const { rows, keys } = await runAndTransform(
157
- integration,
158
- queryVerb,
159
- enrichedQuery,
160
- transformer
161
- )
109
+ try {
110
+ const { rows, keys } = await Runner.run({
111
+ datasource,
112
+ queryVerb,
113
+ query: enrichedQuery,
114
+ transformer,
115
+ })
162
116
 
163
- ctx.body = {
164
- rows,
165
- schemaFields: [...new Set(keys)],
166
- }
167
- // cleanup
168
- if (integration.end) {
169
- integration.end()
117
+ ctx.body = {
118
+ rows,
119
+ schemaFields: [...new Set(keys)],
120
+ }
121
+ } catch (err) {
122
+ ctx.throw(400, err)
170
123
  }
171
124
  }
172
125
 
@@ -176,29 +129,22 @@ exports.execute = async function (ctx) {
176
129
  const query = await db.get(ctx.params.queryId)
177
130
  const datasource = await db.get(query.datasourceId)
178
131
 
179
- const Integration = integrations[datasource.source]
180
-
181
- if (!Integration) {
182
- ctx.throw(400, "Integration type does not exist.")
183
- }
184
-
185
132
  const enrichedQuery = await enrichQueryFields(
186
133
  query.fields,
187
134
  ctx.request.body.parameters
188
135
  )
189
- const integration = new Integration(datasource.config)
190
136
 
191
137
  // call the relevant CRUD method on the integration class
192
- const { rows } = await runAndTransform(
193
- integration,
194
- query.queryVerb,
195
- enrichedQuery,
196
- query.transformer
197
- )
198
- ctx.body = rows
199
- // cleanup
200
- if (integration.end) {
201
- integration.end()
138
+ try {
139
+ const { rows } = await Runner.run({
140
+ datasource,
141
+ queryVerb: query.queryVerb,
142
+ query: enrichedQuery,
143
+ transformer: query.transformer,
144
+ })
145
+ ctx.body = rows
146
+ } catch (err) {
147
+ ctx.throw(400, err)
202
148
  }
203
149
  }
204
150
 
@@ -97,6 +97,7 @@ exports.syncUser = async function (ctx) {
97
97
  .map(([appId]) => appId)
98
98
  }
99
99
  for (let prodAppId of prodAppIds) {
100
+ const roleId = roles[prodAppId]
100
101
  const devAppId = getDevelopmentAppID(prodAppId)
101
102
  for (let appId of [prodAppId, devAppId]) {
102
103
  if (!(await doesDatabaseExist(appId))) {
@@ -115,17 +116,21 @@ exports.syncUser = async function (ctx) {
115
116
  tableId: InternalTables.USER_METADATA,
116
117
  }
117
118
  }
118
- let combined
119
- if (deleting) {
120
- combined = {
121
- ...metadata,
122
- status: UserStatus.INACTIVE,
123
- metadata: BUILTIN_ROLE_IDS.PUBLIC,
124
- }
125
- } else {
126
- combined = combineMetadataAndUser(user, metadata)
119
+ // assign the roleId for the metadata doc
120
+ if (roleId) {
121
+ metadata.roleId = roleId
122
+ }
123
+ let combined = !deleting
124
+ ? combineMetadataAndUser(user, metadata)
125
+ : {
126
+ ...metadata,
127
+ status: UserStatus.INACTIVE,
128
+ metadata: BUILTIN_ROLE_IDS.PUBLIC,
129
+ }
130
+ // if its null then there was no updates required
131
+ if (combined) {
132
+ await db.put(combined)
127
133
  }
128
- await db.put(combined)
129
134
  }
130
135
  }
131
136
  ctx.body = {
@@ -66,6 +66,7 @@ router
66
66
  )
67
67
  .get(
68
68
  "/api/queries/:queryId",
69
+ paramResource("queryId"),
69
70
  authorized(PermissionTypes.QUERY, PermissionLevels.READ),
70
71
  queryController.find
71
72
  )
package/src/app.ts CHANGED
@@ -41,16 +41,8 @@ app.use(
41
41
  )
42
42
 
43
43
  if (!env.isTest()) {
44
- const bullApp = bullboard.init()
45
- app.use(async (ctx: ExtendableContext, next: () => any) => {
46
- if (ctx.path.startsWith(bullboard.pathPrefix)) {
47
- ctx.status = 200
48
- ctx.respond = false
49
- bullApp(ctx.req, ctx.res)
50
- } else {
51
- await next()
52
- }
53
- })
44
+ const plugin = bullboard.init()
45
+ app.use(plugin)
54
46
  }
55
47
 
56
48
  app.context.eventEmitter = eventEmitter
@@ -1,6 +1,6 @@
1
- const { createBullBoard } = require("bull-board")
2
- const { BullAdapter } = require("bull-board/bullAdapter")
3
- const express = require("express")
1
+ const { createBullBoard } = require("@bull-board/api")
2
+ const { BullAdapter } = require("@bull-board/api/bullAdapter")
3
+ const { KoaAdapter } = require("@bull-board/koa")
4
4
  const env = require("../environment")
5
5
  const Queue = env.isTest()
6
6
  ? require("../utilities/queue/inMemoryQueue")
@@ -9,23 +9,40 @@ const { JobQueues } = require("../constants")
9
9
  const { utils } = require("@budibase/auth/redis")
10
10
  const { opts, redisProtocolUrl } = utils.getRedisOptions()
11
11
 
12
- const redisConfig = redisProtocolUrl || { redis: opts }
13
- let automationQueue = new Queue(JobQueues.AUTOMATIONS, redisConfig)
12
+ const CLEANUP_PERIOD_MS = 60 * 1000
13
+ const queueConfig = redisProtocolUrl || { redis: opts }
14
+ let cleanupInternal = null
14
15
 
15
- exports.pathPrefix = "/bulladmin"
16
+ let automationQueue = new Queue(JobQueues.AUTOMATIONS, queueConfig)
17
+
18
+ async function cleanup() {
19
+ await automationQueue.clean(CLEANUP_PERIOD_MS, "completed")
20
+ }
21
+
22
+ const PATH_PREFIX = "/bulladmin"
16
23
 
17
24
  exports.init = () => {
18
- const expressApp = express()
25
+ // cleanup the events every 5 minutes
26
+ if (!cleanupInternal) {
27
+ cleanupInternal = setInterval(cleanup, CLEANUP_PERIOD_MS)
28
+ // fire off an initial cleanup
29
+ cleanup().catch(err => {
30
+ console.error(`Unable to cleanup automation queue initially - ${err}`)
31
+ })
32
+ }
19
33
  // Set up queues for bull board admin
20
34
  const queues = [automationQueue]
21
35
  const adapters = []
36
+ const serverAdapter = new KoaAdapter()
22
37
  for (let queue of queues) {
23
38
  adapters.push(new BullAdapter(queue))
24
39
  }
25
- const { router } = createBullBoard(adapters)
26
-
27
- expressApp.use(exports.pathPrefix, router)
28
- return expressApp
40
+ createBullBoard({
41
+ queues: adapters,
42
+ serverAdapter,
43
+ })
44
+ serverAdapter.setBasePath(PATH_PREFIX)
45
+ return serverAdapter.registerPlugin()
29
46
  }
30
47
 
31
48
  exports.queue = automationQueue
@@ -1,5 +1,5 @@
1
1
  jest.mock("../../utilities/usageQuota")
2
- jest.mock("../thread")
2
+ jest.mock("../../threads/automation")
3
3
  jest.mock("../../utilities/redis", () => ({
4
4
  init: jest.fn(),
5
5
  checkTestFlag: () => {
@@ -11,8 +11,7 @@ jest.spyOn(global.console, "error")
11
11
 
12
12
  require("../../environment")
13
13
  const automation = require("../index")
14
- const usageQuota = require("../../utilities/usageQuota")
15
- const thread = require("../thread")
14
+ const thread = require("../../threads/automation")
16
15
  const triggers = require("../triggers")
17
16
  const { basicAutomation } = require("../../tests/utilities/structures")
18
17
  const { wait } = require("../../utilities")
@@ -62,7 +61,7 @@ describe("Run through some parts of the automations system", () => {
62
61
  }
63
62
  }
64
63
  }
65
- }))
64
+ }), expect.any(Function))
66
65
  })
67
66
 
68
67
  it("should be able to clean inputs with the utilities", () => {
@@ -11,6 +11,10 @@ const utils = require("./utils")
11
11
  const env = require("../environment")
12
12
 
13
13
  const TRIGGER_DEFINITIONS = definitions
14
+ const JOB_OPTS = {
15
+ removeOnComplete: true,
16
+ removeOnFail: true,
17
+ }
14
18
 
15
19
  async function queueRelevantRowAutomations(event, eventType) {
16
20
  if (event.appId == null) {
@@ -47,7 +51,7 @@ async function queueRelevantRowAutomations(event, eventType) {
47
51
  automationTrigger.inputs &&
48
52
  automationTrigger.inputs.tableId === event.row.tableId
49
53
  ) {
50
- await queue.add({ automation, event })
54
+ await queue.add({ automation, event }, JOB_OPTS)
51
55
  }
52
56
  }
53
57
  }
@@ -86,7 +90,7 @@ exports.externalTrigger = async function (
86
90
  automation.definition.trigger != null &&
87
91
  automation.definition.trigger.stepId === definitions.APP.stepId &&
88
92
  automation.definition.trigger.stepId === "APP" &&
89
- !checkTestFlag(automation._id)
93
+ !(await checkTestFlag(automation._id))
90
94
  ) {
91
95
  // values are likely to be submitted as strings, so we shall convert to correct type
92
96
  const coercedFields = {}
@@ -100,7 +104,7 @@ exports.externalTrigger = async function (
100
104
  if (getResponses) {
101
105
  return utils.processEvent({ data })
102
106
  } else {
103
- return queue.add(data)
107
+ return queue.add(data, JOB_OPTS)
104
108
  }
105
109
  }
106
110
 
@@ -1,4 +1,4 @@
1
- const runner = require("./thread")
1
+ const { Thread, ThreadType } = require("../threads")
2
2
  const { definitions } = require("./triggerInfo")
3
3
  const webhooks = require("../api/controllers/webhook")
4
4
  const CouchDB = require("../db")
@@ -10,11 +10,12 @@ const { getDeployedAppID } = require("@budibase/auth/db")
10
10
 
11
11
  const WH_STEP_ID = definitions.WEBHOOK.stepId
12
12
  const CRON_STEP_ID = definitions.CRON.stepId
13
+ const Runner = new Thread(ThreadType.AUTOMATION)
13
14
 
14
15
  exports.processEvent = async job => {
15
16
  try {
16
17
  // need to actually await these so that an error can be captured properly
17
- return await runner(job)
18
+ return await Runner.run(job)
18
19
  } catch (err) {
19
20
  console.error(
20
21
  `${job.data.automation.appId} automation ${job.data.automation._id} was unable to run - ${err}`
@@ -162,3 +163,12 @@ exports.checkForWebhooks = async ({ appId, oldAuto, newAuto }) => {
162
163
  }
163
164
  return newAuto
164
165
  }
166
+
167
+ /**
168
+ * When removing an app/unpublishing it need to make sure automations are cleaned up (cron).
169
+ * @param appId {string} the app that is being removed.
170
+ * @return {Promise<void>} clean is complete if this succeeds.
171
+ */
172
+ exports.cleanupAutomations = async appId => {
173
+ await exports.disableAllCrons(appId)
174
+ }