@budibase/server 0.9.171 → 0.9.173-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 (261) hide show
  1. package/__mocks__/{mysql.ts → mysql2.ts} +0 -0
  2. package/builder/assets/{index.dbc6728e.css → index.7469e1e4.css} +2 -2
  3. package/builder/assets/{index.cf0febb9.js → index.8a8f7849.js} +34 -34
  4. package/builder/assets/{vendor.54435e8f.js → vendor.278a305c.js} +1 -1
  5. package/builder/index.html +3 -3
  6. package/coverage/clover.xml +973 -849
  7. package/coverage/coverage-final.json +47 -46
  8. package/coverage/lcov-report/api/controllers/analytics.js.html +21 -9
  9. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  10. package/coverage/lcov-report/api/controllers/application.js.html +186 -84
  11. package/coverage/lcov-report/api/controllers/auth.js.html +37 -10
  12. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/backup.js.html +6 -9
  14. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  15. package/coverage/lcov-report/api/controllers/component.js.html +1 -1
  16. package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/dev.js.html +41 -17
  18. package/coverage/lcov-report/api/controllers/hosting.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/index.html +71 -71
  20. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  23. package/coverage/lcov-report/api/controllers/permission.js.html +7 -4
  24. package/coverage/lcov-report/api/controllers/query.js.html +129 -24
  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 +1 -1
  28. package/coverage/lcov-report/api/controllers/row/index.html +5 -5
  29. package/coverage/lcov-report/api/controllers/row/index.js.html +15 -15
  30. package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
  31. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
  32. package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
  33. package/coverage/lcov-report/api/controllers/screen.js.html +9 -9
  34. package/coverage/lcov-report/api/controllers/script.js.html +12 -57
  35. package/coverage/lcov-report/api/controllers/table/index.html +28 -28
  36. package/coverage/lcov-report/api/controllers/table/index.js.html +16 -37
  37. package/coverage/lcov-report/api/controllers/table/utils.js.html +279 -33
  38. package/coverage/lcov-report/api/controllers/templates.js.html +49 -13
  39. package/coverage/lcov-report/api/controllers/user.js.html +3 -3
  40. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  41. package/coverage/lcov-report/api/controllers/view/index.html +19 -19
  42. package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
  43. package/coverage/lcov-report/api/controllers/view/utils.js.html +8 -8
  44. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +54 -15
  45. package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
  46. package/coverage/lcov-report/api/index.html +1 -1
  47. package/coverage/lcov-report/api/index.js.html +3 -3
  48. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  49. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  50. package/coverage/lcov-report/api/routes/application.js.html +5 -2
  51. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  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 +26 -11
  59. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/index.html +9 -9
  61. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/query.js.html +9 -3
  67. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/static.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  78. package/coverage/lcov-report/automations/actions.js.html +31 -10
  79. package/coverage/lcov-report/automations/automationUtils.js.html +14 -86
  80. package/coverage/lcov-report/automations/bullboard.js.html +1 -1
  81. package/coverage/lcov-report/automations/index.html +37 -37
  82. package/coverage/lcov-report/automations/index.js.html +1 -1
  83. package/coverage/lcov-report/automations/steps/bash.js.html +10 -7
  84. package/coverage/lcov-report/automations/steps/createRow.js.html +2 -2
  85. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/deleteRow.js.html +8 -5
  87. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/executeQuery.js.html +10 -7
  89. package/coverage/lcov-report/automations/steps/executeScript.js.html +10 -7
  90. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/index.html +57 -57
  92. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +10 -7
  94. package/coverage/lcov-report/automations/steps/queryRows.js.html +10 -7
  95. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +10 -7
  96. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  98. package/coverage/lcov-report/automations/steps/updateRow.js.html +14 -14
  99. package/coverage/lcov-report/automations/steps/utils.js.html +10 -10
  100. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  101. package/coverage/lcov-report/automations/thread.js.html +54 -12
  102. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  103. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  104. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  105. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  106. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  107. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  108. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  109. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  110. package/coverage/lcov-report/automations/triggers.js.html +1 -1
  111. package/coverage/lcov-report/automations/utils.js.html +28 -28
  112. package/coverage/lcov-report/constants/index.html +1 -1
  113. package/coverage/lcov-report/constants/index.js.html +5 -2
  114. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  115. package/coverage/lcov-report/constants/screens.js.html +2 -2
  116. package/coverage/lcov-report/db/client.js.html +1 -1
  117. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  118. package/coverage/lcov-report/db/index.html +1 -1
  119. package/coverage/lcov-report/db/index.js.html +1 -1
  120. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +12 -12
  121. package/coverage/lcov-report/db/linkedRows/index.html +18 -18
  122. package/coverage/lcov-report/db/linkedRows/index.js.html +47 -26
  123. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  124. package/coverage/lcov-report/db/newid.js.html +2 -2
  125. package/coverage/lcov-report/db/utils.js.html +17 -17
  126. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  127. package/coverage/lcov-report/events/index.html +1 -1
  128. package/coverage/lcov-report/events/index.js.html +1 -1
  129. package/coverage/lcov-report/events/utils.js.html +1 -1
  130. package/coverage/lcov-report/index.html +107 -107
  131. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  132. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  133. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  134. package/coverage/lcov-report/middleware/authorized.js.html +2 -2
  135. package/coverage/lcov-report/middleware/builder.js.html +1 -1
  136. package/coverage/lcov-report/middleware/currentapp.js.html +52 -25
  137. package/coverage/lcov-report/middleware/index.html +15 -15
  138. package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
  139. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  140. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  141. package/coverage/lcov-report/middleware/usageQuota.js.html +1 -1
  142. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  143. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  144. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  145. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  146. package/coverage/lcov-report/utilities/global.js.html +27 -27
  147. package/coverage/lcov-report/utilities/index.html +32 -17
  148. package/coverage/lcov-report/utilities/index.js.html +4 -4
  149. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +29 -8
  150. package/coverage/lcov-report/utilities/queue/index.html +15 -15
  151. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  152. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  153. package/coverage/lcov-report/utilities/rowProcessor/index.html +14 -14
  154. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +38 -8
  155. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
  156. package/coverage/lcov-report/utilities/scriptRunner.js.html +143 -0
  157. package/coverage/lcov-report/utilities/security.js.html +1 -1
  158. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  159. package/coverage/lcov-report/utilities/users.js.html +1 -1
  160. package/coverage/lcov-report/utilities/workerRequests.js.html +19 -19
  161. package/coverage/lcov.info +1692 -1452
  162. package/dist/api/controllers/analytics.js +4 -0
  163. package/dist/api/controllers/application.js +185 -171
  164. package/dist/api/controllers/auth.js +12 -2
  165. package/dist/api/controllers/backup.js +4 -4
  166. package/dist/api/controllers/deploy/index.js +3 -16
  167. package/dist/api/controllers/dev.js +17 -11
  168. package/dist/api/controllers/permission.js +1 -0
  169. package/dist/api/controllers/query.js +26 -8
  170. package/dist/api/controllers/row/ExternalRequest.js +46 -5
  171. package/dist/api/controllers/script.js +4 -16
  172. package/dist/api/controllers/table/index.js +5 -10
  173. package/dist/api/controllers/table/utils.js +83 -10
  174. package/dist/api/controllers/templates.js +18 -3
  175. package/dist/api/controllers/view/viewBuilder.js +15 -1
  176. package/dist/api/routes/application.js +1 -0
  177. package/dist/api/routes/dev.js +8 -4
  178. package/dist/api/routes/query.js +3 -1
  179. package/dist/api/routes/tests/utilities/TestFunctions.js +7 -9
  180. package/dist/automations/actions.js +9 -3
  181. package/dist/automations/automationUtils.js +8 -29
  182. package/dist/automations/steps/bash.js +2 -1
  183. package/dist/automations/steps/createRow.js +1 -1
  184. package/dist/automations/steps/deleteRow.js +2 -1
  185. package/dist/automations/steps/executeQuery.js +2 -1
  186. package/dist/automations/steps/executeScript.js +2 -1
  187. package/dist/automations/steps/outgoingWebhook.js +2 -1
  188. package/dist/automations/steps/queryRows.js +2 -1
  189. package/dist/automations/steps/sendSmtpEmail.js +2 -1
  190. package/dist/automations/steps/updateRow.js +6 -2
  191. package/dist/automations/thread.js +14 -3
  192. package/dist/constants/index.js +1 -0
  193. package/dist/db/linkedRows/index.js +9 -6
  194. package/dist/integrations/base/sql.js +4 -4
  195. package/dist/integrations/mysql.js +1 -1
  196. package/dist/integrations/utils.js +17 -1
  197. package/dist/middleware/currentapp.js +8 -2
  198. package/dist/package.json +7 -6
  199. package/dist/tests/utilities/TestConfiguration.js +34 -19
  200. package/dist/tests/utilities/controllers.js +1 -0
  201. package/dist/tsconfig.tsbuildinfo +1 -1
  202. package/dist/utilities/fileSystem/index.js +49 -33
  203. package/dist/utilities/global.js +1 -1
  204. package/dist/utilities/queue/inMemoryQueue.js +6 -0
  205. package/dist/utilities/rowProcessor/index.js +8 -0
  206. package/dist/utilities/scriptRunner.js +19 -0
  207. package/package.json +8 -7
  208. package/scripts/jestSetup.js +2 -0
  209. package/src/api/controllers/analytics.js +4 -0
  210. package/src/api/controllers/application.js +43 -9
  211. package/src/api/controllers/auth.js +12 -3
  212. package/src/api/controllers/backup.js +4 -5
  213. package/src/api/controllers/deploy/index.js +3 -18
  214. package/src/api/controllers/dev.js +16 -8
  215. package/src/api/controllers/permission.js +1 -0
  216. package/src/api/controllers/query.js +46 -11
  217. package/src/api/controllers/row/ExternalRequest.ts +53 -4
  218. package/src/api/controllers/script.js +4 -19
  219. package/src/api/controllers/table/index.js +5 -12
  220. package/src/api/controllers/table/utils.js +94 -12
  221. package/src/api/controllers/templates.js +17 -5
  222. package/src/api/controllers/view/tests/__snapshots__/viewBuilder.spec.js.snap +1 -1
  223. package/src/api/controllers/view/viewBuilder.js +14 -1
  224. package/src/api/routes/application.js +1 -0
  225. package/src/api/routes/dev.js +9 -4
  226. package/src/api/routes/query.js +3 -1
  227. package/src/api/routes/tests/auth.spec.js +1 -2
  228. package/src/api/routes/tests/permissions.spec.js +3 -0
  229. package/src/api/routes/tests/query.spec.js +2 -2
  230. package/src/api/routes/tests/routing.spec.js +24 -8
  231. package/src/api/routes/tests/user.spec.js +1 -1
  232. package/src/api/routes/tests/utilities/TestFunctions.js +7 -9
  233. package/src/api/routes/tests/webhook.spec.js +4 -1
  234. package/src/automations/actions.js +10 -3
  235. package/src/automations/automationUtils.js +7 -31
  236. package/src/automations/steps/bash.js +2 -1
  237. package/src/automations/steps/createRow.js +1 -1
  238. package/src/automations/steps/deleteRow.js +2 -1
  239. package/src/automations/steps/executeQuery.js +2 -1
  240. package/src/automations/steps/executeScript.js +2 -1
  241. package/src/automations/steps/outgoingWebhook.js +2 -1
  242. package/src/automations/steps/queryRows.js +2 -1
  243. package/src/automations/steps/sendSmtpEmail.js +2 -1
  244. package/src/automations/steps/updateRow.js +6 -6
  245. package/src/automations/thread.js +17 -3
  246. package/src/constants/index.js +1 -0
  247. package/src/db/linkedRows/index.js +14 -7
  248. package/src/integrations/base/sql.ts +11 -3
  249. package/src/integrations/mysql.ts +1 -1
  250. package/src/integrations/tests/mongo.spec.js +10 -6
  251. package/src/integrations/tests/mysql.spec.js +1 -1
  252. package/src/integrations/utils.ts +18 -0
  253. package/src/middleware/currentapp.js +11 -2
  254. package/src/middleware/tests/currentapp.spec.js +3 -3
  255. package/src/tests/utilities/TestConfiguration.js +47 -19
  256. package/src/tests/utilities/controllers.js +1 -0
  257. package/src/utilities/fileSystem/index.js +65 -43
  258. package/src/utilities/global.js +1 -1
  259. package/src/utilities/queue/inMemoryQueue.js +7 -0
  260. package/src/utilities/rowProcessor/index.js +10 -0
  261. package/src/utilities/scriptRunner.js +21 -0
@@ -15,8 +15,9 @@ import {
15
15
  import {
16
16
  breakRowIdField,
17
17
  generateRowIdField,
18
+ isRowId,
19
+ convertRowId,
18
20
  } from "../../../integrations/utils"
19
- import { RelationshipTypes } from "../../../constants"
20
21
 
21
22
  interface ManyRelationship {
22
23
  tableId?: string
@@ -36,7 +37,11 @@ interface RunConfig {
36
37
 
37
38
  module External {
38
39
  const { makeExternalQuery } = require("./utils")
39
- const { DataSourceOperation, FieldTypes } = require("../../../constants")
40
+ const {
41
+ DataSourceOperation,
42
+ FieldTypes,
43
+ RelationshipTypes,
44
+ } = require("../../../constants")
40
45
  const { breakExternalTableId, isSQL } = require("../../../integrations/utils")
41
46
  const { processObjectSync } = require("@budibase/string-templates")
42
47
  const { cloneDeep } = require("lodash/fp")
@@ -83,6 +88,48 @@ module External {
83
88
  }
84
89
  }
85
90
 
91
+ /**
92
+ * This function checks the incoming parameters to make sure all the inputs are
93
+ * valid based on on the table schema. The main thing this is looking for is when a
94
+ * user has made use of the _id field of a row for a foreign key or a search parameter.
95
+ * In these cases the key will be sent up as [1], rather than 1. In these cases we will
96
+ * simplify it down to the requirements. This function is quite complex as we try to be
97
+ * relatively restrictive over what types of columns we will perform this action for.
98
+ */
99
+ function cleanupConfig(config: RunConfig, table: Table): RunConfig {
100
+ const primaryOptions = [
101
+ FieldTypes.STRING,
102
+ FieldTypes.LONGFORM,
103
+ FieldTypes.OPTIONS,
104
+ FieldTypes.NUMBER,
105
+ ]
106
+ // filter out fields which cannot be keys
107
+ const fieldNames = Object.entries(table.schema)
108
+ .filter(schema => primaryOptions.find(val => val === schema[1].type))
109
+ .map(([fieldName]) => fieldName)
110
+ const iterateObject = (obj: { [key: string]: any }) => {
111
+ for (let [field, value] of Object.entries(obj)) {
112
+ if (fieldNames.find(name => name === field) && isRowId(value)) {
113
+ obj[field] = convertRowId(value)
114
+ }
115
+ }
116
+ }
117
+ // check the row and filters to make sure they aren't a key of some sort
118
+ if (config.filters) {
119
+ for (let filter of Object.values(config.filters)) {
120
+ if (typeof filter !== "object" || Object.keys(filter).length === 0) {
121
+ continue
122
+ }
123
+ iterateObject(filter)
124
+ }
125
+ }
126
+ if (config.row) {
127
+ iterateObject(config.row)
128
+ }
129
+
130
+ return config
131
+ }
132
+
86
133
  function generateIdForRow(row: Row | undefined, table: Table): string {
87
134
  const primary = table.primary
88
135
  if (!row || !primary) {
@@ -509,7 +556,7 @@ module External {
509
556
  return fields
510
557
  }
511
558
 
512
- async run({ id, row, filters, sort, paginate }: RunConfig) {
559
+ async run(config: RunConfig) {
513
560
  const { appId, operation, tableId } = this
514
561
  let { datasourceId, tableName } = breakExternalTableId(tableId)
515
562
  if (!this.datasource) {
@@ -525,9 +572,11 @@ module External {
525
572
  if (!table) {
526
573
  throw `Unable to process query, table "${tableName}" not defined.`
527
574
  }
528
- // clean up row on ingress using schema
575
+ // look for specific components of config which may not be considered acceptable
576
+ let { id, row, filters, sort, paginate } = cleanupConfig(config, table)
529
577
  filters = buildFilters(id, filters || {}, table)
530
578
  const relationships = this.buildRelationships(table)
579
+ // clean up row on ingress using schema
531
580
  const processed = this.inputProcessing(row, table)
532
581
  row = processed.row
533
582
  if (
@@ -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) {
@@ -9,11 +9,7 @@ const {
9
9
  BudibaseInternalDB,
10
10
  } = require("../../../db/utils")
11
11
  const { FieldTypes } = require("../../../constants")
12
- const { TableSaveFunctions, getExternalTable } = require("./utils")
13
- const {
14
- isExternalTable,
15
- breakExternalTableId,
16
- } = require("../../../integrations/utils")
12
+ const { TableSaveFunctions, getTable } = require("./utils")
17
13
 
18
14
  exports.fetch = async function (ctx) {
19
15
  const db = new CouchDB(ctx.appId)
@@ -48,14 +44,8 @@ exports.fetch = async function (ctx) {
48
44
  }
49
45
 
50
46
  exports.find = async function (ctx) {
51
- const db = new CouchDB(ctx.appId)
52
47
  const tableId = ctx.params.id
53
- if (isExternalTable(tableId)) {
54
- let { datasourceId, tableName } = breakExternalTableId(tableId)
55
- ctx.body = await getExternalTable(ctx.appId, datasourceId, tableName)
56
- } else {
57
- ctx.body = await db.get(ctx.params.id)
58
- }
48
+ ctx.body = await getTable(ctx.appId, tableId)
59
49
  }
60
50
 
61
51
  exports.save = async function (ctx) {
@@ -91,6 +81,9 @@ exports.save = async function (ctx) {
91
81
  for (let propKey of Object.keys(tableToSave.schema)) {
92
82
  let column = tableToSave.schema[propKey]
93
83
  let oldColumn = oldTable.schema[propKey]
84
+ if (oldColumn && oldColumn.type === "internal") {
85
+ oldColumn.type = "auto"
86
+ }
94
87
  if (oldColumn && oldColumn.type !== column.type) {
95
88
  ctx.throw(400, "Cannot change the type of a column")
96
89
  }
@@ -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,
@@ -10,6 +9,12 @@ const { isEqual } = require("lodash/fp")
10
9
  const { AutoFieldSubTypes, FieldTypes } = require("../../../constants")
11
10
  const { inputProcessing } = require("../../../utilities/rowProcessor")
12
11
  const { USERS_TABLE_SCHEMA } = require("../../../constants")
12
+ const {
13
+ isExternalTable,
14
+ breakExternalTableId,
15
+ } = require("../../../integrations/utils")
16
+ const { getViews, saveView } = require("../view/utils")
17
+ const viewTemplate = require("../view/viewBuilder")
13
18
 
14
19
  exports.checkForColumnUpdates = async (db, oldTable, updatedTable) => {
15
20
  let updatedRows = []
@@ -22,6 +27,7 @@ exports.checkForColumnUpdates = async (db, oldTable, updatedTable) => {
22
27
  }
23
28
  // check for renaming of columns or deleted columns
24
29
  if (rename || deletedColumns.length !== 0) {
30
+ // Update all rows
25
31
  const rows = await db.allDocs(
26
32
  getRowParams(updatedTable._id, null, {
27
33
  include_docs: true,
@@ -36,6 +42,9 @@ exports.checkForColumnUpdates = async (db, oldTable, updatedTable) => {
36
42
  }
37
43
  return doc
38
44
  })
45
+
46
+ // Update views
47
+ await exports.checkForViewUpdates(db, updatedTable, rename, deletedColumns)
39
48
  delete updatedTable._rename
40
49
  }
41
50
  return { rows: updatedRows, table: updatedTable }
@@ -93,19 +102,10 @@ exports.handleDataImport = async (appId, user, table, dataImport) => {
93
102
  }
94
103
  }
95
104
 
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
- )
105
+ finalData.push(row)
106
106
  }
107
107
 
108
- await db.bulkDocs(await Promise.all(finalData))
108
+ await db.bulkDocs(finalData)
109
109
  let response = await db.put(table)
110
110
  table._rev = response._rev
111
111
  }
@@ -233,4 +233,86 @@ exports.getExternalTable = async (appId, datasourceId, tableName) => {
233
233
  return entities[tableName]
234
234
  }
235
235
 
236
+ exports.getTable = async (appId, tableId) => {
237
+ const db = new CouchDB(appId)
238
+ if (isExternalTable(tableId)) {
239
+ let { datasourceId, tableName } = breakExternalTableId(tableId)
240
+ return exports.getExternalTable(appId, datasourceId, tableName)
241
+ } else {
242
+ return db.get(tableId)
243
+ }
244
+ }
245
+
246
+ exports.checkForViewUpdates = async (db, table, rename, deletedColumns) => {
247
+ const views = await getViews(db)
248
+ const tableViews = views.filter(view => view.meta.tableId === table._id)
249
+
250
+ // Check each table view to see if impacted by this table action
251
+ for (let view of tableViews) {
252
+ let needsUpdated = false
253
+
254
+ // First check for renames, otherwise check for deletions
255
+ if (rename) {
256
+ // Update calculation field if required
257
+ if (view.meta.field === rename.old) {
258
+ view.meta.field = rename.updated
259
+ needsUpdated = true
260
+ }
261
+
262
+ // Update group by field if required
263
+ if (view.meta.groupBy === rename.old) {
264
+ view.meta.groupBy = rename.updated
265
+ needsUpdated = true
266
+ }
267
+
268
+ // Update filters if required
269
+ if (view.meta.filters) {
270
+ view.meta.filters.forEach(filter => {
271
+ if (filter.key === rename.old) {
272
+ filter.key = rename.updated
273
+ needsUpdated = true
274
+ }
275
+ })
276
+ }
277
+ } else if (deletedColumns) {
278
+ deletedColumns.forEach(column => {
279
+ // Remove calculation statement if required
280
+ if (view.meta.field === column) {
281
+ delete view.meta.field
282
+ delete view.meta.calculation
283
+ delete view.meta.groupBy
284
+ needsUpdated = true
285
+ }
286
+
287
+ // Remove group by field if required
288
+ if (view.meta.groupBy === column) {
289
+ delete view.meta.groupBy
290
+ needsUpdated = true
291
+ }
292
+
293
+ // Remove filters referencing deleted field if required
294
+ if (view.meta.filters && view.meta.filters.length) {
295
+ const initialLength = view.meta.filters.length
296
+ view.meta.filters = view.meta.filters.filter(filter => {
297
+ return filter.key !== column
298
+ })
299
+ if (initialLength !== view.meta.filters.length) {
300
+ needsUpdated = true
301
+ }
302
+ }
303
+ })
304
+ }
305
+
306
+ // Update view if required
307
+ if (needsUpdated) {
308
+ const newViewTemplate = viewTemplate(view.meta)
309
+ await saveView(db, null, view.name, newViewTemplate)
310
+ if (!newViewTemplate.meta.schema) {
311
+ newViewTemplate.meta.schema = table.schema
312
+ }
313
+ table.views[view.name] = newViewTemplate.meta
314
+ }
315
+ }
316
+ }
317
+
236
318
  exports.TableSaveFunctions = TableSaveFunctions
@@ -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
@@ -44,7 +44,7 @@ Object {
44
44
  exports[`viewBuilder Filter creates a view with multiple filters and conjunctions 1`] = `
45
45
  Object {
46
46
  "map": "function (doc) {
47
- if (doc.tableId === \\"14f1c4e94d6a47b682ce89d35d4c78b0\\" && doc[\\"Name\\"] === \\"Test\\" || doc[\\"Yes\\"] > \\"Value\\") {
47
+ if (doc.tableId === \\"14f1c4e94d6a47b682ce89d35d4c78b0\\" && (doc[\\"Name\\"] === \\"Test\\" || doc[\\"Yes\\"] > \\"Value\\")) {
48
48
  emit(doc[\\"_id\\"], doc[\\"undefined\\"]);
49
49
  }
50
50
  }",
@@ -10,6 +10,15 @@ const TOKEN_MAP = {
10
10
  OR: "||",
11
11
  }
12
12
 
13
+ const isEmptyExpression = key => {
14
+ return `(
15
+ doc["${key}"] === undefined ||
16
+ doc["${key}"] === null ||
17
+ doc["${key}"] === "" ||
18
+ (Array.isArray(doc["${key}"]) && doc["${key}"].length === 0)
19
+ )`
20
+ }
21
+
13
22
  const GROUP_PROPERTY = {
14
23
  group: {
15
24
  type: "string",
@@ -72,6 +81,10 @@ function parseFilterExpression(filters) {
72
81
  expression.push(
73
82
  `doc["${filter.key}"].${TOKEN_MAP[filter.condition]}("${filter.value}")`
74
83
  )
84
+ } else if (filter.condition === "EMPTY") {
85
+ expression.push(isEmptyExpression(filter.key))
86
+ } else if (filter.condition === "NOT_EMPTY") {
87
+ expression.push(`!${isEmptyExpression(filter.key)}`)
75
88
  } else {
76
89
  const value =
77
90
  typeof filter.value == "string" ? `"${filter.value}"` : filter.value
@@ -113,7 +126,7 @@ function viewTemplate({ field, tableId, groupBy, filters = [], calculation }) {
113
126
  delete filters[0].conjunction
114
127
  }
115
128
  const parsedFilters = parseFilterExpression(filters)
116
- const filterExpression = parsedFilters ? `&& ${parsedFilters}` : ""
129
+ const filterExpression = parsedFilters ? `&& (${parsedFilters})` : ""
117
130
 
118
131
  const emitExpression = parseEmitExpression(field, groupBy)
119
132
 
@@ -7,6 +7,7 @@ const usage = require("../../middleware/usageQuota")
7
7
  const router = Router()
8
8
 
9
9
  router
10
+ .post("/api/applications/:appId/sync", authorized(BUILDER), controller.sync)
10
11
  .post("/api/applications", authorized(BUILDER), usage, controller.create)
11
12
  .get("/api/applications/:appId/definition", controller.fetchAppDefinition)
12
13
  .get("/api/applications", controller.fetch)
@@ -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
  }
@@ -13,8 +13,7 @@ describe("/authenticate", () => {
13
13
 
14
14
  describe("fetch self", () => {
15
15
  it("should be able to fetch self", async () => {
16
- await config.createUser("test@test.com", "p4ssw0rd")
17
- const headers = await config.login("test@test.com", "p4ssw0rd", { userId: "us_uuid1" })
16
+ const headers = await config.login()
18
17
  const res = await request
19
18
  .get(`/api/self`)
20
19
  .set(headers)
@@ -89,6 +89,9 @@ describe("/permission", () => {
89
89
 
90
90
  describe("check public user allowed", () => {
91
91
  it("should be able to read the row", async () => {
92
+ // replicate changes before checking permissions
93
+ await config.deploy()
94
+
92
95
  const res = await request
93
96
  .get(`/api/${table._id}/rows`)
94
97
  .set(config.publicHeaders())
@@ -94,9 +94,9 @@ describe("/queries", () => {
94
94
  const query = await config.createQuery()
95
95
  const res = await request
96
96
  .get(`/api/queries/${query._id}`)
97
- .set(await config.roleHeaders({}))
98
- .expect("Content-Type", /json/)
97
+ .set(await config.defaultHeaders())
99
98
  .expect(200)
99
+ .expect("Content-Type", /json/)
100
100
  expect(res.body.fields).toBeUndefined()
101
101
  expect(res.body.parameters).toBeUndefined()
102
102
  expect(res.body.schema).toBeUndefined()
@@ -21,16 +21,31 @@ describe("/routing", () => {
21
21
  screen2.routing.roleId = BUILTIN_ROLE_IDS.POWER
22
22
  screen2.routing.route = route
23
23
  screen2 = await config.createScreen(screen2)
24
+ await config.deploy()
24
25
  })
25
26
 
26
27
  describe("fetch", () => {
28
+ it("prevents a public user from accessing development app", async () => {
29
+ await request
30
+ .get(`/api/routing/client`)
31
+ .set(config.publicHeaders({ prodApp: false }))
32
+ .expect(302)
33
+ })
34
+
35
+ it("prevents a non builder from accessing development app", async () => {
36
+ await request
37
+ .get(`/api/routing/client`)
38
+ .set(await config.roleHeaders({
39
+ roleId: BUILTIN_ROLE_IDS.BASIC,
40
+ prodApp: false
41
+ }))
42
+ .expect(302)
43
+ })
27
44
  it("returns the correct routing for basic user", async () => {
28
45
  const res = await request
29
46
  .get(`/api/routing/client`)
30
47
  .set(await config.roleHeaders({
31
- email: "basic@test.com",
32
- roleId: BUILTIN_ROLE_IDS.BASIC,
33
- builder: false
48
+ roleId: BUILTIN_ROLE_IDS.BASIC
34
49
  }))
35
50
  .expect("Content-Type", /json/)
36
51
  .expect(200)
@@ -49,9 +64,7 @@ describe("/routing", () => {
49
64
  const res = await request
50
65
  .get(`/api/routing/client`)
51
66
  .set(await config.roleHeaders({
52
- email: "basic@test.com",
53
- roleId: BUILTIN_ROLE_IDS.POWER,
54
- builder: false,
67
+ roleId: BUILTIN_ROLE_IDS.POWER
55
68
  }))
56
69
  .expect("Content-Type", /json/)
57
70
  .expect(200)
@@ -71,9 +84,12 @@ describe("/routing", () => {
71
84
  it("should fetch all routes for builder", async () => {
72
85
  const res = await request
73
86
  .get(`/api/routing`)
74
- .set(config.defaultHeaders())
75
- .expect("Content-Type", /json/)
87
+ .set(await config.roleHeaders({
88
+ roleId: BUILTIN_ROLE_IDS.POWER,
89
+ builder: true,
90
+ }))
76
91
  .expect(200)
92
+ .expect("Content-Type", /json/)
77
93
  expect(res.body.routes).toBeDefined()
78
94
  expect(res.body.routes[route].subpaths[route]).toBeDefined()
79
95
  const subpath = res.body.routes[route].subpaths[route]
@@ -38,7 +38,7 @@ describe("/users", () => {
38
38
  })
39
39
 
40
40
  it("should apply authorization to endpoint", async () => {
41
- await config.createUser("brenda@brenda.com", "brendas_password")
41
+ await config.createUser()
42
42
  await checkPermissionsEndpoint({
43
43
  config,
44
44
  request,
@@ -50,9 +50,10 @@ exports.createRequest = (request, method, url, body) => {
50
50
  }
51
51
 
52
52
  exports.checkBuilderEndpoint = async ({ config, method, url, body }) => {
53
- const headers = await config.login("test@test.com", "test", {
53
+ const headers = await config.login({
54
54
  userId: "us_fail",
55
55
  builder: false,
56
+ prodApp: true,
56
57
  })
57
58
  await exports
58
59
  .createRequest(config.request, method, url, body)
@@ -68,11 +69,9 @@ exports.checkPermissionsEndpoint = async ({
68
69
  passRole,
69
70
  failRole,
70
71
  }) => {
71
- const password = "PASSWORD"
72
- let user = await config.createUser("pass@budibase.com", password, passRole)
73
- const passHeader = await config.login("pass@budibase.com", password, {
72
+ const passHeader = await config.login({
74
73
  roleId: passRole,
75
- userId: user.globalId,
74
+ prodApp: true,
76
75
  })
77
76
 
78
77
  await exports
@@ -82,13 +81,12 @@ exports.checkPermissionsEndpoint = async ({
82
81
 
83
82
  let failHeader
84
83
  if (failRole === BUILTIN_ROLE_IDS.PUBLIC) {
85
- failHeader = config.publicHeaders()
84
+ failHeader = config.publicHeaders({ prodApp: true })
86
85
  } else {
87
- user = await config.createUser("fail@budibase.com", password, failRole)
88
- failHeader = await config.login("fail@budibase.com", password, {
86
+ failHeader = await config.login({
89
87
  roleId: failRole,
90
- userId: user.globalId,
91
88
  builder: false,
89
+ prodApp: true,
92
90
  })
93
91
  }
94
92
 
@@ -112,8 +112,11 @@ describe("/webhooks", () => {
112
112
 
113
113
  describe("trigger", () => {
114
114
  it("should allow triggering from public", async () => {
115
+ // replicate changes before checking webhook
116
+ await config.deploy()
117
+
115
118
  const res = await request
116
- .post(`/api/webhooks/trigger/${config.getAppId()}/${webhook._id}`)
119
+ .post(`/api/webhooks/trigger/${config.prodAppId}/${webhook._id}`)
117
120
  .expect("Content-Type", /json/)
118
121
  .expect(200)
119
122
  expect(res.body.message).toBeDefined()
@@ -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) {