@budibase/server 1.1.32 → 1.1.33-alpha.0

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 (204) hide show
  1. package/builder/assets/{index.192adf4c.css → index.4d57d3d4.css} +2 -2
  2. package/builder/assets/index.f51f2e5c.js +977 -0
  3. package/builder/assets/{vendor.162098da.js → vendor.6bcafa60.js} +1 -1
  4. package/builder/index.html +3 -3
  5. package/coverage/clover.xml +381 -696
  6. package/coverage/coverage-final.json +13 -16
  7. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  8. package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
  9. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  10. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  11. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  12. package/coverage/lcov-report/api/controllers/component.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
  15. package/coverage/lcov-report/api/controllers/index.html +1 -1
  16. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  18. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/migrations.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/query/index.html +1 -1
  22. package/coverage/lcov-report/api/controllers/query/validation.js.html +4 -4
  23. package/coverage/lcov-report/api/controllers/role.js.html +1 -1
  24. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  25. package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
  26. package/coverage/lcov-report/api/controllers/row/index.html +18 -18
  27. package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +94 -49
  29. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +1 -1
  30. package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
  31. package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
  32. package/coverage/lcov-report/api/controllers/script.js.html +1 -1
  33. package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +1 -1
  34. package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
  35. package/coverage/lcov-report/api/controllers/table/index.html +1 -1
  36. package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
  37. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  38. package/coverage/lcov-report/api/controllers/user.js.html +1 -1
  39. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  40. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  41. package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
  42. package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
  43. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
  44. package/coverage/lcov-report/api/controllers/webhook.js.html +15 -15
  45. package/coverage/lcov-report/api/index.html +1 -1
  46. package/coverage/lcov-report/api/index.js.html +1 -1
  47. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  48. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  49. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  50. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  51. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/index.html +1 -1
  58. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/migrations.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/utils/index.html +1 -1
  72. package/coverage/lcov-report/api/routes/utils/validators.js.html +2 -2
  73. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  75. package/coverage/lcov-report/automations/actions.js.html +1 -1
  76. package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
  77. package/coverage/lcov-report/automations/bullboard.js.html +17 -17
  78. package/coverage/lcov-report/automations/index.html +1 -1
  79. package/coverage/lcov-report/automations/index.js.html +1 -1
  80. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  81. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  82. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  83. package/coverage/lcov-report/automations/steps/executeQuery.js.html +5 -2
  84. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  85. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/index.html +1 -1
  87. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/loop.js.html +1 -1
  89. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
  96. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  97. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  98. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  99. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  100. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  101. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  102. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  103. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  104. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  105. package/coverage/lcov-report/automations/triggers.js.html +18 -18
  106. package/coverage/lcov-report/constants/index.html +1 -1
  107. package/coverage/lcov-report/constants/index.js.html +5 -14
  108. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  109. package/coverage/lcov-report/constants/screens.js.html +1 -1
  110. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  111. package/coverage/lcov-report/db/index.html +19 -19
  112. package/coverage/lcov-report/db/index.js.html +1 -1
  113. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
  114. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  115. package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
  116. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  117. package/coverage/lcov-report/db/newid.js.html +1 -1
  118. package/coverage/lcov-report/db/utils.js.html +61 -13
  119. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  120. package/coverage/lcov-report/events/index.html +1 -1
  121. package/coverage/lcov-report/events/index.js.html +1 -1
  122. package/coverage/lcov-report/events/utils.js.html +1 -1
  123. package/coverage/lcov-report/index.html +57 -72
  124. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  125. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  126. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  127. package/coverage/lcov-report/middleware/builder.js.html +1 -1
  128. package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
  129. package/coverage/lcov-report/middleware/index.html +21 -21
  130. package/coverage/lcov-report/middleware/joi-validator.js.html +43 -43
  131. package/coverage/lcov-report/middleware/publicApi.js.html +1 -1
  132. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  133. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  134. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  135. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  136. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  137. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  138. package/coverage/lcov-report/utilities/global.js.html +68 -14
  139. package/coverage/lcov-report/utilities/index.html +19 -19
  140. package/coverage/lcov-report/utilities/index.js.html +1 -1
  141. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +26 -14
  142. package/coverage/lcov-report/utilities/queue/index.html +17 -17
  143. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  144. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  145. package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
  146. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
  147. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
  148. package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
  149. package/coverage/lcov-report/utilities/security.js.html +2 -2
  150. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  151. package/coverage/lcov-report/utilities/usageQuota/index.html +1 -1
  152. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +1 -1
  153. package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +1 -1
  154. package/coverage/lcov-report/utilities/users.js.html +1 -1
  155. package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
  156. package/coverage/lcov.info +695 -1214
  157. package/dist/api/controllers/deploy/index.js +1 -0
  158. package/dist/api/controllers/query/index.js +1 -1
  159. package/dist/api/controllers/query/validation.js +3 -3
  160. package/dist/api/controllers/row/internalSearch.js +30 -16
  161. package/dist/api/routes/utils/validators.js +1 -1
  162. package/dist/automations/utils.js +47 -1
  163. package/dist/constants/index.js +1 -4
  164. package/dist/db/utils.js +14 -1
  165. package/dist/definitions/automations.js +8 -0
  166. package/dist/integrations/base/sql.js +43 -14
  167. package/dist/package.json +8 -8
  168. package/dist/threads/automation.js +147 -54
  169. package/dist/threads/definitions.js +2 -0
  170. package/dist/threads/index.js +27 -20
  171. package/dist/threads/query.js +23 -15
  172. package/dist/threads/utils.js +47 -24
  173. package/dist/tsconfig.build.tsbuildinfo +1 -1
  174. package/dist/utilities/global.js +24 -5
  175. package/dist/utilities/queue/inMemoryQueue.js +5 -0
  176. package/package.json +9 -9
  177. package/src/api/controllers/deploy/index.ts +6 -1
  178. package/src/api/controllers/query/index.ts +1 -1
  179. package/src/api/controllers/query/validation.js +3 -3
  180. package/src/api/controllers/row/internalSearch.js +35 -20
  181. package/src/api/routes/tests/user.spec.js +2 -0
  182. package/src/api/routes/utils/validators.js +1 -1
  183. package/src/automations/steps/executeQuery.js +1 -0
  184. package/src/automations/tests/automation.spec.js +2 -2
  185. package/src/automations/utils.ts +54 -1
  186. package/src/constants/index.js +2 -5
  187. package/src/db/utils.js +16 -0
  188. package/src/definitions/automations.ts +49 -0
  189. package/src/definitions/datasource.ts +3 -0
  190. package/src/integrations/base/sql.ts +44 -13
  191. package/src/integrations/tests/sql.spec.js +50 -4
  192. package/src/middleware/tests/currentapp.spec.js +11 -0
  193. package/src/threads/{automation.js → automation.ts} +172 -48
  194. package/src/threads/definitions.ts +18 -0
  195. package/src/threads/index.ts +31 -23
  196. package/src/threads/{query.js → query.ts} +32 -12
  197. package/src/threads/{utils.js → utils.ts} +31 -12
  198. package/src/utilities/global.js +21 -3
  199. package/src/utilities/queue/inMemoryQueue.js +4 -0
  200. package/builder/assets/index.133db51f.js +0 -978
  201. package/coverage/lcov-report/threads/automation.js.html +0 -1114
  202. package/coverage/lcov-report/threads/index.html +0 -146
  203. package/coverage/lcov-report/threads/query.js.html +0 -916
  204. package/coverage/lcov-report/threads/utils.js.html +0 -319
@@ -16,7 +16,9 @@ const { user: userCache } = require("@budibase/backend-core/cache");
16
16
  const { getGlobalDB, isUserInAppTenant, } = require("@budibase/backend-core/tenancy");
17
17
  const env = require("../environment");
18
18
  const { getAppId } = require("@budibase/backend-core/context");
19
+ const { groups } = require("@budibase/pro");
19
20
  exports.updateAppRole = (user, { appId } = {}) => {
21
+ var _a, _b;
20
22
  appId = appId || getAppId();
21
23
  if (!user || !user.roles) {
22
24
  return user;
@@ -34,17 +36,34 @@ exports.updateAppRole = (user, { appId } = {}) => {
34
36
  if (!user.roleId && user.builder && user.builder.global) {
35
37
  user.roleId = BUILTIN_ROLE_IDS.ADMIN;
36
38
  }
37
- else if (!user.roleId) {
39
+ else if (!user.roleId && !((_a = user === null || user === void 0 ? void 0 : user.userGroups) === null || _a === void 0 ? void 0 : _a.length)) {
38
40
  user.roleId = BUILTIN_ROLE_IDS.PUBLIC;
39
41
  }
42
+ else if ((_b = user === null || user === void 0 ? void 0 : user.userGroups) === null || _b === void 0 ? void 0 : _b.length) {
43
+ user.roleId = null;
44
+ }
40
45
  delete user.roles;
41
46
  return user;
42
47
  };
48
+ function checkGroupRoles(user, { appId } = {}) {
49
+ return __awaiter(this, void 0, void 0, function* () {
50
+ let roleId = yield groups.getGroupRoleId(user, appId);
51
+ user.roleId = roleId;
52
+ return user;
53
+ });
54
+ }
43
55
  function processUser(user, { appId } = {}) {
44
- if (user) {
45
- delete user.password;
46
- }
47
- return exports.updateAppRole(user, { appId });
56
+ var _a;
57
+ return __awaiter(this, void 0, void 0, function* () {
58
+ if (user) {
59
+ delete user.password;
60
+ }
61
+ user = yield exports.updateAppRole(user, { appId });
62
+ if (!user.roleId && ((_a = user === null || user === void 0 ? void 0 : user.userGroups) === null || _a === void 0 ? void 0 : _a.length)) {
63
+ user = yield checkGroupRoles(user, { appId });
64
+ }
65
+ return user;
66
+ });
48
67
  }
49
68
  exports.getCachedSelf = (ctx, appId) => __awaiter(void 0, void 0, void 0, function* () {
50
69
  // this has to be tenant aware, can't depend on the context to find it out
@@ -114,5 +114,10 @@ class InMemoryQueue {
114
114
  return [];
115
115
  });
116
116
  }
117
+ getJob() {
118
+ return __awaiter(this, void 0, void 0, function* () {
119
+ return {};
120
+ });
121
+ }
117
122
  }
118
123
  module.exports = InMemoryQueue;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/server",
3
3
  "email": "hi@budibase.com",
4
- "version": "1.1.32",
4
+ "version": "1.1.33-alpha.0",
5
5
  "description": "Budibase Web Server",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -77,11 +77,11 @@
77
77
  "license": "GPL-3.0",
78
78
  "dependencies": {
79
79
  "@apidevtools/swagger-parser": "10.0.3",
80
- "@budibase/backend-core": "^1.1.32",
81
- "@budibase/client": "^1.1.32",
82
- "@budibase/pro": "1.1.31",
83
- "@budibase/string-templates": "^1.1.32",
84
- "@budibase/types": "^1.1.32",
80
+ "@budibase/backend-core": "1.1.33-alpha.0",
81
+ "@budibase/client": "1.1.33-alpha.0",
82
+ "@budibase/pro": "1.1.32",
83
+ "@budibase/string-templates": "1.1.33-alpha.0",
84
+ "@budibase/types": "1.1.33-alpha.0",
85
85
  "@bull-board/api": "3.7.0",
86
86
  "@bull-board/koa": "3.9.4",
87
87
  "@elastic/elasticsearch": "7.10.0",
@@ -118,7 +118,7 @@
118
118
  "koa-send": "5.0.0",
119
119
  "koa-session": "5.12.0",
120
120
  "koa-static": "5.0.0",
121
- "koa2-ratelimit": "1.1.0",
121
+ "koa2-ratelimit": "1.1.1",
122
122
  "lodash": "4.17.21",
123
123
  "memorystream": "0.3.1",
124
124
  "mongodb": "3.6.3",
@@ -137,7 +137,7 @@
137
137
  "redis": "4",
138
138
  "server-destroy": "1.0.1",
139
139
  "snowflake-promise": "^4.5.0",
140
- "svelte": "3.44.1",
140
+ "svelte": "3.49.0",
141
141
  "swagger-parser": "10.0.3",
142
142
  "to-json-schema": "0.2.5",
143
143
  "uuid": "3.3.2",
@@ -195,5 +195,5 @@
195
195
  "optionalDependencies": {
196
196
  "oracledb": "5.3.0"
197
197
  },
198
- "gitHead": "900d5de0723c1b7acf218446507d52755fdf7d83"
198
+ "gitHead": "b57c4e5e740dafe0a44c035602d121eff65c70e2"
199
199
  }
@@ -5,7 +5,11 @@ import {
5
5
  getDevelopmentAppID,
6
6
  } from "@budibase/backend-core/db"
7
7
  import { DocumentTypes, getAutomationParams } from "../../../db/utils"
8
- import { disableAllCrons, enableCronTrigger } from "../../../automations/utils"
8
+ import {
9
+ disableAllCrons,
10
+ enableCronTrigger,
11
+ clearMetadata,
12
+ } from "../../../automations/utils"
9
13
  import { app as appCache } from "@budibase/backend-core/cache"
10
14
  import {
11
15
  getAppId,
@@ -80,6 +84,7 @@ async function initDeployedApp(prodAppId: any) {
80
84
  })
81
85
  )
82
86
  ).rows.map((row: any) => row.doc)
87
+ await clearMetadata()
83
88
  console.log("You have " + automations.length + " automations")
84
89
  const promises = []
85
90
  console.log("Disabling prod crons..")
@@ -246,7 +246,7 @@ async function execute(
246
246
  }
247
247
 
248
248
  export async function executeV1(ctx: any) {
249
- return execute(ctx, { rowsOnly: true })
249
+ return execute(ctx, { rowsOnly: true, isAutomation: false })
250
250
  }
251
251
 
252
252
  export async function executeV2(
@@ -1,4 +1,4 @@
1
- const joiValidator = require("../../../middleware/joi-validator")
1
+ const { joiValidator } = require("@budibase/backend-core/auth")
2
2
  const Joi = require("joi")
3
3
 
4
4
  const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("")
@@ -22,7 +22,7 @@ exports.queryValidation = () => {
22
22
  schema: Joi.object({}).required().unknown(true),
23
23
  transformer: OPTIONAL_STRING,
24
24
  flags: Joi.object().optional(),
25
- })
25
+ }).unknown(true)
26
26
  }
27
27
 
28
28
  exports.generateQueryValidation = () => {
@@ -46,5 +46,5 @@ exports.generateQueryPreviewValidation = () => {
46
46
  transformer: OPTIONAL_STRING,
47
47
  parameters: Joi.object({}).required().unknown(true),
48
48
  queryId: OPTIONAL_STRING,
49
- }))
49
+ }).unknown(true))
50
50
  }
@@ -19,6 +19,7 @@ class QueryBuilder {
19
19
  empty: {},
20
20
  notEmpty: {},
21
21
  oneOf: {},
22
+ contains: {},
22
23
  ...base,
23
24
  }
24
25
  this.limit = 50
@@ -119,6 +120,11 @@ class QueryBuilder {
119
120
  return this
120
121
  }
121
122
 
123
+ addContains(key, value) {
124
+ this.query.contains[key] = value
125
+ return this
126
+ }
127
+
122
128
  /**
123
129
  * Preprocesses a value before going into a lucene search.
124
130
  * Transforms strings to lowercase and wraps strings and bools in quotes.
@@ -164,6 +170,31 @@ class QueryBuilder {
164
170
  return `${key}:${builder.preprocess(value, allPreProcessingOpts)}`
165
171
  }
166
172
 
173
+ const contains = (key, value) => {
174
+ if (!value && value !== 0) {
175
+ return null
176
+ }
177
+ return `${key}:${builder.preprocess(value, { escape: true })}`
178
+ }
179
+
180
+ const oneOf = (key, value) => {
181
+ if (!Array.isArray(value)) {
182
+ if (typeof value === "string") {
183
+ value = value.split(",")
184
+ } else {
185
+ return ""
186
+ }
187
+ }
188
+ let orStatement = `${builder.preprocess(value[0], allPreProcessingOpts)}`
189
+ for (let i = 1; i < value.length; i++) {
190
+ orStatement += ` OR ${builder.preprocess(
191
+ value[i],
192
+ allPreProcessingOpts
193
+ )}`
194
+ }
195
+ return `${key}:(${orStatement})`
196
+ }
197
+
167
198
  function build(structure, queryFn) {
168
199
  for (let [key, value] of Object.entries(structure)) {
169
200
  key = builder.preprocess(key.replace(/ /g, "_"), {
@@ -239,26 +270,10 @@ class QueryBuilder {
239
270
  build(this.query.notEmpty, key => `${key}:["" TO *]`)
240
271
  }
241
272
  if (this.query.oneOf) {
242
- build(this.query.oneOf, (key, value) => {
243
- if (!Array.isArray(value)) {
244
- if (typeof value === "string") {
245
- value = value.split(",")
246
- } else {
247
- return ""
248
- }
249
- }
250
- let orStatement = `${builder.preprocess(
251
- value[0],
252
- allPreProcessingOpts
253
- )}`
254
- for (let i = 1; i < value.length; i++) {
255
- orStatement += ` OR ${builder.preprocess(
256
- value[i],
257
- allPreProcessingOpts
258
- )}`
259
- }
260
- return `${key}:(${orStatement})`
261
- })
273
+ build(this.query.oneOf, oneOf)
274
+ }
275
+ if (this.query.contains) {
276
+ build(this.query.contains, contains)
262
277
  }
263
278
  // make sure table ID is always added as an AND
264
279
  if (tableId) {
@@ -23,6 +23,7 @@ describe("/users", () => {
23
23
  })
24
24
 
25
25
  describe("fetch", () => {
26
+
26
27
  it("returns a list of users from an instance db", async () => {
27
28
  await config.createUser("uuidx")
28
29
  await config.createUser("uuidy")
@@ -37,6 +38,7 @@ describe("/users", () => {
37
38
  expect(res.body.find(u => u._id === `ro_ta_users_us_uuidy`)).toBeDefined()
38
39
  })
39
40
 
41
+
40
42
  it("should apply authorization to endpoint", async () => {
41
43
  await config.createUser()
42
44
  await checkPermissionsEndpoint({
@@ -1,4 +1,4 @@
1
- const joiValidator = require("../../../middleware/joi-validator")
1
+ const { joiValidator } = require("@budibase/backend-core/auth")
2
2
  const { DataSourceOperation } = require("../../../constants")
3
3
  const { WebhookType } = require("../../../constants")
4
4
  const {
@@ -74,6 +74,7 @@ exports.run = async function ({ inputs, appId, emitter }) {
74
74
  try {
75
75
  await queryController.executeV2(ctx, { isAutomation: true })
76
76
  const { data, ...rest } = ctx.body
77
+
77
78
  return {
78
79
  response: data,
79
80
  info: rest,
@@ -31,7 +31,7 @@ describe("Run through some parts of the automations system", () => {
31
31
  it("should be able to init in builder", async () => {
32
32
  await triggers.externalTrigger(basicAutomation(), { a: 1 })
33
33
  await wait(100)
34
- expect(thread).toHaveBeenCalled()
34
+ expect(thread.execute).toHaveBeenCalled()
35
35
  })
36
36
 
37
37
  it("should be able to init in prod", async () => {
@@ -52,7 +52,7 @@ describe("Run through some parts of the automations system", () => {
52
52
  }
53
53
  })
54
54
  await wait(100)
55
- expect(thread).toHaveBeenCalledWith(makePartial({
55
+ expect(thread.execute).toHaveBeenCalledWith(makePartial({
56
56
  data: {
57
57
  event: {
58
58
  fields: {
@@ -6,10 +6,16 @@ import newid from "../db/newid"
6
6
  import { updateEntityMetadata } from "../utilities"
7
7
  import { MetadataTypes, WebhookType } from "../constants"
8
8
  import { getProdAppID, doWithDB } from "@budibase/backend-core/db"
9
+ import { getAutomationMetadataParams } from "../db/utils"
9
10
  import { cloneDeep } from "lodash/fp"
10
- import { getAppDB, getAppId } from "@budibase/backend-core/context"
11
+ import {
12
+ getAppDB,
13
+ getAppId,
14
+ getProdAppDB,
15
+ } from "@budibase/backend-core/context"
11
16
  import { tenancy } from "@budibase/backend-core"
12
17
  import { quotas } from "@budibase/pro"
18
+ import { Automation } from "@budibase/types"
13
19
 
14
20
  const WH_STEP_ID = definitions.WEBHOOK.stepId
15
21
  const CRON_STEP_ID = definitions.CRON.stepId
@@ -82,6 +88,26 @@ export async function disableAllCrons(appId: any) {
82
88
  return Promise.all(promises)
83
89
  }
84
90
 
91
+ export async function disableCron(jobId: string, jobKey: string) {
92
+ await queue.removeRepeatableByKey(jobKey)
93
+ await queue.removeJobs(jobId)
94
+ }
95
+
96
+ export async function clearMetadata() {
97
+ const db = getProdAppDB()
98
+ const automationMetadata = (
99
+ await db.allDocs(
100
+ getAutomationMetadataParams({
101
+ include_docs: true,
102
+ })
103
+ )
104
+ ).rows.map((row: any) => row.doc)
105
+ for (let metadata of automationMetadata) {
106
+ metadata._deleted = true
107
+ }
108
+ await db.bulkDocs(automationMetadata)
109
+ }
110
+
85
111
  /**
86
112
  * This function handles checking of any cron jobs that need to be enabled/updated.
87
113
  * @param {string} appId The ID of the app in which we are checking for webhooks
@@ -204,3 +230,30 @@ export async function checkForWebhooks({ oldAuto, newAuto }: any) {
204
230
  export async function cleanupAutomations(appId: any) {
205
231
  await disableAllCrons(appId)
206
232
  }
233
+
234
+ /**
235
+ * Checks if the supplied automation is of a recurring type.
236
+ * @param automation The automation to check.
237
+ * @return {boolean} if it is recurring (cron).
238
+ */
239
+ export function isRecurring(automation: Automation) {
240
+ return automation.definition.trigger.stepId === definitions.CRON.stepId
241
+ }
242
+
243
+ export function isErrorInOutput(output: {
244
+ steps: { outputs?: { success: boolean } }[]
245
+ }) {
246
+ let first = true,
247
+ error = false
248
+ for (let step of output.steps) {
249
+ // skip the trigger, its always successful if automation ran
250
+ if (first) {
251
+ first = false
252
+ continue
253
+ }
254
+ if (!step.outputs?.success) {
255
+ error = true
256
+ }
257
+ }
258
+ return error
259
+ }
@@ -208,10 +208,7 @@ exports.AutomationErrors = {
208
208
  FAILURE_CONDITION: "FAILURE_CONDITION_MET",
209
209
  }
210
210
 
211
- exports.LoopStepTypes = {
212
- ARRAY: "Array",
213
- STRING: "String",
214
- }
215
-
216
211
  // pass through the list from the auth/core lib
217
212
  exports.ObjectStoreBuckets = ObjectStoreBuckets
213
+
214
+ exports.MAX_AUTOMATION_RECURRING_ERRORS = 5
package/src/db/utils.js CHANGED
@@ -41,6 +41,7 @@ const DocumentTypes = {
41
41
  METADATA: "metadata",
42
42
  MEM_VIEW: "view",
43
43
  USER_FLAG: "flag",
44
+ AUTOMATION_METADATA: "meta_au",
44
45
  }
45
46
 
46
47
  const InternalTables = {
@@ -311,6 +312,21 @@ exports.generateQueryID = datasourceId => {
311
312
  }${SEPARATOR}${datasourceId}${SEPARATOR}${newid()}`
312
313
  }
313
314
 
315
+ /**
316
+ * Generates a metadata ID for automations, used to track errors in recurring
317
+ * automations etc.
318
+ */
319
+ exports.generateAutomationMetadataID = automationId => {
320
+ return `${DocumentTypes.AUTOMATION_METADATA}${SEPARATOR}${automationId}`
321
+ }
322
+
323
+ /**
324
+ * Retrieve all automation metadata in an app database.
325
+ */
326
+ exports.getAutomationMetadataParams = (otherProps = {}) => {
327
+ return getDocParams(DocumentTypes.AUTOMATION_METADATA, null, otherProps)
328
+ }
329
+
314
330
  /**
315
331
  * Gets parameters for retrieving a query, this is a utility function for the getDocParams function.
316
332
  */
@@ -0,0 +1,49 @@
1
+ import {
2
+ Automation,
3
+ AutomationResults,
4
+ AutomationStep,
5
+ Document,
6
+ } from "@budibase/types"
7
+
8
+ export enum LoopStepTypes {
9
+ ARRAY = "Array",
10
+ STRING = "String",
11
+ }
12
+
13
+ export interface LoopStep extends AutomationStep {
14
+ inputs: {
15
+ option: LoopStepTypes
16
+ [key: string]: any
17
+ }
18
+ }
19
+
20
+ export interface LoopInput {
21
+ binding: string[] | string
22
+ }
23
+
24
+ export interface TriggerOutput {
25
+ metadata?: any
26
+ appId?: string
27
+ timestamp?: number
28
+ }
29
+
30
+ export interface AutomationEvent {
31
+ data: {
32
+ automation: Automation
33
+ event: any
34
+ }
35
+ opts?: {
36
+ repeat?: {
37
+ jobId: string
38
+ }
39
+ }
40
+ }
41
+
42
+ export interface AutomationContext extends AutomationResults {
43
+ steps: any[]
44
+ trigger: any
45
+ }
46
+
47
+ export interface AutomationMetadata extends Document {
48
+ errorCount?: number
49
+ }
@@ -131,6 +131,9 @@ export interface SearchFilters {
131
131
  oneOf?: {
132
132
  [key: string]: any[]
133
133
  }
134
+ contains?: {
135
+ [key: string]: any
136
+ }
134
137
  }
135
138
 
136
139
  export interface SortJson {
@@ -142,6 +142,21 @@ class InternalBuilder {
142
142
  }
143
143
  }
144
144
  }
145
+
146
+ const like = (key: string, value: any) => {
147
+ const fnc = allOr ? "orWhere" : "where"
148
+ // postgres supports ilike, nothing else does
149
+ if (this.client === SqlClients.POSTGRES) {
150
+ query = query[fnc](key, "ilike", `%${value}%`)
151
+ } else {
152
+ const rawFnc = `${fnc}Raw`
153
+ // @ts-ignore
154
+ query = query[rawFnc](`LOWER(${likeKey(this.client, key)}) LIKE ?`, [
155
+ `%${value}%`,
156
+ ])
157
+ }
158
+ }
159
+
145
160
  if (!filters) {
146
161
  return query
147
162
  }
@@ -168,19 +183,7 @@ class InternalBuilder {
168
183
  })
169
184
  }
170
185
  if (filters.fuzzy) {
171
- iterate(filters.fuzzy, (key, value) => {
172
- const fnc = allOr ? "orWhere" : "where"
173
- // postgres supports ilike, nothing else does
174
- if (this.client === SqlClients.POSTGRES) {
175
- query = query[fnc](key, "ilike", `%${value}%`)
176
- } else {
177
- const rawFnc = `${fnc}Raw`
178
- // @ts-ignore
179
- query = query[rawFnc](`LOWER(${likeKey(this.client, key)}) LIKE ?`, [
180
- `%${value}%`,
181
- ])
182
- }
183
- })
186
+ iterate(filters.fuzzy, like)
184
187
  }
185
188
  if (filters.range) {
186
189
  iterate(filters.range, (key, value) => {
@@ -223,6 +226,34 @@ class InternalBuilder {
223
226
  query = query[fnc](key)
224
227
  })
225
228
  }
229
+ if (filters.contains) {
230
+ const fnc = allOr ? "orWhere" : "where"
231
+ const rawFnc = `${fnc}Raw`
232
+ if (this.client === SqlClients.POSTGRES) {
233
+ iterate(filters.contains, (key: string, value: any) => {
234
+ const fieldNames = key.split(/\./g)
235
+ const tableName = fieldNames[0]
236
+ const columnName = fieldNames[1]
237
+ if (typeof value === "string") {
238
+ value = `"${value}"`
239
+ }
240
+ // @ts-ignore
241
+ query = query[rawFnc](
242
+ `"${tableName}"."${columnName}"::jsonb @> '[${value}]'`
243
+ )
244
+ })
245
+ } else if (this.client === SqlClients.MY_SQL) {
246
+ iterate(filters.contains, (key: string, value: any) => {
247
+ if (typeof value === "string") {
248
+ value = `"${value}"`
249
+ }
250
+ // @ts-ignore
251
+ query = query[rawFnc](`JSON_CONTAINS(${key}, '${value}')`)
252
+ })
253
+ } else {
254
+ iterate(filters.contains, like)
255
+ }
256
+ }
226
257
  return query
227
258
  }
228
259
 
@@ -1,4 +1,5 @@
1
1
  const Sql = require("../base/sql")
2
+ const { SqlClients } = require("../utils")
2
3
 
3
4
  const TABLE_NAME = "test"
4
5
 
@@ -46,7 +47,7 @@ function generateDeleteJson(table = TABLE_NAME, filters = {}) {
46
47
 
47
48
  describe("SQL query builder", () => {
48
49
  const limit = 500
49
- const client = "pg"
50
+ const client = SqlClients.POSTGRES
50
51
  let sql
51
52
 
52
53
  beforeEach(() => {
@@ -173,15 +174,15 @@ describe("SQL query builder", () => {
173
174
  })
174
175
 
175
176
  it("should work with MS-SQL", () => {
176
- const query = new Sql("mssql", 10)._query(generateReadJson())
177
+ const query = new Sql(SqlClients.MS_SQL, 10)._query(generateReadJson())
177
178
  expect(query).toEqual({
178
179
  bindings: [10],
179
180
  sql: `select * from (select top (@p0) * from [${TABLE_NAME}]) as [${TABLE_NAME}]`
180
181
  })
181
182
  })
182
183
 
183
- it("should work with mySQL", () => {
184
- const query = new Sql("mysql", 10)._query(generateReadJson())
184
+ it("should work with MySQL", () => {
185
+ const query = new Sql(SqlClients.MY_SQL, 10)._query(generateReadJson())
185
186
  expect(query).toEqual({
186
187
  bindings: [10],
187
188
  sql: `select * from (select * from \`${TABLE_NAME}\` limit ?) as \`${TABLE_NAME}\``
@@ -238,4 +239,49 @@ describe("SQL query builder", () => {
238
239
  sql: `select * from (select * from "${TABLE_NAME}" where "${TABLE_NAME}"."property" > $1 limit $2) as "${TABLE_NAME}"`
239
240
  })
240
241
  })
242
+
243
+ it("should use like expression for MS-SQL when filter is contains", () => {
244
+ const query = new Sql(SqlClients.MS_SQL, 10)._query(generateReadJson({
245
+ filters: {
246
+ contains: {
247
+ age: 20,
248
+ name: "John"
249
+ }
250
+ }
251
+ }))
252
+ expect(query).toEqual({
253
+ bindings: [10, "%20%", "%John%"],
254
+ sql: `select * from (select top (@p0) * from [${TABLE_NAME}] where LOWER(${TABLE_NAME}.age) LIKE @p1 and LOWER(${TABLE_NAME}.name) LIKE @p2) as [${TABLE_NAME}]`
255
+ })
256
+ })
257
+
258
+ it("should use JSON_CONTAINS expression for MySQL when filter is contains", () => {
259
+ const query = new Sql(SqlClients.MY_SQL, 10)._query(generateReadJson({
260
+ filters: {
261
+ contains: {
262
+ age: 20,
263
+ name: "John"
264
+ }
265
+ }
266
+ }))
267
+ expect(query).toEqual({
268
+ bindings: [10],
269
+ sql: `select * from (select * from \`${TABLE_NAME}\` where JSON_CONTAINS(${TABLE_NAME}.age, '20') and JSON_CONTAINS(${TABLE_NAME}.name, '"John"') limit ?) as \`${TABLE_NAME}\``
270
+ })
271
+ })
272
+
273
+ it("should use jsonb operator expression for PostgreSQL when filter is contains", () => {
274
+ const query = new Sql(SqlClients.POSTGRES, 10)._query(generateReadJson({
275
+ filters: {
276
+ contains: {
277
+ age: 20,
278
+ name: "John"
279
+ }
280
+ }
281
+ }))
282
+ expect(query).toEqual({
283
+ bindings: [10],
284
+ sql: `select * from (select * from \"${TABLE_NAME}\" where \"${TABLE_NAME}\".\"age\"::jsonb @> '[20]' and \"${TABLE_NAME}\".\"name\"::jsonb @> '["John"]' limit $1) as \"${TABLE_NAME}\"`
285
+ })
286
+ })
241
287
  })
@@ -1,5 +1,6 @@
1
1
  mockAuthWithNoCookie()
2
2
  mockWorker()
3
+ mockUserGroups()
3
4
 
4
5
  jest.mock("@budibase/backend-core/db", () => {
5
6
  const coreDb = jest.requireActual("@budibase/backend-core/db")
@@ -29,6 +30,16 @@ function mockReset() {
29
30
  mockWorker()
30
31
  }
31
32
 
33
+ function mockUserGroups() {
34
+ jest.mock("@budibase/pro", () => ({
35
+ groups: {
36
+ getGroupRoleId: () => {
37
+ return "BASIC"
38
+ },
39
+ },
40
+ }))
41
+ }
42
+
32
43
  function mockAuthWithNoCookie() {
33
44
  jest.resetModules()
34
45
  mockWorker()