@budibase/server 0.9.167-alpha.0 → 0.9.167-alpha.12

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 (197) hide show
  1. package/builder/assets/{index.32c8bc7d.js → index.bb938864.js} +34 -34
  2. package/builder/assets/{index.18ad7651.css → index.bcacf476.css} +2 -2
  3. package/builder/assets/{vendor.60887453.js → vendor.a3855e3d.js} +1 -1
  4. package/builder/index.html +3 -3
  5. package/coverage/clover.xml +416 -369
  6. package/coverage/coverage-final.json +17 -16
  7. package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
  8. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  9. package/coverage/lcov-report/api/controllers/application.js.html +1 -1
  10. package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
  11. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  12. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/component.js.html +1 -1
  15. package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
  16. package/coverage/lcov-report/api/controllers/dev.js.html +41 -17
  17. package/coverage/lcov-report/api/controllers/hosting.js.html +1 -1
  18. package/coverage/lcov-report/api/controllers/index.html +43 -43
  19. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
  23. package/coverage/lcov-report/api/controllers/query.js.html +129 -24
  24. package/coverage/lcov-report/api/controllers/role.js.html +1 -1
  25. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  26. package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/row/index.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/index.js.html +1 -1
  29. package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
  30. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
  31. package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
  32. package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
  33. package/coverage/lcov-report/api/controllers/script.js.html +12 -57
  34. package/coverage/lcov-report/api/controllers/table/index.html +20 -20
  35. package/coverage/lcov-report/api/controllers/table/index.js.html +17 -8
  36. package/coverage/lcov-report/api/controllers/table/utils.js.html +8 -38
  37. package/coverage/lcov-report/api/controllers/templates.js.html +49 -13
  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 +1 -1
  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/application.js.html +1 -1
  50. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  51. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/dev.js.html +26 -11
  58. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/index.html +9 -9
  60. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/metadata.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 +9 -3
  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/row.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/static.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  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 +31 -10
  78. package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
  79. package/coverage/lcov-report/automations/bullboard.js.html +1 -1
  80. package/coverage/lcov-report/automations/index.html +19 -19
  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/createRow.js.html +1 -1
  84. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  85. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  87. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  89. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/index.html +1 -1
  91. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  96. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  98. package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
  99. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  100. package/coverage/lcov-report/automations/thread.js.html +54 -12
  101. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  102. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  103. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  104. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  105. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  106. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  107. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  108. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  109. package/coverage/lcov-report/automations/triggers.js.html +1 -1
  110. package/coverage/lcov-report/automations/utils.js.html +1 -1
  111. package/coverage/lcov-report/constants/index.html +1 -1
  112. package/coverage/lcov-report/constants/index.js.html +5 -2
  113. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  114. package/coverage/lcov-report/constants/screens.js.html +1 -1
  115. package/coverage/lcov-report/db/client.js.html +1 -1
  116. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  117. package/coverage/lcov-report/db/index.html +1 -1
  118. package/coverage/lcov-report/db/index.js.html +1 -1
  119. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +12 -12
  120. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  121. package/coverage/lcov-report/db/linkedRows/index.js.html +9 -9
  122. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  123. package/coverage/lcov-report/db/newid.js.html +1 -1
  124. package/coverage/lcov-report/db/utils.js.html +1 -1
  125. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  126. package/coverage/lcov-report/events/index.html +1 -1
  127. package/coverage/lcov-report/events/index.js.html +1 -1
  128. package/coverage/lcov-report/events/utils.js.html +1 -1
  129. package/coverage/lcov-report/index.html +53 -53
  130. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  131. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  132. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  133. package/coverage/lcov-report/middleware/authorized.js.html +1 -1
  134. package/coverage/lcov-report/middleware/builder.js.html +1 -1
  135. package/coverage/lcov-report/middleware/currentapp.js.html +2 -2
  136. package/coverage/lcov-report/middleware/index.html +1 -1
  137. package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
  138. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  139. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  140. package/coverage/lcov-report/middleware/usageQuota.js.html +1 -1
  141. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  142. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  143. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  144. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  145. package/coverage/lcov-report/utilities/global.js.html +2 -2
  146. package/coverage/lcov-report/utilities/index.html +22 -7
  147. package/coverage/lcov-report/utilities/index.js.html +1 -1
  148. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +1 -1
  149. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  150. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  151. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  152. package/coverage/lcov-report/utilities/rowProcessor/index.html +14 -14
  153. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +38 -8
  154. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
  155. package/coverage/lcov-report/utilities/scriptRunner.js.html +143 -0
  156. package/coverage/lcov-report/utilities/security.js.html +1 -1
  157. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  158. package/coverage/lcov-report/utilities/users.js.html +1 -1
  159. package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
  160. package/coverage/lcov.info +700 -608
  161. package/dist/api/controllers/dev.js +17 -11
  162. package/dist/api/controllers/query.js +26 -8
  163. package/dist/api/controllers/script.js +4 -16
  164. package/dist/api/controllers/table/index.js +3 -0
  165. package/dist/api/controllers/table/utils.js +2 -10
  166. package/dist/api/controllers/templates.js +18 -3
  167. package/dist/api/routes/dev.js +8 -4
  168. package/dist/api/routes/query.js +3 -1
  169. package/dist/automations/actions.js +9 -3
  170. package/dist/automations/thread.js +14 -3
  171. package/dist/constants/index.js +1 -0
  172. package/dist/integrations/base/sql.js +4 -4
  173. package/dist/middleware/currentapp.js +1 -1
  174. package/dist/package.json +5 -4
  175. package/dist/tsconfig.tsbuildinfo +1 -1
  176. package/dist/utilities/global.js +1 -1
  177. package/dist/utilities/rowProcessor/index.js +8 -0
  178. package/dist/utilities/scriptRunner.js +19 -0
  179. package/package.json +6 -5
  180. package/src/api/controllers/dev.js +16 -8
  181. package/src/api/controllers/query.js +46 -11
  182. package/src/api/controllers/script.js +4 -19
  183. package/src/api/controllers/table/index.js +3 -0
  184. package/src/api/controllers/table/utils.js +2 -12
  185. package/src/api/controllers/templates.js +17 -5
  186. package/src/api/routes/dev.js +9 -4
  187. package/src/api/routes/query.js +3 -1
  188. package/src/automations/actions.js +10 -3
  189. package/src/automations/thread.js +17 -3
  190. package/src/constants/index.js +1 -0
  191. package/src/integrations/base/sql.ts +5 -3
  192. package/src/integrations/tests/mongo.spec.js +10 -6
  193. package/src/middleware/currentapp.js +1 -1
  194. package/src/middleware/tests/currentapp.spec.js +3 -3
  195. package/src/utilities/global.js +1 -1
  196. package/src/utilities/rowProcessor/index.js +10 -0
  197. package/src/utilities/scriptRunner.js +21 -0
@@ -33,7 +33,7 @@ exports.updateAppRole = (appId, user) => {
33
33
  user.roleId = BUILTIN_ROLE_IDS.ADMIN;
34
34
  }
35
35
  else if (!user.roleId) {
36
- user.roleId = BUILTIN_ROLE_IDS.BASIC;
36
+ user.roleId = BUILTIN_ROLE_IDS.PUBLIC;
37
37
  }
38
38
  delete user.roles;
39
39
  return user;
@@ -149,6 +149,10 @@ exports.processAutoColumn = processAutoColumn;
149
149
  * @returns {object} The coerced value
150
150
  */
151
151
  exports.coerce = (row, type) => {
152
+ // no coercion specified for type, skip it
153
+ if (!TYPE_TRANSFORM_MAP[type]) {
154
+ return row;
155
+ }
152
156
  // eslint-disable-next-line no-prototype-builtins
153
157
  if (TYPE_TRANSFORM_MAP[type].hasOwnProperty(row)) {
154
158
  return TYPE_TRANSFORM_MAP[type][row];
@@ -190,6 +194,10 @@ exports.inputProcessing = (user = {}, table, row, opts = { noAutoRelationships:
190
194
  clonedRow[key] = exports.coerce(value, field.type);
191
195
  }
192
196
  }
197
+ if (!clonedRow._id || !clonedRow._rev) {
198
+ clonedRow._id = row._id;
199
+ clonedRow._rev = row._rev;
200
+ }
193
201
  // handle auto columns - this returns an object like {table, row}
194
202
  return processAutoColumn(user, copiedTable, clonedRow, opts);
195
203
  };
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ const fetch = require("node-fetch");
3
+ const { VM, VMScript } = require("vm2");
4
+ class ScriptRunner {
5
+ constructor(script, context) {
6
+ const code = `let fn = () => {\n${script}\n}; results.out = fn();`;
7
+ this.vm = new VM();
8
+ this.results = { out: "" };
9
+ this.vm.setGlobals(context);
10
+ this.vm.setGlobal("fetch", fetch);
11
+ this.vm.setGlobal("results", this.results);
12
+ this.script = new VMScript(code);
13
+ }
14
+ execute() {
15
+ this.vm.run(this.script);
16
+ return this.results.out;
17
+ }
18
+ }
19
+ module.exports = ScriptRunner;
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.167-alpha.0",
4
+ "version": "0.9.167-alpha.12",
5
5
  "description": "Budibase Web Server",
6
6
  "main": "src/index.js",
7
7
  "repository": {
@@ -68,9 +68,9 @@
68
68
  "author": "Budibase",
69
69
  "license": "AGPL-3.0-or-later",
70
70
  "dependencies": {
71
- "@budibase/auth": "^0.9.167-alpha.0",
72
- "@budibase/client": "^0.9.167-alpha.0",
73
- "@budibase/string-templates": "^0.9.167-alpha.0",
71
+ "@budibase/auth": "^0.9.167-alpha.12",
72
+ "@budibase/client": "^0.9.167-alpha.12",
73
+ "@budibase/string-templates": "^0.9.167-alpha.12",
74
74
  "@elastic/elasticsearch": "7.10.0",
75
75
  "@koa/router": "8.0.0",
76
76
  "@sendgrid/mail": "7.1.1",
@@ -119,6 +119,7 @@
119
119
  "to-json-schema": "0.2.5",
120
120
  "uuid": "3.3.2",
121
121
  "validate.js": "0.13.1",
122
+ "vm2": "^3.9.3",
122
123
  "yargs": "13.2.4",
123
124
  "zlib": "1.0.5"
124
125
  },
@@ -148,5 +149,5 @@
148
149
  "typescript": "^4.3.5",
149
150
  "update-dotenv": "^1.1.1"
150
151
  },
151
- "gitHead": "54fff6db18dced835ab577cd85453ef11606b0d0"
152
+ "gitHead": "eed61105f0b7a018bd4be19f655fc7f1020252be"
152
153
  }
@@ -7,11 +7,13 @@ const { clearLock } = require("../../utilities/redis")
7
7
  const { Replication } = require("@budibase/auth").db
8
8
  const { DocumentTypes } = require("../../db/utils")
9
9
 
10
- async function redirect(ctx, method) {
10
+ async function redirect(ctx, method, path = "global") {
11
11
  const { devPath } = ctx.params
12
12
  const queryString = ctx.originalUrl.split("?")[1] || ""
13
13
  const response = await fetch(
14
- checkSlashesInUrl(`${env.WORKER_URL}/api/global/${devPath}?${queryString}`),
14
+ checkSlashesInUrl(
15
+ `${env.WORKER_URL}/api/${path}/${devPath}?${queryString}`
16
+ ),
15
17
  request(
16
18
  ctx,
17
19
  {
@@ -41,16 +43,22 @@ async function redirect(ctx, method) {
41
43
  ctx.cookies
42
44
  }
43
45
 
44
- exports.redirectGet = async ctx => {
45
- await redirect(ctx, "GET")
46
+ exports.buildRedirectGet = path => {
47
+ return async ctx => {
48
+ await redirect(ctx, "GET", path)
49
+ }
46
50
  }
47
51
 
48
- exports.redirectPost = async ctx => {
49
- await redirect(ctx, "POST")
52
+ exports.buildRedirectPost = path => {
53
+ return async ctx => {
54
+ await redirect(ctx, "POST", path)
55
+ }
50
56
  }
51
57
 
52
- exports.redirectDelete = async ctx => {
53
- await redirect(ctx, "DELETE")
58
+ exports.buildRedirectDelete = path => {
59
+ return async ctx => {
60
+ await redirect(ctx, "DELETE", path)
61
+ }
54
62
  }
55
63
 
56
64
  exports.clearLock = async ctx => {
@@ -4,6 +4,7 @@ const { generateQueryID, getQueryParams } = require("../../db/utils")
4
4
  const { integrations } = require("../../integrations")
5
5
  const { BaseQueryVerbs } = require("../../constants")
6
6
  const env = require("../../environment")
7
+ const ScriptRunner = require("../../utilities/scriptRunner")
7
8
 
8
9
  // simple function to append "readable" to all read queries
9
10
  function enrichQueries(input) {
@@ -28,12 +29,39 @@ function formatResponse(resp) {
28
29
  resp = { response: resp }
29
30
  }
30
31
  }
31
- if (!Array.isArray(resp)) {
32
- resp = [resp]
33
- }
34
32
  return resp
35
33
  }
36
34
 
35
+ async function runAndTransform(
36
+ integration,
37
+ queryVerb,
38
+ enrichedQuery,
39
+ transformer
40
+ ) {
41
+ let rows = formatResponse(await integration[queryVerb](enrichedQuery))
42
+
43
+ // transform as required
44
+ if (transformer) {
45
+ const runner = new ScriptRunner(transformer, { data: rows })
46
+ rows = runner.execute()
47
+ }
48
+
49
+ // needs to an array for next step
50
+ if (!Array.isArray(rows)) {
51
+ rows = [rows]
52
+ }
53
+
54
+ // map into JSON if just raw primitive here
55
+ if (rows.find(row => typeof row !== "object")) {
56
+ rows = rows.map(value => ({ value }))
57
+ }
58
+
59
+ // get all the potential fields in the schema
60
+ let keys = rows.flatMap(Object.keys)
61
+
62
+ return { rows, keys }
63
+ }
64
+
37
65
  exports.fetch = async function (ctx) {
38
66
  const db = new CouchDB(ctx.appId)
39
67
 
@@ -122,15 +150,16 @@ exports.preview = async function (ctx) {
122
150
  ctx.throw(400, "Integration type does not exist.")
123
151
  }
124
152
 
125
- const { fields, parameters, queryVerb } = ctx.request.body
126
-
153
+ const { fields, parameters, queryVerb, transformer } = ctx.request.body
127
154
  const enrichedQuery = await enrichQueryFields(fields, parameters)
128
-
129
155
  const integration = new Integration(datasource.config)
130
- const rows = formatResponse(await integration[queryVerb](enrichedQuery))
131
156
 
132
- // get all the potential fields in the schema
133
- const keys = rows.flatMap(Object.keys)
157
+ const { rows, keys } = await runAndTransform(
158
+ integration,
159
+ queryVerb,
160
+ enrichedQuery,
161
+ transformer
162
+ )
134
163
 
135
164
  ctx.body = {
136
165
  rows,
@@ -158,10 +187,16 @@ exports.execute = async function (ctx) {
158
187
  query.fields,
159
188
  ctx.request.body.parameters
160
189
  )
161
-
162
190
  const integration = new Integration(datasource.config)
191
+
163
192
  // call the relevant CRUD method on the integration class
164
- ctx.body = formatResponse(await integration[query.queryVerb](enrichedQuery))
193
+ const { rows } = await runAndTransform(
194
+ integration,
195
+ query.queryVerb,
196
+ enrichedQuery,
197
+ query.transformer
198
+ )
199
+ ctx.body = rows
165
200
  // cleanup
166
201
  if (integration.end) {
167
202
  integration.end()
@@ -1,24 +1,9 @@
1
- const fetch = require("node-fetch")
2
- const vm = require("vm")
3
-
4
- class ScriptExecutor {
5
- constructor(body) {
6
- const code = `let fn = () => {\n${body.script}\n}; out = fn();`
7
- this.script = new vm.Script(code)
8
- this.context = vm.createContext(body.context)
9
- this.context.fetch = fetch
10
- }
11
-
12
- execute() {
13
- this.script.runInContext(this.context)
14
- return this.context.out
15
- }
16
- }
1
+ const ScriptRunner = require("../../utilities/scriptRunner")
17
2
 
18
3
  exports.execute = async function (ctx) {
19
- const executor = new ScriptExecutor(ctx.request.body)
20
-
21
- ctx.body = executor.execute()
4
+ const { script, context } = ctx.request.body
5
+ const runner = new ScriptRunner(script, context)
6
+ ctx.body = runner.execute()
22
7
  }
23
8
 
24
9
  exports.save = async function (ctx) {
@@ -91,6 +91,9 @@ exports.save = async function (ctx) {
91
91
  for (let propKey of Object.keys(tableToSave.schema)) {
92
92
  let column = tableToSave.schema[propKey]
93
93
  let oldColumn = oldTable.schema[propKey]
94
+ if (oldColumn && oldColumn.type === "internal") {
95
+ oldColumn.type = "auto"
96
+ }
94
97
  if (oldColumn && oldColumn.type !== column.type) {
95
98
  ctx.throw(400, "Cannot change the type of a column")
96
99
  }
@@ -1,5 +1,4 @@
1
1
  const CouchDB = require("../../../db")
2
- const linkRows = require("../../../db/linkedRows")
3
2
  const csvParser = require("../../../utilities/csvParser")
4
3
  const {
5
4
  getRowParams,
@@ -93,19 +92,10 @@ exports.handleDataImport = async (appId, user, table, dataImport) => {
93
92
  }
94
93
  }
95
94
 
96
- // make sure link rows are up to date
97
- finalData.push(
98
- linkRows.updateLinks({
99
- appId,
100
- eventType: linkRows.EventType.ROW_SAVE,
101
- row,
102
- tableId: row.tableId,
103
- table,
104
- })
105
- )
95
+ finalData.push(row)
106
96
  }
107
97
 
108
- await db.bulkDocs(await Promise.all(finalData))
98
+ await db.bulkDocs(finalData)
109
99
  let response = await db.put(table)
110
100
  table._rev = response._rev
111
101
  }
@@ -7,11 +7,23 @@ const DEFAULT_TEMPLATES_BUCKET =
7
7
 
8
8
  exports.fetch = async function (ctx) {
9
9
  const { type = "app" } = ctx.query
10
- const response = await fetch(
11
- `https://${DEFAULT_TEMPLATES_BUCKET}/manifest.json`
12
- )
13
- const json = await response.json()
14
- ctx.body = Object.values(json.templates[type])
10
+ let response,
11
+ error = false
12
+ try {
13
+ response = await fetch(`https://${DEFAULT_TEMPLATES_BUCKET}/manifest.json`)
14
+ if (response.status !== 200) {
15
+ error = true
16
+ }
17
+ } catch (err) {
18
+ error = true
19
+ }
20
+ // if there is an error, simply return no templates
21
+ if (!error && response) {
22
+ const json = await response.json()
23
+ ctx.body = Object.values(json.templates[type])
24
+ } else {
25
+ ctx.body = []
26
+ }
15
27
  }
16
28
 
17
29
  // can't currently test this, have to ignore from coverage
@@ -6,11 +6,16 @@ const { BUILDER } = require("@budibase/auth/permissions")
6
6
 
7
7
  const router = Router()
8
8
 
9
- if (env.isDev() || env.isTest()) {
9
+ function redirectPath(path) {
10
10
  router
11
- .get("/api/global/:devPath(.*)", controller.redirectGet)
12
- .post("/api/global/:devPath(.*)", controller.redirectPost)
13
- .delete("/api/global/:devPath(.*)", controller.redirectDelete)
11
+ .get(`/api/${path}/:devPath(.*)`, controller.buildRedirectGet(path))
12
+ .post(`/api/${path}/:devPath(.*)`, controller.buildRedirectPost(path))
13
+ .delete(`/api/${path}/:devPath(.*)`, controller.buildRedirectDelete(path))
14
+ }
15
+
16
+ if (env.isDev() || env.isTest()) {
17
+ redirectPath("global")
18
+ redirectPath("system")
14
19
  }
15
20
 
16
21
  router
@@ -31,7 +31,8 @@ function generateQueryValidation() {
31
31
  })),
32
32
  queryVerb: Joi.string().allow().required(),
33
33
  extra: Joi.object().optional(),
34
- schema: Joi.object({}).required().unknown(true)
34
+ schema: Joi.object({}).required().unknown(true),
35
+ transformer: Joi.string().optional(),
35
36
  }))
36
37
  }
37
38
 
@@ -42,6 +43,7 @@ function generateQueryPreviewValidation() {
42
43
  queryVerb: Joi.string().allow().required(),
43
44
  extra: Joi.object().optional(),
44
45
  datasourceId: Joi.string().required(),
46
+ transformer: Joi.string().optional(),
45
47
  parameters: Joi.object({}).required().unknown(true)
46
48
  }))
47
49
  }
@@ -3,7 +3,6 @@ const createRow = require("./steps/createRow")
3
3
  const updateRow = require("./steps/updateRow")
4
4
  const deleteRow = require("./steps/deleteRow")
5
5
  const executeScript = require("./steps/executeScript")
6
- const bash = require("./steps/bash")
7
6
  const executeQuery = require("./steps/executeQuery")
8
7
  const outgoingWebhook = require("./steps/outgoingWebhook")
9
8
  const serverLog = require("./steps/serverLog")
@@ -14,6 +13,7 @@ const integromat = require("./steps/integromat")
14
13
  let filter = require("./steps/filter")
15
14
  let delay = require("./steps/delay")
16
15
  let queryRow = require("./steps/queryRows")
16
+ const env = require("../environment")
17
17
 
18
18
  const ACTION_IMPLS = {
19
19
  SEND_EMAIL_SMTP: sendSmtpEmail.run,
@@ -22,7 +22,6 @@ const ACTION_IMPLS = {
22
22
  DELETE_ROW: deleteRow.run,
23
23
  OUTGOING_WEBHOOK: outgoingWebhook.run,
24
24
  EXECUTE_SCRIPT: executeScript.run,
25
- EXECUTE_BASH: bash.run,
26
25
  EXECUTE_QUERY: executeQuery.run,
27
26
  SERVER_LOG: serverLog.run,
28
27
  DELAY: delay.run,
@@ -42,7 +41,6 @@ const ACTION_DEFINITIONS = {
42
41
  OUTGOING_WEBHOOK: outgoingWebhook.definition,
43
42
  EXECUTE_SCRIPT: executeScript.definition,
44
43
  EXECUTE_QUERY: executeQuery.definition,
45
- EXECUTE_BASH: bash.definition,
46
44
  SERVER_LOG: serverLog.definition,
47
45
  DELAY: delay.definition,
48
46
  FILTER: filter.definition,
@@ -54,6 +52,15 @@ const ACTION_DEFINITIONS = {
54
52
  integromat: integromat.definition,
55
53
  }
56
54
 
55
+ // don't add the bash script/definitions unless in self host
56
+ // the fact this isn't included in any definitions means it cannot be
57
+ // ran at all
58
+ if (env.SELF_HOSTED) {
59
+ const bash = require("./steps/bash")
60
+ ACTION_IMPLS["EXECUTE_BASH"] = bash.run
61
+ ACTION_DEFINITIONS["EXECUTE_BASH"] = bash.definition
62
+ }
63
+
57
64
  /* istanbul ignore next */
58
65
  exports.getAction = async function (actionName) {
59
66
  if (ACTION_IMPLS[actionName] != null) {
@@ -10,6 +10,7 @@ const env = require("../environment")
10
10
  const usage = require("../utilities/usageQuota")
11
11
 
12
12
  const FILTER_STEP_ID = actions.ACTION_DEFINITIONS.FILTER.stepId
13
+ const STOPPED_STATUS = { success: false, status: "STOPPED" }
13
14
 
14
15
  /**
15
16
  * The automation orchestrator is a class responsible for executing automations.
@@ -68,7 +69,13 @@ class Orchestrator {
68
69
  async execute() {
69
70
  let automation = this._automation
70
71
  const app = await this.getApp()
72
+ let stopped = false
71
73
  for (let step of automation.definition.steps) {
74
+ // execution stopped, record state for that
75
+ if (stopped) {
76
+ this.updateExecutionOutput(step.id, step.stepId, {}, STOPPED_STATUS)
77
+ continue
78
+ }
72
79
  let stepFn = await this.getStepFunctionality(step.stepId)
73
80
  step.inputs = await processObject(step.inputs, this._context)
74
81
  step.inputs = automationUtils.cleanInputValues(
@@ -86,10 +93,17 @@ class Orchestrator {
86
93
  context: this._context,
87
94
  })
88
95
  })
96
+ this._context.steps.push(outputs)
97
+ // if filter causes us to stop execution don't break the loop, set a var
98
+ // so that we can finish iterating through the steps and record that it stopped
89
99
  if (step.stepId === FILTER_STEP_ID && !outputs.success) {
90
- break
100
+ stopped = true
101
+ this.updateExecutionOutput(step.id, step.stepId, step.inputs, {
102
+ ...outputs,
103
+ ...STOPPED_STATUS,
104
+ })
105
+ continue
91
106
  }
92
- this._context.steps.push(outputs)
93
107
  this.updateExecutionOutput(step.id, step.stepId, step.inputs, outputs)
94
108
  } catch (err) {
95
109
  console.error(`Automation error - ${step.stepId} - ${err}`)
@@ -99,7 +113,7 @@ class Orchestrator {
99
113
 
100
114
  // Increment quota for automation runs
101
115
  if (!env.SELF_HOSTED && !isDevAppID(this._appId)) {
102
- usage.update(usage.Properties.AUTOMATION, 1)
116
+ await usage.update(usage.Properties.AUTOMATION, 1)
103
117
  }
104
118
  return this.executionOutput
105
119
  }
@@ -42,6 +42,7 @@ exports.FieldTypes = {
42
42
  FORMULA: "formula",
43
43
  AUTO: "auto",
44
44
  JSON: "json",
45
+ INTERNAL: "internal",
45
46
  }
46
47
 
47
48
  exports.RelationshipTypes = {
@@ -98,7 +98,9 @@ function addFilters(
98
98
  }
99
99
 
100
100
  function addRelationships(
101
+ knex: Knex,
101
102
  query: KnexQuery,
103
+ fields: string | string[],
102
104
  fromTable: string,
103
105
  relationships: RelationshipsJson[] | undefined
104
106
  ): KnexQuery {
@@ -114,7 +116,7 @@ function addRelationships(
114
116
  query = query.leftJoin(
115
117
  toTable,
116
118
  `${fromTable}.${from}`,
117
- `${relationship.tableName}.${to}`
119
+ `${toTable}.${to}`
118
120
  )
119
121
  } else {
120
122
  const throughTable = relationship.through
@@ -130,7 +132,7 @@ function addRelationships(
130
132
  .leftJoin(toTable, `${toTable}.${toPrimary}`, `${throughTable}.${to}`)
131
133
  }
132
134
  }
133
- return query
135
+ return query.limit(BASE_LIMIT)
134
136
  }
135
137
 
136
138
  function buildCreate(
@@ -199,7 +201,7 @@ function buildRead(knex: Knex, json: QueryJson, limit: number): KnexQuery {
199
201
  [tableName]: query,
200
202
  }).select(selectStatement)
201
203
  // handle joins
202
- return addRelationships(preQuery, tableName, relationships)
204
+ return addRelationships(knex, preQuery, selectStatement, tableName, relationships)
203
205
  }
204
206
 
205
207
  function buildUpdate(
@@ -27,7 +27,7 @@ describe("MongoDB Integration", () => {
27
27
  const body = {
28
28
  name: "Hello"
29
29
  }
30
- const response = await config.integration.create({
30
+ await config.integration.create({
31
31
  index: indexName,
32
32
  json: body,
33
33
  extra: { collection: 'testCollection', actionTypes: 'insertOne'}
@@ -54,7 +54,7 @@ describe("MongoDB Integration", () => {
54
54
  },
55
55
  extra: { collection: 'testCollection', actionTypes: 'deleteOne'}
56
56
  }
57
- const response = await config.integration.delete(query)
57
+ await config.integration.delete(query)
58
58
  expect(config.integration.client.deleteOne).toHaveBeenCalledWith(query.json)
59
59
  })
60
60
 
@@ -65,7 +65,7 @@ describe("MongoDB Integration", () => {
65
65
  },
66
66
  extra: { collection: 'testCollection', actionTypes: 'updateOne'}
67
67
  }
68
- const response = await config.integration.update(query)
68
+ await config.integration.update(query)
69
69
  expect(config.integration.client.updateOne).toHaveBeenCalledWith(query.json)
70
70
  })
71
71
 
@@ -75,10 +75,14 @@ describe("MongoDB Integration", () => {
75
75
  const query = {
76
76
  extra: { collection: 'testCollection', actionTypes: 'deleteOne'}
77
77
  }
78
- // Weird, need to do an IIFE for jest to recognize that it throws
79
- expect(() => config.integration.read(query)()).toThrow(expect.any(Object))
80
78
 
79
+ let error = null
80
+ try {
81
+ await config.integration.read(query)
82
+ } catch (err) {
83
+ error = err
84
+ }
85
+ expect(error).toBeDefined()
81
86
  restore()
82
87
  })
83
-
84
88
  })
@@ -45,7 +45,7 @@ module.exports = async (ctx, next) => {
45
45
  const globalUser = await getCachedSelf(ctx, requestAppId)
46
46
  appId = requestAppId
47
47
  // retrieving global user gets the right role
48
- roleId = globalUser.roleId || BUILTIN_ROLE_IDS.BASIC
48
+ roleId = globalUser.roleId || roleId
49
49
  }
50
50
 
51
51
  // nothing more to do
@@ -127,8 +127,8 @@ describe("Current app middleware", () => {
127
127
  } else {
128
128
  expect(cookieFn).not.toHaveBeenCalled()
129
129
  }
130
- expect(config.ctx.roleId).toEqual("BASIC")
131
- expect(config.ctx.user.role._id).toEqual("BASIC")
130
+ expect(config.ctx.roleId).toEqual("PUBLIC")
131
+ expect(config.ctx.user.role._id).toEqual("PUBLIC")
132
132
  expect(config.ctx.appId).toEqual("app_test")
133
133
  expect(config.next).toHaveBeenCalled()
134
134
  }
@@ -163,7 +163,7 @@ describe("Current app middleware", () => {
163
163
  return "app_test"
164
164
  },
165
165
  setCookie: jest.fn(),
166
- getCookie: () => ({appId: "app_test", roleId: "BASIC"}),
166
+ getCookie: () => ({appId: "app_test", roleId: "PUBLIC"}),
167
167
  },
168
168
  constants: { Cookies: {} },
169
169
  }))
@@ -26,7 +26,7 @@ exports.updateAppRole = (appId, user) => {
26
26
  if (!user.roleId && user.builder && user.builder.global) {
27
27
  user.roleId = BUILTIN_ROLE_IDS.ADMIN
28
28
  } else if (!user.roleId) {
29
- user.roleId = BUILTIN_ROLE_IDS.BASIC
29
+ user.roleId = BUILTIN_ROLE_IDS.PUBLIC
30
30
  }
31
31
  delete user.roles
32
32
  return user
@@ -150,6 +150,10 @@ exports.processAutoColumn = processAutoColumn
150
150
  * @returns {object} The coerced value
151
151
  */
152
152
  exports.coerce = (row, type) => {
153
+ // no coercion specified for type, skip it
154
+ if (!TYPE_TRANSFORM_MAP[type]) {
155
+ return row
156
+ }
153
157
  // eslint-disable-next-line no-prototype-builtins
154
158
  if (TYPE_TRANSFORM_MAP[type].hasOwnProperty(row)) {
155
159
  return TYPE_TRANSFORM_MAP[type][row]
@@ -196,6 +200,12 @@ exports.inputProcessing = (
196
200
  clonedRow[key] = exports.coerce(value, field.type)
197
201
  }
198
202
  }
203
+
204
+ if (!clonedRow._id || !clonedRow._rev) {
205
+ clonedRow._id = row._id
206
+ clonedRow._rev = row._rev
207
+ }
208
+
199
209
  // handle auto columns - this returns an object like {table, row}
200
210
  return processAutoColumn(user, copiedTable, clonedRow, opts)
201
211
  }
@@ -0,0 +1,21 @@
1
+ const fetch = require("node-fetch")
2
+ const { VM, VMScript } = require("vm2")
3
+
4
+ class ScriptRunner {
5
+ constructor(script, context) {
6
+ const code = `let fn = () => {\n${script}\n}; results.out = fn();`
7
+ this.vm = new VM()
8
+ this.results = { out: "" }
9
+ this.vm.setGlobals(context)
10
+ this.vm.setGlobal("fetch", fetch)
11
+ this.vm.setGlobal("results", this.results)
12
+ this.script = new VMScript(code)
13
+ }
14
+
15
+ execute() {
16
+ this.vm.run(this.script)
17
+ return this.results.out
18
+ }
19
+ }
20
+
21
+ module.exports = ScriptRunner