@budibase/server 1.0.219 → 1.0.220-alpha.2

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 (284) hide show
  1. package/.yarnrc +1 -0
  2. package/__mocks__/node-fetch.ts +9 -0
  3. package/builder/assets/index.7786f497.css +6 -0
  4. package/builder/assets/index.e1885c5f.js +974 -0
  5. package/builder/assets/{vendor.9bf2fcb4.js → vendor.001dcf8c.js} +163 -163
  6. package/builder/index.html +3 -3
  7. package/coverage/clover.xml +1018 -950
  8. package/coverage/coverage-final.json +31 -31
  9. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  10. package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
  11. package/coverage/lcov-report/api/controllers/automation.js.html +111 -12
  12. package/coverage/lcov-report/api/controllers/backup.js.html +9 -6
  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 +16 -16
  16. package/coverage/lcov-report/api/controllers/dev.js.html +13 -166
  17. package/coverage/lcov-report/api/controllers/index.html +32 -32
  18. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/migrations.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 +1 -1
  24. package/coverage/lcov-report/api/controllers/query/validation.js.html +1 -1
  25. package/coverage/lcov-report/api/controllers/role.js.html +1 -1
  26. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/row/external.js.html +47 -17
  28. package/coverage/lcov-report/api/controllers/row/index.html +33 -33
  29. package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
  30. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +159 -21
  31. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +1 -1
  32. package/coverage/lcov-report/api/controllers/row/utils.js.html +17 -8
  33. package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
  34. package/coverage/lcov-report/api/controllers/script.js.html +1 -1
  35. package/coverage/lcov-report/api/controllers/table/bulkFormula.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/templates.js.html +1 -1
  40. package/coverage/lcov-report/api/controllers/user.js.html +1 -1
  41. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  42. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  43. package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
  44. package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
  45. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
  46. package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
  47. package/coverage/lcov-report/api/index.html +15 -15
  48. package/coverage/lcov-report/api/index.js.html +22 -13
  49. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  50. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  51. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/automation.js.html +32 -2
  53. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/dev.js.html +2 -5
  59. package/coverage/lcov-report/api/routes/index.html +1 -1
  60. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/migrations.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/utils/index.html +1 -1
  74. package/coverage/lcov-report/api/routes/utils/validators.js.html +5 -2
  75. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  77. package/coverage/lcov-report/automations/actions.js.html +1 -1
  78. package/coverage/lcov-report/automations/automationUtils.js.html +57 -12
  79. package/coverage/lcov-report/automations/bullboard.js.html +1 -1
  80. package/coverage/lcov-report/automations/index.html +17 -17
  81. package/coverage/lcov-report/automations/index.js.html +1 -1
  82. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  83. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  84. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  85. package/coverage/lcov-report/automations/steps/executeQuery.js.html +2 -2
  86. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  87. package/coverage/lcov-report/automations/steps/filter.js.html +58 -34
  88. package/coverage/lcov-report/automations/steps/index.html +11 -11
  89. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/loop.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  96. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
  98. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  99. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  100. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  101. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  102. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  103. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  104. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  105. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  106. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  107. package/coverage/lcov-report/automations/triggers.js.html +4 -4
  108. package/coverage/lcov-report/constants/index.html +1 -1
  109. package/coverage/lcov-report/constants/index.js.html +1 -1
  110. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  111. package/coverage/lcov-report/constants/screens.js.html +1 -1
  112. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  113. package/coverage/lcov-report/db/index.html +14 -14
  114. package/coverage/lcov-report/db/index.js.html +1 -1
  115. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
  116. package/coverage/lcov-report/db/linkedRows/index.html +13 -13
  117. package/coverage/lcov-report/db/linkedRows/index.js.html +10 -7
  118. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  119. package/coverage/lcov-report/db/newid.js.html +2 -2
  120. package/coverage/lcov-report/db/utils.js.html +27 -51
  121. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  122. package/coverage/lcov-report/events/index.html +1 -1
  123. package/coverage/lcov-report/events/index.js.html +1 -1
  124. package/coverage/lcov-report/events/utils.js.html +1 -1
  125. package/coverage/lcov-report/index.html +83 -83
  126. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  127. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  128. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  129. package/coverage/lcov-report/middleware/builder.js.html +12 -12
  130. package/coverage/lcov-report/middleware/currentapp.js.html +33 -33
  131. package/coverage/lcov-report/middleware/index.html +1 -1
  132. package/coverage/lcov-report/middleware/joi-validator.js.html +10 -10
  133. package/coverage/lcov-report/middleware/publicApi.js.html +1 -1
  134. package/coverage/lcov-report/middleware/resourceId.js.html +7 -7
  135. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  136. package/coverage/lcov-report/middleware/utils.js.html +2 -2
  137. package/coverage/lcov-report/threads/automation.js.html +18 -9
  138. package/coverage/lcov-report/threads/index.html +25 -25
  139. package/coverage/lcov-report/threads/query.js.html +301 -79
  140. package/coverage/lcov-report/threads/utils.js.html +4 -4
  141. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  142. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  143. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  144. package/coverage/lcov-report/utilities/global.js.html +14 -14
  145. package/coverage/lcov-report/utilities/index.html +26 -26
  146. package/coverage/lcov-report/utilities/index.js.html +17 -32
  147. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +2 -2
  148. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  149. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  150. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  151. package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
  152. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
  153. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
  154. package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
  155. package/coverage/lcov-report/utilities/security.js.html +2 -2
  156. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  157. package/coverage/lcov-report/utilities/usageQuota/index.html +1 -1
  158. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +1 -1
  159. package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +1 -1
  160. package/coverage/lcov-report/utilities/users.js.html +1 -1
  161. package/coverage/lcov-report/utilities/workerRequests.js.html +57 -9
  162. package/coverage/lcov.info +1789 -1665
  163. package/dist/api/controllers/application.js +19 -16
  164. package/dist/api/controllers/automation.js +28 -2
  165. package/dist/api/controllers/backup.js +3 -2
  166. package/dist/api/controllers/dev.js +2 -40
  167. package/dist/api/controllers/public/applications.js +1 -1
  168. package/dist/api/controllers/public/utils.js +8 -1
  169. package/dist/api/controllers/query/index.js +31 -3
  170. package/dist/api/controllers/row/external.js +19 -7
  171. package/dist/api/controllers/row/internalSearch.js +47 -10
  172. package/dist/api/controllers/row/utils.js +3 -0
  173. package/dist/api/controllers/static/index.js +1 -5
  174. package/dist/api/index.js +4 -1
  175. package/dist/api/routes/automation.js +2 -0
  176. package/dist/api/routes/dev.js +1 -2
  177. package/dist/api/routes/public/rows.js +7 -2
  178. package/dist/api/routes/utils/validators.js +1 -0
  179. package/dist/app.js +20 -0
  180. package/dist/automations/automationUtils.js +16 -4
  181. package/dist/automations/logging/index.js +70 -0
  182. package/dist/automations/steps/executeQuery.js +1 -1
  183. package/dist/automations/steps/filter.js +38 -29
  184. package/dist/automations/triggers.js +1 -1
  185. package/dist/db/linkedRows/index.js +1 -0
  186. package/dist/db/utils.js +4 -31
  187. package/dist/definitions/datasource.js +1 -0
  188. package/dist/environment.js +3 -0
  189. package/dist/integrations/airtable.js +1 -0
  190. package/dist/integrations/arangodb.js +1 -0
  191. package/dist/integrations/base/sql.js +22 -3
  192. package/dist/integrations/couchdb.js +1 -0
  193. package/dist/integrations/dynamodb.js +1 -0
  194. package/dist/integrations/elasticsearch.js +1 -0
  195. package/dist/integrations/firebase.js +1 -0
  196. package/dist/integrations/googlesheets.js +1 -0
  197. package/dist/integrations/index.js +4 -0
  198. package/dist/integrations/microsoftSqlServer.js +2 -1
  199. package/dist/integrations/mongodb.js +1 -0
  200. package/dist/integrations/mysql.js +2 -1
  201. package/dist/integrations/oracle.js +1 -0
  202. package/dist/integrations/postgres.js +2 -1
  203. package/dist/integrations/queries/sql.js +3 -0
  204. package/dist/integrations/redis.js +1 -0
  205. package/dist/integrations/rest.js +2 -1
  206. package/dist/integrations/s3.js +1 -0
  207. package/dist/integrations/snowflake.js +102 -0
  208. package/dist/package.json +7 -6
  209. package/dist/tests/utilities/TestConfiguration.js +19 -6
  210. package/dist/threads/automation.js +6 -3
  211. package/dist/threads/index.js +15 -1
  212. package/dist/threads/query.js +65 -12
  213. package/dist/tsconfig.build.tsbuildinfo +1 -1
  214. package/dist/utilities/fileSystem/index.js +16 -6
  215. package/dist/utilities/index.js +1 -5
  216. package/dist/utilities/workerRequests.js +8 -0
  217. package/package.json +8 -7
  218. package/scripts/dev/manage.js +2 -0
  219. package/scripts/integrations/oracle/instantclient/linux/arm64/basiclite-19.10.zip +0 -0
  220. package/scripts/integrations/oracle/instantclient/linux/arm64/install.sh +23 -0
  221. package/scripts/integrations/oracle/instantclient/linux/install.sh +10 -0
  222. package/specs/openapi.json +9 -5
  223. package/specs/openapi.yaml +10 -4
  224. package/src/api/controllers/application.ts +19 -19
  225. package/src/api/controllers/automation.js +35 -2
  226. package/src/api/controllers/backup.js +3 -2
  227. package/src/api/controllers/dev.js +2 -53
  228. package/src/api/controllers/public/applications.ts +2 -2
  229. package/src/api/controllers/public/utils.ts +8 -1
  230. package/src/api/controllers/query/index.ts +40 -4
  231. package/src/api/controllers/row/external.js +19 -9
  232. package/src/api/controllers/row/internalSearch.js +55 -9
  233. package/src/api/controllers/row/utils.js +3 -0
  234. package/src/api/controllers/static/index.js +1 -6
  235. package/src/api/index.js +4 -1
  236. package/src/api/routes/automation.js +10 -0
  237. package/src/api/routes/dev.js +0 -1
  238. package/src/api/routes/public/rows.ts +7 -2
  239. package/src/api/routes/tests/query.spec.js +166 -1
  240. package/src/api/routes/utils/validators.js +1 -0
  241. package/src/app.ts +30 -1
  242. package/src/automations/automationUtils.js +19 -4
  243. package/src/automations/logging/index.ts +35 -0
  244. package/src/automations/steps/executeQuery.js +1 -1
  245. package/src/automations/steps/filter.js +36 -28
  246. package/src/automations/tests/filter.spec.js +2 -1
  247. package/src/automations/triggers.js +1 -1
  248. package/src/db/linkedRows/index.js +1 -0
  249. package/src/db/utils.js +5 -13
  250. package/src/definitions/common.ts +1 -0
  251. package/src/definitions/datasource.ts +2 -0
  252. package/src/definitions/openapi.ts +471 -469
  253. package/src/environment.js +3 -0
  254. package/src/integrations/airtable.ts +1 -0
  255. package/src/integrations/arangodb.ts +1 -0
  256. package/src/integrations/base/sql.ts +29 -11
  257. package/src/integrations/couchdb.ts +1 -0
  258. package/src/integrations/dynamodb.ts +1 -0
  259. package/src/integrations/elasticsearch.ts +1 -0
  260. package/src/integrations/firebase.ts +1 -0
  261. package/src/integrations/googlesheets.ts +1 -0
  262. package/src/integrations/index.ts +4 -0
  263. package/src/integrations/microsoftSqlServer.ts +2 -0
  264. package/src/integrations/mongodb.ts +1 -0
  265. package/src/integrations/mysql.ts +2 -0
  266. package/src/integrations/oracle.ts +1 -0
  267. package/src/integrations/postgres.ts +2 -0
  268. package/src/integrations/queries/sql.ts +3 -1
  269. package/src/integrations/redis.ts +1 -0
  270. package/src/integrations/rest.ts +2 -1
  271. package/src/integrations/s3.ts +1 -0
  272. package/src/integrations/snowflake.ts +99 -0
  273. package/src/integrations/tests/rest.spec.js +16 -1
  274. package/src/migrations/tests/index.spec.ts +18 -2
  275. package/src/module.d.ts +2 -0
  276. package/src/tests/utilities/TestConfiguration.js +22 -3
  277. package/src/threads/automation.js +6 -3
  278. package/src/threads/index.ts +5 -1
  279. package/src/threads/query.js +88 -14
  280. package/src/utilities/fileSystem/index.js +22 -8
  281. package/src/utilities/index.js +1 -6
  282. package/src/utilities/workerRequests.js +16 -0
  283. package/builder/assets/index.1c640a53.js +0 -1316
  284. package/builder/assets/index.24307ca0.css +0 -6
@@ -10,6 +10,7 @@ const { getAppId } = require("@budibase/backend-core/context")
10
10
  class QueryBuilder {
11
11
  constructor(base) {
12
12
  this.query = {
13
+ allOr: false,
13
14
  string: {},
14
15
  fuzzy: {},
15
16
  range: {},
@@ -17,6 +18,7 @@ class QueryBuilder {
17
18
  notEqual: {},
18
19
  empty: {},
19
20
  notEmpty: {},
21
+ oneOf: {},
20
22
  ...base,
21
23
  }
22
24
  this.limit = 50
@@ -112,6 +114,11 @@ class QueryBuilder {
112
114
  return this
113
115
  }
114
116
 
117
+ addOneOf(key, value) {
118
+ this.query.oneOf[key] = value
119
+ return this
120
+ }
121
+
115
122
  /**
116
123
  * Preprocesses a value before going into a lucene search.
117
124
  * Transforms strings to lowercase and wraps strings and bools in quotes.
@@ -140,8 +147,22 @@ class QueryBuilder {
140
147
 
141
148
  buildSearchQuery() {
142
149
  const builder = this
143
- let query = "*:*"
150
+ let allOr = this.query && this.query.allOr
151
+ let query = allOr ? "" : "*:*"
144
152
  const allPreProcessingOpts = { escape: true, lowercase: true, wrap: true }
153
+ let tableId
154
+ if (this.query.equal.tableId) {
155
+ tableId = this.query.equal.tableId
156
+ delete this.query.equal.tableId
157
+ }
158
+
159
+ const equal = (key, value) => {
160
+ // 0 evaluates to false, which means we would return all rows if we don't check it
161
+ if (!value && value !== 0) {
162
+ return null
163
+ }
164
+ return `${key}:${builder.preprocess(value, allPreProcessingOpts)}`
165
+ }
145
166
 
146
167
  function build(structure, queryFn) {
147
168
  for (let [key, value] of Object.entries(structure)) {
@@ -152,7 +173,10 @@ class QueryBuilder {
152
173
  if (expression == null) {
153
174
  continue
154
175
  }
155
- query += ` AND ${expression}`
176
+ if (query.length > 0) {
177
+ query += ` ${allOr ? "OR" : "AND"} `
178
+ }
179
+ query += expression
156
180
  }
157
181
  }
158
182
 
@@ -198,13 +222,7 @@ class QueryBuilder {
198
222
  })
199
223
  }
200
224
  if (this.query.equal) {
201
- build(this.query.equal, (key, value) => {
202
- // 0 evaluates to false, which means we would return all rows if we don't check it
203
- if (!value && value !== 0) {
204
- return null
205
- }
206
- return `${key}:${builder.preprocess(value, allPreProcessingOpts)}`
207
- })
225
+ build(this.query.equal, equal)
208
226
  }
209
227
  if (this.query.notEqual) {
210
228
  build(this.query.notEqual, (key, value) => {
@@ -220,6 +238,34 @@ class QueryBuilder {
220
238
  if (this.query.notEmpty) {
221
239
  build(this.query.notEmpty, key => `${key}:["" TO *]`)
222
240
  }
241
+ 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
+ })
262
+ }
263
+ // make sure table ID is always added as an AND
264
+ if (tableId) {
265
+ query = `(${query})`
266
+ allOr = false
267
+ build({ tableId }, equal)
268
+ }
223
269
  return query
224
270
  }
225
271
 
@@ -64,6 +64,9 @@ exports.validate = async ({ tableId, row, table }) => {
64
64
  // Validate.js doesn't seem to handle array
65
65
  if (type === FieldTypes.ARRAY && row[fieldName]) {
66
66
  if (row[fieldName].length) {
67
+ if (!Array.isArray(row[fieldName])) {
68
+ row[fieldName] = row[fieldName].split(",")
69
+ }
67
70
  row[fieldName].map(val => {
68
71
  if (
69
72
  !constraints.inclusion.includes(val) &&
@@ -73,12 +73,7 @@ exports.toggleBetaUiFeature = async function (ctx) {
73
73
  }
74
74
 
75
75
  exports.serveBuilder = async function (ctx) {
76
- // Temporary: New Design UI
77
- const designUiCookie = ctx.cookies.get("beta:design_ui")
78
- // TODO: get this from the tmp Dir that we downloaded from MinIO
79
- const uiPath = designUiCookie ? "new_design_ui" : "builder"
80
-
81
- let builderPath = resolve(TOP_LEVEL_PATH, uiPath)
76
+ const builderPath = resolve(TOP_LEVEL_PATH, "builder")
82
77
  await send(ctx, ctx.file, { root: builderPath })
83
78
  if (!ctx.file.includes("assets/")) {
84
79
  await events.serve.servedBuilder()
package/src/api/index.js CHANGED
@@ -72,7 +72,10 @@ router.use(async (ctx, next) => {
72
72
  error,
73
73
  }
74
74
  ctx.log.error(err)
75
- console.trace(err)
75
+ // unauthorised errors don't provide a useful trace
76
+ if (!env.isTest()) {
77
+ console.trace(err)
78
+ }
76
79
  }
77
80
  })
78
81
 
@@ -51,6 +51,16 @@ router
51
51
  automationValidator(false),
52
52
  controller.create
53
53
  )
54
+ .post(
55
+ "/api/automations/logs/search",
56
+ authorized(BUILDER),
57
+ controller.logSearch
58
+ )
59
+ .delete(
60
+ "/api/automations/logs",
61
+ authorized(BUILDER),
62
+ controller.clearLogError
63
+ )
54
64
  .delete(
55
65
  "/api/automations/:id/:rev",
56
66
  paramResource("id"),
@@ -22,6 +22,5 @@ router
22
22
  .get("/api/dev/version", authorized(BUILDER), controller.getBudibaseVersion)
23
23
  .delete("/api/dev/:appId/lock", authorized(BUILDER), controller.clearLock)
24
24
  .post("/api/dev/:appId/revert", authorized(BUILDER), controller.revert)
25
- .get("/api/beta/access", controller.checkBetaAccess)
26
25
 
27
26
  module.exports = router
@@ -150,6 +150,11 @@ read.push(new Endpoint("get", "/tables/:tableId/rows/:rowId", controller.read))
150
150
  * query:
151
151
  * type: object
152
152
  * properties:
153
+ * allOr:
154
+ * type: boolean
155
+ * description: Specifies that a row should be returned if it satisfies
156
+ * any of the specified options, rather than requiring it to fulfill all
157
+ * the search parameters. This defaults to false, meaning AND logic will be used.
153
158
  * string:
154
159
  * type: object
155
160
  * example:
@@ -167,9 +172,9 @@ read.push(new Endpoint("get", "/tables/:tableId/rows/:rowId", controller.read))
167
172
  * range:
168
173
  * type: object
169
174
  * description: Searches within a range, the format of this must be
170
- * columnName -> [low, high].
175
+ * in the format of an object with a "low" and "high" property.
171
176
  * example:
172
- * columnName1: [10, 20]
177
+ * columnName1: { low: 10, high: 20 }
173
178
  * equal:
174
179
  * type: object
175
180
  * description: Searches for rows that have a column value that is
@@ -218,7 +218,6 @@ describe("/queries", () => {
218
218
  expect(res.body.schemaFields).toEqual(["a", "b"])
219
219
  expect(res.body.rows.length).toEqual(1)
220
220
  expect(events.query.previewed).toBeCalledTimes(1)
221
- datasource.config = { schema: "public" }
222
221
  expect(events.query.previewed).toBeCalledWith(datasource, query)
223
222
  })
224
223
 
@@ -346,4 +345,170 @@ describe("/queries", () => {
346
345
  expect(contents).toBe(null)
347
346
  })
348
347
  })
348
+
349
+ describe("Current User Request Mapping", () => {
350
+
351
+ async function previewGet(datasource, fields, params) {
352
+ return config.previewQuery(request, config, datasource, fields, params)
353
+ }
354
+
355
+ async function previewPost(datasource, fields, params) {
356
+ return config.previewQuery(request, config, datasource, fields, params, "create")
357
+ }
358
+
359
+ it("should parse global and query level header mappings", async () => {
360
+ const userDetails = config.getUserDetails()
361
+
362
+ const datasource = await config.restDatasource({
363
+ defaultHeaders: {
364
+ "test": "headerVal",
365
+ "emailHdr": "{{[user].[email]}}"
366
+ }
367
+ })
368
+ const res = await previewGet(datasource, {
369
+ path: "www.google.com",
370
+ queryString: "email={{[user].[email]}}",
371
+ headers: {
372
+ queryHdr : "{{[user].[firstName]}}",
373
+ secondHdr : "1234"
374
+ }
375
+ })
376
+
377
+ const parsedRequest = JSON.parse(res.body.extra.raw)
378
+ expect(parsedRequest.opts.headers).toEqual({
379
+ "test": "headerVal",
380
+ "emailHdr": userDetails.email,
381
+ "queryHdr": userDetails.firstName,
382
+ "secondHdr" : "1234"
383
+ })
384
+ expect(res.body.rows[0].url).toEqual("http://www.google.com?email=" + userDetails.email)
385
+ })
386
+
387
+ it("should bind the current user to query parameters", async () => {
388
+ const userDetails = config.getUserDetails()
389
+
390
+ const datasource = await config.restDatasource()
391
+
392
+ const res = await previewGet(datasource, {
393
+ path: "www.google.com",
394
+ queryString: "test={{myEmail}}&testName={{myName}}&testParam={{testParam}}",
395
+ }, {
396
+ "myEmail" : "{{[user].[email]}}",
397
+ "myName" : "{{[user].[firstName]}}",
398
+ "testParam" : "1234"
399
+ })
400
+
401
+ expect(res.body.rows[0].url).toEqual("http://www.google.com?test=" + userDetails.email +
402
+ "&testName=" + userDetails.firstName + "&testParam=1234")
403
+ })
404
+
405
+ it("should bind the current user the request body - plain text", async () => {
406
+ const userDetails = config.getUserDetails()
407
+ const datasource = await config.restDatasource()
408
+
409
+ const res = await previewPost(datasource, {
410
+ path: "www.google.com",
411
+ queryString: "testParam={{testParam}}",
412
+ requestBody: "This is plain text and this is my email: {{[user].[email]}}. This is a test param: {{testParam}}",
413
+ bodyType: "text"
414
+ }, {
415
+ "testParam" : "1234"
416
+ })
417
+
418
+ const parsedRequest = JSON.parse(res.body.extra.raw)
419
+ expect(parsedRequest.opts.body).toEqual(`This is plain text and this is my email: ${userDetails.email}. This is a test param: 1234`)
420
+ expect(res.body.rows[0].url).toEqual("http://www.google.com?testParam=1234")
421
+ })
422
+
423
+ it("should bind the current user the request body - json", async () => {
424
+ const userDetails = config.getUserDetails()
425
+ const datasource = await config.restDatasource()
426
+
427
+ const res = await previewPost(datasource, {
428
+ path: "www.google.com",
429
+ queryString: "testParam={{testParam}}",
430
+ requestBody: "{\"email\":\"{{[user].[email]}}\",\"queryCode\":{{testParam}},\"userRef\":\"{{userRef}}\"}",
431
+ bodyType: "json"
432
+ }, {
433
+ "testParam" : "1234",
434
+ "userRef" : "{{[user].[firstName]}}"
435
+ })
436
+
437
+ const parsedRequest = JSON.parse(res.body.extra.raw)
438
+ const test = `{"email":"${userDetails.email}","queryCode":1234,"userRef":"${userDetails.firstName}"}`
439
+ expect(parsedRequest.opts.body).toEqual(test)
440
+ expect(res.body.rows[0].url).toEqual("http://www.google.com?testParam=1234")
441
+ })
442
+
443
+ it("should bind the current user the request body - xml", async () => {
444
+ const userDetails = config.getUserDetails()
445
+ const datasource = await config.restDatasource()
446
+
447
+ const res = await previewPost(datasource, {
448
+ path: "www.google.com",
449
+ queryString: "testParam={{testParam}}",
450
+ requestBody: "<note> <email>{{[user].[email]}}</email> <code>{{testParam}}</code> " +
451
+ "<ref>{{userId}}</ref> <somestring>testing</somestring> </note>",
452
+ bodyType: "xml"
453
+ }, {
454
+ "testParam" : "1234",
455
+ "userId" : "{{[user].[firstName]}}"
456
+ })
457
+
458
+ const parsedRequest = JSON.parse(res.body.extra.raw)
459
+ const test = `<note> <email>${userDetails.email}</email> <code>1234</code> <ref>${userDetails.firstName}</ref> <somestring>testing</somestring> </note>`
460
+
461
+ expect(parsedRequest.opts.body).toEqual(test)
462
+ expect(res.body.rows[0].url).toEqual("http://www.google.com?testParam=1234")
463
+ })
464
+
465
+ it("should bind the current user the request body - form-data", async () => {
466
+ const userDetails = config.getUserDetails()
467
+ const datasource = await config.restDatasource()
468
+
469
+ const res = await previewPost(datasource, {
470
+ path: "www.google.com",
471
+ queryString: "testParam={{testParam}}",
472
+ requestBody: "{\"email\":\"{{[user].[email]}}\",\"queryCode\":{{testParam}},\"userRef\":\"{{userRef}}\"}",
473
+ bodyType: "form"
474
+ }, {
475
+ "testParam" : "1234",
476
+ "userRef" : "{{[user].[firstName]}}"
477
+ })
478
+
479
+ const parsedRequest = JSON.parse(res.body.extra.raw)
480
+
481
+ const emailData = parsedRequest.opts.body._streams[1]
482
+ expect(emailData).toEqual(userDetails.email)
483
+
484
+ const queryCodeData = parsedRequest.opts.body._streams[4]
485
+ expect(queryCodeData).toEqual("1234")
486
+
487
+ const userRef = parsedRequest.opts.body._streams[7]
488
+ expect(userRef).toEqual(userDetails.firstName)
489
+
490
+ expect(res.body.rows[0].url).toEqual("http://www.google.com?testParam=1234")
491
+ })
492
+
493
+ it("should bind the current user the request body - encoded", async () => {
494
+ const userDetails = config.getUserDetails()
495
+ const datasource = await config.restDatasource()
496
+
497
+ const res = await previewPost(datasource, {
498
+ path: "www.google.com",
499
+ queryString: "testParam={{testParam}}",
500
+ requestBody: "{\"email\":\"{{[user].[email]}}\",\"queryCode\":{{testParam}},\"userRef\":\"{{userRef}}\"}",
501
+ bodyType: "encoded"
502
+ }, {
503
+ "testParam" : "1234",
504
+ "userRef" : "{{[user].[firstName]}}"
505
+ })
506
+ const parsedRequest = JSON.parse(res.body.extra.raw)
507
+
508
+ expect(parsedRequest.opts.body.email).toEqual(userDetails.email)
509
+ expect(parsedRequest.opts.body.queryCode).toEqual("1234")
510
+ expect(parsedRequest.opts.body.userRef).toEqual(userDetails.firstName)
511
+ })
512
+
513
+ });
349
514
  })
@@ -58,6 +58,7 @@ function filterObject() {
58
58
  oneOf: Joi.object().optional(),
59
59
  contains: Joi.object().optional(),
60
60
  notContains: Joi.object().optional(),
61
+ allOr: Joi.boolean().optional(),
61
62
  }).unknown(true)
62
63
  }
63
64
 
package/src/app.ts CHANGED
@@ -18,7 +18,9 @@ const { logAlert } = require("@budibase/backend-core/logging")
18
18
  const { Thread } = require("./threads")
19
19
  import redis from "./utilities/redis"
20
20
  import * as migrations from "./migrations"
21
- import { events, installation } from "@budibase/backend-core"
21
+ import { events, installation, tenancy } from "@budibase/backend-core"
22
+ import { createAdminUser, getChecklist } from "./utilities/workerRequests"
23
+ import { tenantSucceeded } from "@budibase/backend-core/dist/src/events/publishers/backfill"
22
24
 
23
25
  const app = new Koa()
24
26
 
@@ -110,6 +112,33 @@ module.exports = server.listen(env.PORT || 0, async () => {
110
112
  }
111
113
  }
112
114
 
115
+ // check and create admin user if required
116
+ if (
117
+ env.SELF_HOSTED &&
118
+ !env.MULTI_TENANCY &&
119
+ env.BB_ADMIN_USER_EMAIL &&
120
+ env.BB_ADMIN_USER_PASSWORD
121
+ ) {
122
+ const checklist = await getChecklist()
123
+ if (!checklist?.adminUser?.checked) {
124
+ try {
125
+ const tenantId = tenancy.getTenantId()
126
+ await createAdminUser(
127
+ env.BB_ADMIN_USER_EMAIL,
128
+ env.BB_ADMIN_USER_PASSWORD,
129
+ tenantId
130
+ )
131
+ console.log(
132
+ "Admin account automatically created for",
133
+ env.BB_ADMIN_USER_EMAIL
134
+ )
135
+ } catch (e) {
136
+ logAlert("Error creating initial admin user. Exiting.", e)
137
+ shutdown()
138
+ }
139
+ }
140
+ }
141
+
113
142
  // check for version updates
114
143
  await installation.checkInstallVersion()
115
144
 
@@ -1,5 +1,10 @@
1
1
  const { getTable } = require("../api/controllers/table/utils")
2
- const { findHBSBlocks } = require("@budibase/string-templates")
2
+ const {
3
+ findHBSBlocks,
4
+ decodeJSBinding,
5
+ isJSBinding,
6
+ encodeJSBinding,
7
+ } = require("@budibase/string-templates")
3
8
 
4
9
  /**
5
10
  * When values are input to the system generally they will be of type string as this is required for template strings.
@@ -77,11 +82,21 @@ exports.getError = err => {
77
82
  }
78
83
 
79
84
  exports.substituteLoopStep = (hbsString, substitute) => {
80
- let blocks = findHBSBlocks(hbsString)
85
+ let blocks = []
86
+ let checkForJS = isJSBinding(hbsString)
87
+ if (checkForJS) {
88
+ hbsString = decodeJSBinding(hbsString)
89
+ blocks.push(hbsString)
90
+ } else {
91
+ blocks = findHBSBlocks(hbsString)
92
+ }
81
93
  for (let block of blocks) {
82
- let oldBlock = block
83
94
  block = block.replace(/loop/, substitute)
84
- hbsString = hbsString.replace(new RegExp(oldBlock, "g"), block)
95
+ if (checkForJS) {
96
+ hbsString = encodeJSBinding(block)
97
+ } else {
98
+ hbsString = block
99
+ }
85
100
  }
86
101
 
87
102
  return hbsString
@@ -0,0 +1,35 @@
1
+ import * as env from "../../environment"
2
+ import { AutomationResults, Automation, App } from "@budibase/types"
3
+ import { automations } from "@budibase/pro"
4
+ import { db as dbUtils } from "@budibase/backend-core"
5
+
6
+ export async function storeLog(
7
+ automation: Automation,
8
+ results: AutomationResults
9
+ ) {
10
+ // can disable this if un-needed in self-host, also only do this for prod apps
11
+ if (env.DISABLE_AUTOMATION_LOGS) {
12
+ return
13
+ }
14
+ await automations.logs.storeLog(automation, results)
15
+ }
16
+
17
+ export async function checkAppMetadata(apps: App[]) {
18
+ const maxStartDate = await automations.logs.oldestLogDate()
19
+ for (let metadata of apps) {
20
+ if (!metadata.automationErrors) {
21
+ continue
22
+ }
23
+ for (let [key, errors] of Object.entries(metadata.automationErrors)) {
24
+ const updated = []
25
+ for (let error of errors) {
26
+ const startDate = error.split(dbUtils.SEPARATOR)[2]
27
+ if (startDate > maxStartDate) {
28
+ updated.push(error)
29
+ }
30
+ }
31
+ metadata.automationErrors[key] = updated
32
+ }
33
+ }
34
+ return apps
35
+ }
@@ -72,7 +72,7 @@ exports.run = async function ({ inputs, appId, emitter }) {
72
72
  })
73
73
 
74
74
  try {
75
- await queryController.executeV2(ctx)
75
+ await queryController.executeV2(ctx, { isAutomation: true })
76
76
  const { data, ...rest } = ctx.body
77
77
  return {
78
78
  response: data,
@@ -50,43 +50,51 @@ exports.definition = {
50
50
  outputs: {
51
51
  properties: {
52
52
  success: {
53
+ type: "boolean",
54
+ description: "Whether the action was successful",
55
+ },
56
+ result: {
53
57
  type: "boolean",
54
58
  description: "Whether the logic block passed",
55
59
  },
56
60
  },
57
- required: ["success"],
61
+ required: ["success", "result"],
58
62
  },
59
63
  },
60
64
  }
61
65
 
62
66
  exports.run = async function filter({ inputs }) {
63
- let { field, condition, value } = inputs
64
- // coerce types so that we can use them
65
- if (!isNaN(value) && !isNaN(field)) {
66
- value = parseFloat(value)
67
- field = parseFloat(field)
68
- } else if (!isNaN(Date.parse(value)) && !isNaN(Date.parse(field))) {
69
- value = Date.parse(value)
70
- field = Date.parse(field)
71
- }
72
- let success = false
73
- if (typeof field !== "object" && typeof value !== "object") {
74
- switch (condition) {
75
- case FilterConditions.EQUAL:
76
- success = field === value
77
- break
78
- case FilterConditions.NOT_EQUAL:
79
- success = field !== value
80
- break
81
- case FilterConditions.GREATER_THAN:
82
- success = field > value
83
- break
84
- case FilterConditions.LESS_THAN:
85
- success = field < value
86
- break
67
+ try {
68
+ let { field, condition, value } = inputs
69
+ // coerce types so that we can use them
70
+ if (!isNaN(value) && !isNaN(field)) {
71
+ value = parseFloat(value)
72
+ field = parseFloat(field)
73
+ } else if (!isNaN(Date.parse(value)) && !isNaN(Date.parse(field))) {
74
+ value = Date.parse(value)
75
+ field = Date.parse(field)
76
+ }
77
+ let result = false
78
+ if (typeof field !== "object" && typeof value !== "object") {
79
+ switch (condition) {
80
+ case FilterConditions.EQUAL:
81
+ result = field === value
82
+ break
83
+ case FilterConditions.NOT_EQUAL:
84
+ result = field !== value
85
+ break
86
+ case FilterConditions.GREATER_THAN:
87
+ result = field > value
88
+ break
89
+ case FilterConditions.LESS_THAN:
90
+ result = field < value
91
+ break
92
+ }
93
+ } else {
94
+ result = false
87
95
  }
88
- } else {
89
- success = false
96
+ return { success: true, result }
97
+ } catch (err) {
98
+ return { success: false, result: false }
90
99
  }
91
- return { success }
92
100
  }
@@ -6,7 +6,8 @@ describe("test the filter logic", () => {
6
6
  let res = await setup.runStep(setup.actions.FILTER.stepId,
7
7
  { field, condition, value }
8
8
  )
9
- expect(res.success).toEqual(pass)
9
+ expect(res.result).toEqual(pass)
10
+ expect(res.success).toEqual(true)
10
11
  }
11
12
 
12
13
  it("should be able test equality", async () => {
@@ -97,7 +97,7 @@ exports.externalTrigger = async function (
97
97
  // values are likely to be submitted as strings, so we shall convert to correct type
98
98
  const coercedFields = {}
99
99
  const fields = automation.definition.trigger.inputs.fields
100
- for (let key of Object.keys(fields)) {
100
+ for (let key of Object.keys(fields || {})) {
101
101
  coercedFields[key] = coerce(params.fields[key], fields[key])
102
102
  }
103
103
  params.fields = coercedFields
@@ -65,6 +65,7 @@ async function getLinksForRows(rows) {
65
65
  // return duplicates, could be querying for both tables in a relation
66
66
  return getUniqueByProp(
67
67
  responses
68
+ .filter(el => el != null)
68
69
  // create a unique ID which we can use for getting only unique ones
69
70
  .map(el => ({ ...el, unique: el.id + el.thisId + el.fieldName })),
70
71
  "unique"