@budibase/server 0.9.188 → 0.9.190-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 (307) hide show
  1. package/Dockerfile +7 -1
  2. package/__mocks__/mssql.ts +1 -1
  3. package/__mocks__/oracledb.ts +31 -0
  4. package/__mocks__/pg.ts +3 -0
  5. package/builder/assets/bb-space-man.5be51a45.svg +290 -0
  6. package/builder/assets/index.7d405e74.css +1 -0
  7. package/builder/assets/index.edea4fa5.js +1316 -0
  8. package/builder/assets/oracle.66592424.png +0 -0
  9. package/builder/assets/vendor.470042a4.js +152 -0
  10. package/builder/assets/vendor.5e1fe644.css +1 -0
  11. package/builder/index.html +4 -4
  12. package/coverage/clover.xml +1507 -1081
  13. package/coverage/coverage-final.json +93 -89
  14. package/coverage/lcov-report/api/controllers/analytics.js.html +2 -2
  15. package/coverage/lcov-report/api/controllers/apikeys.js.html +2 -2
  16. package/coverage/lcov-report/api/controllers/application.js.html +64 -13
  17. package/coverage/lcov-report/api/controllers/auth.js.html +19 -10
  18. package/coverage/lcov-report/api/controllers/automation.js.html +3 -3
  19. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/component.js.html +4 -4
  22. package/coverage/lcov-report/api/controllers/datasource.js.html +232 -28
  23. package/coverage/lcov-report/api/controllers/dev.js.html +16 -7
  24. package/coverage/lcov-report/api/controllers/hosting.js.html +10 -10
  25. package/coverage/lcov-report/api/controllers/index.html +91 -91
  26. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/layout.js.html +3 -3
  28. package/coverage/lcov-report/api/controllers/metadata.js.html +3 -3
  29. package/coverage/lcov-report/api/controllers/permission.js.html +3 -3
  30. package/coverage/lcov-report/api/controllers/query.js.html +53 -209
  31. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  32. package/coverage/lcov-report/api/controllers/routing.js.html +3 -3
  33. package/coverage/lcov-report/api/controllers/row/external.js.html +17 -8
  34. package/coverage/lcov-report/api/controllers/row/index.html +26 -26
  35. package/coverage/lcov-report/api/controllers/row/index.js.html +2 -2
  36. package/coverage/lcov-report/api/controllers/row/internal.js.html +9 -9
  37. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +2 -2
  38. package/coverage/lcov-report/api/controllers/row/utils.js.html +21 -30
  39. package/coverage/lcov-report/api/controllers/screen.js.html +2 -2
  40. package/coverage/lcov-report/api/controllers/script.js.html +3 -3
  41. package/coverage/lcov-report/api/controllers/table/external.js.html +1001 -0
  42. package/coverage/lcov-report/api/controllers/table/index.html +56 -26
  43. package/coverage/lcov-report/api/controllers/table/index.js.html +80 -350
  44. package/coverage/lcov-report/api/controllers/table/internal.js.html +545 -0
  45. package/coverage/lcov-report/api/controllers/table/utils.js.html +175 -52
  46. package/coverage/lcov-report/api/controllers/templates.js.html +4 -4
  47. package/coverage/lcov-report/api/controllers/user.js.html +390 -33
  48. package/coverage/lcov-report/api/controllers/view/exporters.js.html +19 -4
  49. package/coverage/lcov-report/api/controllers/view/index.html +22 -22
  50. package/coverage/lcov-report/api/controllers/view/index.js.html +52 -13
  51. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  52. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +3 -3
  53. package/coverage/lcov-report/api/controllers/webhook.js.html +4 -4
  54. package/coverage/lcov-report/api/index.html +1 -1
  55. package/coverage/lcov-report/api/index.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/dev.js.html +2 -2
  67. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/index.html +4 -4
  69. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/query.js.html +5 -2
  75. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/static.js.html +2 -2
  81. package/coverage/lcov-report/api/routes/table.js.html +35 -2
  82. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  83. package/coverage/lcov-report/api/routes/user.js.html +17 -2
  84. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  85. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  86. package/coverage/lcov-report/automations/actions.js.html +2 -2
  87. package/coverage/lcov-report/automations/automationUtils.js.html +4 -4
  88. package/coverage/lcov-report/automations/bullboard.js.html +71 -20
  89. package/coverage/lcov-report/automations/index.html +35 -50
  90. package/coverage/lcov-report/automations/index.js.html +2 -2
  91. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/discord.js.html +3 -3
  96. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/executeScript.js.html +3 -3
  98. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  99. package/coverage/lcov-report/automations/steps/index.html +14 -14
  100. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  101. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +3 -3
  102. package/coverage/lcov-report/automations/steps/queryRows.js.html +3 -3
  103. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  104. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  105. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  106. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  107. package/coverage/lcov-report/automations/steps/utils.js.html +7 -7
  108. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  109. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  110. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  111. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  112. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  113. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  114. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  115. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  116. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  117. package/coverage/lcov-report/automations/triggers.js.html +22 -10
  118. package/coverage/lcov-report/automations/utils.js.html +42 -12
  119. package/coverage/lcov-report/constants/index.html +5 -5
  120. package/coverage/lcov-report/constants/index.js.html +87 -21
  121. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  122. package/coverage/lcov-report/constants/screens.js.html +1 -1
  123. package/coverage/lcov-report/db/client.js.html +2 -2
  124. package/coverage/lcov-report/db/inMemoryView.js.html +2 -2
  125. package/coverage/lcov-report/db/index.html +2 -2
  126. package/coverage/lcov-report/db/index.js.html +1 -1
  127. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +6 -6
  128. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  129. package/coverage/lcov-report/db/linkedRows/index.js.html +5 -5
  130. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +6 -6
  131. package/coverage/lcov-report/db/newid.js.html +3 -3
  132. package/coverage/lcov-report/db/utils.js.html +57 -57
  133. package/coverage/lcov-report/events/AutomationEmitter.js.html +6 -6
  134. package/coverage/lcov-report/events/index.html +6 -6
  135. package/coverage/lcov-report/events/index.js.html +1 -1
  136. package/coverage/lcov-report/events/utils.js.html +5 -5
  137. package/coverage/lcov-report/index.html +106 -91
  138. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  139. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  140. package/coverage/lcov-report/middleware/appInfo.js.html +2 -2
  141. package/coverage/lcov-report/middleware/authorized.js.html +11 -8
  142. package/coverage/lcov-report/middleware/builder.js.html +16 -7
  143. package/coverage/lcov-report/middleware/currentapp.js.html +6 -6
  144. package/coverage/lcov-report/middleware/index.html +23 -23
  145. package/coverage/lcov-report/middleware/joi-validator.js.html +2 -2
  146. package/coverage/lcov-report/middleware/resourceId.js.html +18 -18
  147. package/coverage/lcov-report/middleware/selfhost.js.html +2 -2
  148. package/coverage/lcov-report/middleware/usageQuota.js.html +4 -4
  149. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  150. package/coverage/lcov-report/{automations/thread.js.html → threads/automation.js.html} +90 -33
  151. package/coverage/lcov-report/threads/index.html +141 -0
  152. package/coverage/lcov-report/threads/index.js.html +278 -0
  153. package/coverage/lcov-report/threads/query.js.html +278 -0
  154. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  155. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  156. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  157. package/coverage/lcov-report/utilities/csvParser.js.html +144 -15
  158. package/coverage/lcov-report/utilities/global.js.html +26 -11
  159. package/coverage/lcov-report/utilities/index.html +48 -48
  160. package/coverage/lcov-report/utilities/index.js.html +47 -14
  161. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +30 -9
  162. package/coverage/lcov-report/utilities/queue/index.html +14 -14
  163. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  164. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  165. package/coverage/lcov-report/utilities/rowProcessor/index.html +4 -4
  166. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +2 -2
  167. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +3 -3
  168. package/coverage/lcov-report/utilities/scriptRunner.js.html +16 -7
  169. package/coverage/lcov-report/utilities/security.js.html +3 -3
  170. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  171. package/coverage/lcov-report/utilities/users.js.html +1 -1
  172. package/coverage/lcov-report/utilities/workerRequests.js.html +5 -5
  173. package/coverage/lcov.info +2495 -1737
  174. package/dist/api/controllers/application.js +15 -1
  175. package/dist/api/controllers/auth.js +3 -3
  176. package/dist/api/controllers/datasource.js +67 -16
  177. package/dist/api/controllers/deploy/index.js +2 -0
  178. package/dist/api/controllers/dev.js +4 -1
  179. package/dist/api/controllers/query.js +30 -58
  180. package/dist/api/controllers/row/ExternalRequest.js +78 -34
  181. package/dist/api/controllers/row/external.js +3 -1
  182. package/dist/api/controllers/row/utils.js +6 -12
  183. package/dist/api/controllers/table/external.js +249 -0
  184. package/dist/api/controllers/table/index.js +49 -123
  185. package/dist/api/controllers/table/internal.js +150 -0
  186. package/dist/api/controllers/table/utils.js +62 -28
  187. package/dist/api/controllers/user.js +123 -11
  188. package/dist/api/controllers/view/exporters.js +4 -0
  189. package/dist/api/controllers/view/index.js +14 -2
  190. package/dist/api/routes/query.js +1 -1
  191. package/dist/api/routes/table.js +7 -1
  192. package/dist/api/routes/user.js +2 -1
  193. package/dist/app.js +2 -11
  194. package/dist/automations/bullboard.js +37 -10
  195. package/dist/automations/triggers.js +7 -3
  196. package/dist/automations/utils.js +11 -2
  197. package/dist/constants/index.js +19 -0
  198. package/dist/definitions/datasource.js +5 -0
  199. package/dist/environment.js +13 -0
  200. package/dist/integrations/base/IntegrationBase.js +2 -0
  201. package/dist/integrations/base/datasourcePlus.js +2 -0
  202. package/dist/integrations/base/sql.js +353 -177
  203. package/dist/integrations/base/sqlTable.js +143 -0
  204. package/dist/integrations/base/utils.js +30 -0
  205. package/dist/integrations/index.js +6 -0
  206. package/dist/integrations/microsoftSqlServer.js +117 -9
  207. package/dist/integrations/mysql.js +16 -85
  208. package/dist/integrations/oracle.js +347 -0
  209. package/dist/integrations/postgres.js +35 -28
  210. package/dist/integrations/utils.js +71 -9
  211. package/dist/middleware/authorized.js +2 -1
  212. package/dist/middleware/builder.js +4 -1
  213. package/dist/package.json +12 -7
  214. package/dist/tests/utilities/TestConfiguration.js +2 -1
  215. package/dist/{automations/thread.js → threads/automation.js} +26 -8
  216. package/dist/threads/index.js +64 -0
  217. package/dist/threads/query.js +64 -0
  218. package/dist/tsconfig.tsbuildinfo +1 -1
  219. package/dist/utilities/csvParser.js +46 -5
  220. package/dist/utilities/global.js +6 -2
  221. package/dist/utilities/index.js +11 -0
  222. package/dist/utilities/queue/inMemoryQueue.js +8 -0
  223. package/dist/utilities/scriptRunner.js +4 -1
  224. package/package.json +13 -8
  225. package/scripts/dev/manage.js +1 -0
  226. package/scripts/integrations/mssql/data/Dockerfile +9 -0
  227. package/scripts/integrations/mssql/data/entrypoint.sh +24 -0
  228. package/scripts/integrations/mssql/data/setup.sql +54 -0
  229. package/scripts/integrations/mssql/docker-compose.yaml +12 -0
  230. package/scripts/integrations/mssql/reset.sh +3 -0
  231. package/scripts/integrations/oracle/docker-compose.yml +19 -0
  232. package/scripts/integrations/oracle/instantclient/linux/x86-64/basiclite-21.4.zip +0 -0
  233. package/scripts/integrations/oracle/instantclient/linux/x86-64/install.sh +23 -0
  234. package/scripts/integrations/oracle/oracle.md +90 -0
  235. package/scripts/integrations/postgres/docker-compose.yml +3 -3
  236. package/scripts/likeCypress.ts +33 -0
  237. package/src/api/controllers/application.js +18 -1
  238. package/src/api/controllers/auth.js +3 -0
  239. package/src/api/controllers/datasource.js +84 -16
  240. package/src/api/controllers/deploy/index.js +2 -0
  241. package/src/api/controllers/dev.js +4 -1
  242. package/src/api/controllers/query.js +29 -81
  243. package/src/api/controllers/row/ExternalRequest.ts +91 -36
  244. package/src/api/controllers/row/external.js +4 -1
  245. package/src/api/controllers/row/utils.js +8 -11
  246. package/src/api/controllers/table/external.js +307 -0
  247. package/src/api/controllers/table/index.js +47 -137
  248. package/src/api/controllers/table/internal.js +155 -0
  249. package/src/api/controllers/table/utils.js +74 -33
  250. package/src/api/controllers/user.js +132 -13
  251. package/src/api/controllers/view/exporters.js +5 -0
  252. package/src/api/controllers/view/index.js +15 -2
  253. package/src/api/routes/query.js +1 -0
  254. package/src/api/routes/table.js +11 -0
  255. package/src/api/routes/tests/datasource.spec.js +2 -2
  256. package/src/api/routes/tests/misc.spec.js +5 -1
  257. package/src/api/routes/tests/query.spec.js +0 -1
  258. package/src/api/routes/user.js +5 -0
  259. package/src/app.ts +2 -10
  260. package/src/automations/bullboard.js +28 -11
  261. package/src/automations/tests/automation.spec.js +3 -4
  262. package/src/automations/triggers.js +7 -3
  263. package/src/automations/utils.js +12 -2
  264. package/src/constants/index.js +22 -0
  265. package/src/definitions/common.ts +7 -1
  266. package/src/definitions/datasource.ts +10 -3
  267. package/src/environment.js +15 -0
  268. package/src/integrations/airtable.ts +2 -1
  269. package/src/integrations/arangodb.ts +2 -1
  270. package/src/integrations/base/IntegrationBase.ts +6 -0
  271. package/src/integrations/base/datasourcePlus.ts +9 -0
  272. package/src/integrations/base/sql.ts +377 -218
  273. package/src/integrations/base/sqlTable.ts +180 -0
  274. package/src/integrations/base/utils.ts +22 -0
  275. package/src/integrations/couchdb.ts +2 -1
  276. package/src/integrations/dynamodb.ts +2 -1
  277. package/src/integrations/elasticsearch.ts +2 -1
  278. package/src/integrations/index.ts +7 -0
  279. package/src/integrations/microsoftSqlServer.ts +145 -13
  280. package/src/integrations/mongodb.ts +2 -1
  281. package/src/integrations/mysql.ts +23 -95
  282. package/src/integrations/oracle.ts +458 -0
  283. package/src/integrations/postgres.ts +45 -35
  284. package/src/integrations/rest.ts +2 -1
  285. package/src/integrations/s3.ts +2 -1
  286. package/src/integrations/tests/microsoftSqlServer.spec.js +22 -15
  287. package/src/integrations/tests/oracle.spec.js +94 -0
  288. package/src/integrations/tests/postgres.spec.js +4 -0
  289. package/src/integrations/utils.ts +78 -8
  290. package/src/middleware/authorized.js +2 -1
  291. package/src/middleware/builder.js +4 -1
  292. package/src/tests/utilities/TestConfiguration.js +2 -1
  293. package/src/{automations/thread.js → threads/automation.js} +27 -8
  294. package/src/threads/index.js +66 -0
  295. package/src/threads/query.js +66 -0
  296. package/src/utilities/csvParser.js +48 -5
  297. package/src/utilities/global.js +7 -2
  298. package/src/utilities/index.js +11 -0
  299. package/src/utilities/queue/inMemoryQueue.js +7 -0
  300. package/src/utilities/scriptRunner.js +4 -1
  301. package/src/utilities/tests/__snapshots__/csvParser.spec.js.snap +0 -6
  302. package/src/utilities/tests/csvParser.spec.js +3 -0
  303. package/tsconfig.json +1 -1
  304. package/builder/assets/index.550fdba7.js +0 -201
  305. package/builder/assets/index.7469e1e4.css +0 -29
  306. package/builder/assets/vendor.278a305c.js +0 -9
  307. package/builder/assets/vendor.d75d5eb7.css +0 -1
@@ -0,0 +1,155 @@
1
+ const CouchDB = require("../../../db")
2
+ const linkRows = require("../../../db/linkedRows")
3
+ const { getRowParams, generateTableID } = require("../../../db/utils")
4
+ const { FieldTypes } = require("../../../constants")
5
+ const {
6
+ TableSaveFunctions,
7
+ hasTypeChanged,
8
+ getTable,
9
+ handleDataImport,
10
+ } = require("./utils")
11
+
12
+ exports.save = async function (ctx) {
13
+ const appId = ctx.appId
14
+ const db = new CouchDB(appId)
15
+ const { dataImport, ...rest } = ctx.request.body
16
+ let tableToSave = {
17
+ type: "table",
18
+ _id: generateTableID(),
19
+ views: {},
20
+ ...rest,
21
+ }
22
+
23
+ // if the table obj had an _id then it will have been retrieved
24
+ let oldTable
25
+ if (ctx.request.body && ctx.request.body._id) {
26
+ oldTable = await db.get(ctx.request.body._id)
27
+ }
28
+
29
+ if (hasTypeChanged(tableToSave, oldTable)) {
30
+ ctx.throw(400, "A column type has changed.")
31
+ }
32
+
33
+ // saving a table is a complex operation, involving many different steps, this
34
+ // has been broken out into a utility to make it more obvious/easier to manipulate
35
+ const tableSaveFunctions = new TableSaveFunctions({
36
+ db,
37
+ ctx,
38
+ oldTable,
39
+ dataImport,
40
+ })
41
+ tableToSave = await tableSaveFunctions.before(tableToSave)
42
+
43
+ // make sure that types don't change of a column, have to remove
44
+ // the column if you want to change the type
45
+ if (oldTable && oldTable.schema) {
46
+ for (let propKey of Object.keys(tableToSave.schema)) {
47
+ let column = tableToSave.schema[propKey]
48
+ let oldColumn = oldTable.schema[propKey]
49
+ if (oldColumn && oldColumn.type === "internal") {
50
+ oldColumn.type = "auto"
51
+ }
52
+ if (oldColumn && oldColumn.type !== column.type) {
53
+ ctx.throw(400, "Cannot change the type of a column")
54
+ }
55
+ }
56
+ }
57
+
58
+ // Don't rename if the name is the same
59
+ let { _rename } = tableToSave
60
+ /* istanbul ignore next */
61
+ if (_rename && _rename.old === _rename.updated) {
62
+ _rename = null
63
+ delete tableToSave._rename
64
+ }
65
+
66
+ // rename row fields when table column is renamed
67
+ /* istanbul ignore next */
68
+ if (_rename && tableToSave.schema[_rename.updated].type === FieldTypes.LINK) {
69
+ ctx.throw(400, "Cannot rename a linked column.")
70
+ }
71
+
72
+ tableToSave = await tableSaveFunctions.mid(tableToSave)
73
+
74
+ // update schema of non-statistics views when new columns are added
75
+ for (let view in tableToSave.views) {
76
+ const tableView = tableToSave.views[view]
77
+ if (!tableView) continue
78
+
79
+ if (tableView.schema.group || tableView.schema.field) continue
80
+ tableView.schema = tableToSave.schema
81
+ }
82
+
83
+ // update linked rows
84
+ try {
85
+ const linkResp = await linkRows.updateLinks({
86
+ appId,
87
+ eventType: oldTable
88
+ ? linkRows.EventType.TABLE_UPDATED
89
+ : linkRows.EventType.TABLE_SAVE,
90
+ table: tableToSave,
91
+ oldTable: oldTable,
92
+ })
93
+ if (linkResp != null && linkResp._rev) {
94
+ tableToSave._rev = linkResp._rev
95
+ }
96
+ } catch (err) {
97
+ ctx.throw(400, err)
98
+ }
99
+
100
+ // don't perform any updates until relationships have been
101
+ // checked by the updateLinks function
102
+ const updatedRows = tableSaveFunctions.getUpdatedRows()
103
+ if (updatedRows && updatedRows.length !== 0) {
104
+ await db.bulkDocs(updatedRows)
105
+ }
106
+ const result = await db.put(tableToSave)
107
+ tableToSave._rev = result.rev
108
+
109
+ tableToSave = await tableSaveFunctions.after(tableToSave)
110
+
111
+ return tableToSave
112
+ }
113
+
114
+ exports.destroy = async function (ctx) {
115
+ const appId = ctx.appId
116
+ const db = new CouchDB(appId)
117
+ const tableToDelete = await db.get(ctx.params.tableId)
118
+
119
+ // Delete all rows for that table
120
+ const rows = await db.allDocs(
121
+ getRowParams(ctx.params.tableId, null, {
122
+ include_docs: true,
123
+ })
124
+ )
125
+ await db.bulkDocs(rows.rows.map(row => ({ ...row.doc, _deleted: true })))
126
+
127
+ // update linked rows
128
+ await linkRows.updateLinks({
129
+ appId,
130
+ eventType: linkRows.EventType.TABLE_DELETE,
131
+ table: tableToDelete,
132
+ })
133
+
134
+ // don't remove the table itself until very end
135
+ await db.remove(tableToDelete)
136
+
137
+ // remove table search index
138
+ const currentIndexes = await db.getIndexes()
139
+ const existingIndex = currentIndexes.indexes.find(
140
+ existing => existing.name === `search:${ctx.params.tableId}`
141
+ )
142
+ if (existingIndex) {
143
+ await db.deleteIndex(existingIndex)
144
+ }
145
+
146
+ return tableToDelete
147
+ }
148
+
149
+ exports.bulkImport = async function (ctx) {
150
+ const appId = ctx.appId
151
+ const table = await getTable(appId, ctx.params.tableId)
152
+ const { dataImport } = ctx.request.body
153
+ await handleDataImport(appId, ctx.user, table, dataImport)
154
+ return table
155
+ }
@@ -8,7 +8,7 @@ const {
8
8
  const { isEqual } = require("lodash/fp")
9
9
  const { AutoFieldSubTypes, FieldTypes } = require("../../../constants")
10
10
  const { inputProcessing } = require("../../../utilities/rowProcessor")
11
- const { USERS_TABLE_SCHEMA } = require("../../../constants")
11
+ const { USERS_TABLE_SCHEMA, SwitchableTypes } = require("../../../constants")
12
12
  const {
13
13
  isExternalTable,
14
14
  breakExternalTableId,
@@ -72,43 +72,47 @@ exports.makeSureTableUpToDate = (table, tableToSave) => {
72
72
  }
73
73
 
74
74
  exports.handleDataImport = async (appId, user, table, dataImport) => {
75
+ if (!dataImport || !dataImport.csvString) {
76
+ return table
77
+ }
75
78
  const db = new CouchDB(appId)
76
- if (dataImport && dataImport.csvString) {
77
- // Populate the table with rows imported from CSV in a bulk update
78
- const data = await csvParser.transform(dataImport)
79
-
80
- let finalData = []
81
- for (let i = 0; i < data.length; i++) {
82
- let row = data[i]
83
- row._id = generateRowID(table._id)
84
- row.tableId = table._id
85
- const processed = inputProcessing(user, table, row, {
86
- noAutoRelationships: true,
87
- })
88
- table = processed.table
89
- row = processed.row
90
-
91
- for (let [fieldName, schema] of Object.entries(table.schema)) {
92
- // check whether the options need to be updated for inclusion as part of the data import
93
- if (
94
- schema.type === FieldTypes.OPTIONS &&
95
- (!schema.constraints.inclusion ||
96
- schema.constraints.inclusion.indexOf(row[fieldName]) === -1)
97
- ) {
98
- schema.constraints.inclusion = [
99
- ...schema.constraints.inclusion,
100
- row[fieldName],
101
- ]
102
- }
103
- }
79
+ // Populate the table with rows imported from CSV in a bulk update
80
+ const data = await csvParser.transform({
81
+ ...dataImport,
82
+ existingTable: table,
83
+ })
84
+
85
+ let finalData = []
86
+ for (let i = 0; i < data.length; i++) {
87
+ let row = data[i]
88
+ row._id = generateRowID(table._id)
89
+ row.tableId = table._id
90
+ const processed = inputProcessing(user, table, row, {
91
+ noAutoRelationships: true,
92
+ })
93
+ table = processed.table
94
+ row = processed.row
104
95
 
105
- finalData.push(row)
96
+ for (let [fieldName, schema] of Object.entries(table.schema)) {
97
+ // check whether the options need to be updated for inclusion as part of the data import
98
+ if (
99
+ schema.type === FieldTypes.OPTIONS &&
100
+ (!schema.constraints.inclusion ||
101
+ schema.constraints.inclusion.indexOf(row[fieldName]) === -1)
102
+ ) {
103
+ schema.constraints.inclusion = [
104
+ ...schema.constraints.inclusion,
105
+ row[fieldName],
106
+ ]
107
+ }
106
108
  }
107
109
 
108
- await db.bulkDocs(finalData)
109
- let response = await db.put(table)
110
- table._rev = response._rev
110
+ finalData.push(row)
111
111
  }
112
+
113
+ await db.bulkDocs(finalData)
114
+ let response = await db.put(table)
115
+ table._rev = response._rev
112
116
  return table
113
117
  }
114
118
 
@@ -315,4 +319,41 @@ exports.checkForViewUpdates = async (db, table, rename, deletedColumns) => {
315
319
  }
316
320
  }
317
321
 
322
+ exports.generateForeignKey = (column, relatedTable) => {
323
+ return `fk_${relatedTable.name}_${column.fieldName}`
324
+ }
325
+
326
+ exports.generateJunctionTableName = (column, table, relatedTable) => {
327
+ return `jt_${table.name}_${relatedTable.name}_${column.name}_${column.fieldName}`
328
+ }
329
+
330
+ exports.foreignKeyStructure = (keyName, meta = null) => {
331
+ const structure = {
332
+ type: FieldTypes.NUMBER,
333
+ constraints: {},
334
+ name: keyName,
335
+ }
336
+ if (meta) {
337
+ structure.meta = meta
338
+ }
339
+ return structure
340
+ }
341
+
342
+ exports.hasTypeChanged = (table, oldTable) => {
343
+ if (!oldTable) {
344
+ return false
345
+ }
346
+ for (let [key, field] of Object.entries(oldTable.schema)) {
347
+ const oldType = field.type
348
+ if (!table.schema[key]) {
349
+ continue
350
+ }
351
+ const newType = table.schema[key].type
352
+ if (oldType !== newType && SwitchableTypes.indexOf(oldType) === -1) {
353
+ return true
354
+ }
355
+ }
356
+ return false
357
+ }
358
+
318
359
  exports.TableSaveFunctions = TableSaveFunctions
@@ -4,27 +4,144 @@ const {
4
4
  getUserMetadataParams,
5
5
  } = require("../../db/utils")
6
6
  const { InternalTables } = require("../../db/utils")
7
- const { getGlobalUsers } = require("../../utilities/global")
7
+ const { getGlobalUsers, getRawGlobalUser } = require("../../utilities/global")
8
8
  const { getFullUser } = require("../../utilities/users")
9
+ const { isEqual } = require("lodash")
10
+ const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
11
+ const { getDevelopmentAppID, getDeployedAppIDs } = require("@budibase/auth/db")
12
+ const { doesDatabaseExist } = require("../../utilities")
13
+ const { UserStatus } = require("@budibase/auth/constants")
9
14
 
10
- function removeGlobalProps(user) {
11
- // make sure to always remove some of the global user props
15
+ async function rawMetadata(db) {
16
+ return (
17
+ await db.allDocs(
18
+ getUserMetadataParams(null, {
19
+ include_docs: true,
20
+ })
21
+ )
22
+ ).rows.map(row => row.doc)
23
+ }
24
+
25
+ function combineMetadataAndUser(user, metadata) {
26
+ // skip users with no access
27
+ if (user.roleId === BUILTIN_ROLE_IDS.PUBLIC) {
28
+ return null
29
+ }
30
+ delete user._rev
31
+ const metadataId = generateUserMetadataID(user._id)
32
+ const newDoc = {
33
+ ...user,
34
+ _id: metadataId,
35
+ tableId: InternalTables.USER_METADATA,
36
+ }
37
+ const found = Array.isArray(metadata)
38
+ ? metadata.find(doc => doc._id === metadataId)
39
+ : metadata
40
+ // copy rev over for the purposes of equality check
41
+ if (found) {
42
+ newDoc._rev = found._rev
43
+ }
44
+ if (found == null || !isEqual(newDoc, found)) {
45
+ return {
46
+ ...found,
47
+ ...newDoc,
48
+ }
49
+ }
50
+ return null
51
+ }
52
+
53
+ exports.syncGlobalUsers = async appId => {
54
+ // sync user metadata
55
+ const db = new CouchDB(appId)
56
+ const [users, metadata] = await Promise.all([
57
+ getGlobalUsers(appId),
58
+ rawMetadata(db),
59
+ ])
60
+ const toWrite = []
61
+ for (let user of users) {
62
+ const combined = await combineMetadataAndUser(user, metadata)
63
+ if (combined) {
64
+ toWrite.push(combined)
65
+ }
66
+ }
67
+ await db.bulkDocs(toWrite)
68
+ }
69
+
70
+ exports.syncUser = async function (ctx) {
71
+ let deleting = false,
72
+ user
73
+ const userId = ctx.params.id
74
+ try {
75
+ user = await getRawGlobalUser(userId)
76
+ } catch (err) {
77
+ if (err && err.status === 404) {
78
+ user = {}
79
+ deleting = true
80
+ } else {
81
+ throw err
82
+ }
83
+ }
84
+ const roles = user.roles
85
+ // remove props which aren't useful to metadata
12
86
  delete user.password
87
+ delete user.forceResetPassword
13
88
  delete user.roles
14
- delete user.builder
15
- return user
89
+ // run through all production appIDs in the users roles
90
+ let prodAppIds
91
+ // if they are a builder then get all production app IDs
92
+ if ((user.builder && user.builder.global) || deleting) {
93
+ prodAppIds = await getDeployedAppIDs(CouchDB)
94
+ } else {
95
+ prodAppIds = Object.entries(roles)
96
+ .filter(entry => entry[1] !== BUILTIN_ROLE_IDS.PUBLIC)
97
+ .map(([appId]) => appId)
98
+ }
99
+ for (let prodAppId of prodAppIds) {
100
+ const roleId = roles[prodAppId]
101
+ const devAppId = getDevelopmentAppID(prodAppId)
102
+ for (let appId of [prodAppId, devAppId]) {
103
+ if (!(await doesDatabaseExist(appId))) {
104
+ continue
105
+ }
106
+ const db = new CouchDB(appId)
107
+ const metadataId = generateUserMetadataID(userId)
108
+ let metadata
109
+ try {
110
+ metadata = await db.get(metadataId)
111
+ } catch (err) {
112
+ if (deleting) {
113
+ continue
114
+ }
115
+ metadata = {
116
+ tableId: InternalTables.USER_METADATA,
117
+ }
118
+ }
119
+ // assign the roleId for the metadata doc
120
+ if (roleId) {
121
+ metadata.roleId = roleId
122
+ }
123
+ let combined = !deleting
124
+ ? combineMetadataAndUser(user, metadata)
125
+ : {
126
+ ...metadata,
127
+ status: UserStatus.INACTIVE,
128
+ metadata: BUILTIN_ROLE_IDS.PUBLIC,
129
+ }
130
+ // if its null then there was no updates required
131
+ if (combined) {
132
+ await db.put(combined)
133
+ }
134
+ }
135
+ }
136
+ ctx.body = {
137
+ message: "User synced.",
138
+ }
16
139
  }
17
140
 
18
141
  exports.fetchMetadata = async function (ctx) {
19
142
  const database = new CouchDB(ctx.appId)
20
143
  const global = await getGlobalUsers(ctx.appId)
21
- const metadata = (
22
- await database.allDocs(
23
- getUserMetadataParams(null, {
24
- include_docs: true,
25
- })
26
- )
27
- ).rows.map(row => row.doc)
144
+ const metadata = await rawMetadata(database)
28
145
  const users = []
29
146
  for (let user of global) {
30
147
  // find the metadata that matches up to the global ID
@@ -52,7 +169,9 @@ exports.updateSelfMetadata = async function (ctx) {
52
169
  exports.updateMetadata = async function (ctx) {
53
170
  const appId = ctx.appId
54
171
  const db = new CouchDB(appId)
55
- const user = removeGlobalProps(ctx.request.body)
172
+ const user = ctx.request.body
173
+ // this isn't applicable to the user
174
+ delete user.roles
56
175
  const metadata = {
57
176
  tableId: InternalTables.USER_METADATA,
58
177
  ...user,
@@ -16,3 +16,8 @@ exports.csv = function (headers, rows) {
16
16
  exports.json = function (headers, rows) {
17
17
  return JSON.stringify(rows, undefined, 2)
18
18
  }
19
+
20
+ exports.ExportFormats = {
21
+ CSV: "csv",
22
+ JSON: "json",
23
+ }
@@ -4,6 +4,7 @@ const { apiFileReturn } = require("../../../utilities/fileSystem")
4
4
  const exporters = require("./exporters")
5
5
  const { saveView, getView, getViews, deleteView } = require("./utils")
6
6
  const { fetchView } = require("../row")
7
+ const { getTable } = require("../table/utils")
7
8
 
8
9
  exports.fetch = async ctx => {
9
10
  const db = new CouchDB(ctx.appId)
@@ -56,7 +57,7 @@ exports.exportView = async ctx => {
56
57
  const view = await getView(db, viewName)
57
58
 
58
59
  const format = ctx.query.format
59
- if (!format) {
60
+ if (!format || !Object.values(exporters.ExportFormats).includes(format)) {
60
61
  ctx.throw(400, "Format must be specified, either csv or json")
61
62
  }
62
63
 
@@ -80,10 +81,22 @@ exports.exportView = async ctx => {
80
81
  let schema = view && view.meta && view.meta.schema
81
82
  if (!schema) {
82
83
  const tableId = ctx.params.tableId || view.meta.tableId
83
- const table = await db.get(tableId)
84
+ const table = await getTable(ctx.appId, tableId)
84
85
  schema = table.schema
85
86
  }
86
87
 
88
+ // make sure no "undefined" entries appear in the CSV
89
+ if (format === exporters.ExportFormats.CSV) {
90
+ const schemaKeys = Object.keys(schema)
91
+ for (let key of schemaKeys) {
92
+ for (let row of ctx.body) {
93
+ if (row[key] == null) {
94
+ row[key] = ""
95
+ }
96
+ }
97
+ }
98
+ }
99
+
87
100
  // Export part
88
101
  let headers = Object.keys(schema)
89
102
  const exporter = exporters[format]
@@ -66,6 +66,7 @@ router
66
66
  )
67
67
  .get(
68
68
  "/api/queries/:queryId",
69
+ paramResource("queryId"),
69
70
  authorized(PermissionTypes.QUERY, PermissionLevels.READ),
70
71
  queryController.find
71
72
  )
@@ -53,5 +53,16 @@ router
53
53
  authorized(BUILDER),
54
54
  tableController.destroy
55
55
  )
56
+ // this is currently builder only, but in the future
57
+ // it could be carried out by an end user in app,
58
+ // however some thought will need to be had about
59
+ // implications for automations (triggers)
60
+ // new trigger type, bulk rows created
61
+ .post(
62
+ "/api/tables/:tableId/import",
63
+ paramResource("tableId"),
64
+ authorized(BUILDER),
65
+ tableController.bulkImport
66
+ )
56
67
 
57
68
  module.exports = router
@@ -26,8 +26,8 @@ describe("/datasources", () => {
26
26
  .expect('Content-Type', /json/)
27
27
  .expect(200)
28
28
 
29
- expect(res.res.statusMessage).toEqual("Datasource saved successfully.")
30
- expect(res.body.name).toEqual("Test")
29
+ expect(res.body.datasource.name).toEqual("Test")
30
+ expect(res.body.errors).toBeUndefined()
31
31
  })
32
32
  })
33
33
 
@@ -75,7 +75,11 @@ describe("run misc tests", () => {
75
75
  },
76
76
  })
77
77
  const dataImport = {
78
- csvString: "a,b,c,d\n1,2,3,4"
78
+ csvString: "a,b,c,d\n1,2,3,4",
79
+ schema: {},
80
+ }
81
+ for (let col of ["a", "b", "c", "d"]) {
82
+ dataImport.schema[col] = { type: "string" }
79
83
  }
80
84
  await tableUtils.handleDataImport(
81
85
  config.getAppId(),
@@ -1,7 +1,6 @@
1
1
  // mock out postgres for this
2
2
  jest.mock("pg")
3
3
 
4
- const { findLastKey } = require("lodash/fp")
5
4
  const setup = require("./utilities")
6
5
  const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
7
6
  const { basicQuery, basicDatasource } = setup.structures
@@ -34,5 +34,10 @@ router
34
34
  authorized(PermissionTypes.USER, PermissionLevels.WRITE),
35
35
  controller.destroyMetadata
36
36
  )
37
+ .post(
38
+ "/api/users/metadata/sync/:id",
39
+ authorized(PermissionTypes.USER, PermissionLevels.WRITE),
40
+ controller.syncUser
41
+ )
37
42
 
38
43
  module.exports = router
package/src/app.ts CHANGED
@@ -41,16 +41,8 @@ app.use(
41
41
  )
42
42
 
43
43
  if (!env.isTest()) {
44
- const bullApp = bullboard.init()
45
- app.use(async (ctx: ExtendableContext, next: () => any) => {
46
- if (ctx.path.startsWith(bullboard.pathPrefix)) {
47
- ctx.status = 200
48
- ctx.respond = false
49
- bullApp(ctx.req, ctx.res)
50
- } else {
51
- await next()
52
- }
53
- })
44
+ const plugin = bullboard.init()
45
+ app.use(plugin)
54
46
  }
55
47
 
56
48
  app.context.eventEmitter = eventEmitter
@@ -1,6 +1,6 @@
1
- const { createBullBoard } = require("bull-board")
2
- const { BullAdapter } = require("bull-board/bullAdapter")
3
- const express = require("express")
1
+ const { createBullBoard } = require("@bull-board/api")
2
+ const { BullAdapter } = require("@bull-board/api/bullAdapter")
3
+ const { KoaAdapter } = require("@bull-board/koa")
4
4
  const env = require("../environment")
5
5
  const Queue = env.isTest()
6
6
  ? require("../utilities/queue/inMemoryQueue")
@@ -9,23 +9,40 @@ const { JobQueues } = require("../constants")
9
9
  const { utils } = require("@budibase/auth/redis")
10
10
  const { opts, redisProtocolUrl } = utils.getRedisOptions()
11
11
 
12
- const redisConfig = redisProtocolUrl || { redis: opts }
13
- let automationQueue = new Queue(JobQueues.AUTOMATIONS, redisConfig)
12
+ const CLEANUP_PERIOD_MS = 60 * 1000
13
+ const queueConfig = redisProtocolUrl || { redis: opts }
14
+ let cleanupInternal = null
14
15
 
15
- exports.pathPrefix = "/bulladmin"
16
+ let automationQueue = new Queue(JobQueues.AUTOMATIONS, queueConfig)
17
+
18
+ async function cleanup() {
19
+ await automationQueue.clean(CLEANUP_PERIOD_MS, "completed")
20
+ }
21
+
22
+ const PATH_PREFIX = "/bulladmin"
16
23
 
17
24
  exports.init = () => {
18
- const expressApp = express()
25
+ // cleanup the events every 5 minutes
26
+ if (!cleanupInternal) {
27
+ cleanupInternal = setInterval(cleanup, CLEANUP_PERIOD_MS)
28
+ // fire off an initial cleanup
29
+ cleanup().catch(err => {
30
+ console.error(`Unable to cleanup automation queue initially - ${err}`)
31
+ })
32
+ }
19
33
  // Set up queues for bull board admin
20
34
  const queues = [automationQueue]
21
35
  const adapters = []
36
+ const serverAdapter = new KoaAdapter()
22
37
  for (let queue of queues) {
23
38
  adapters.push(new BullAdapter(queue))
24
39
  }
25
- const { router } = createBullBoard(adapters)
26
-
27
- expressApp.use(exports.pathPrefix, router)
28
- return expressApp
40
+ createBullBoard({
41
+ queues: adapters,
42
+ serverAdapter,
43
+ })
44
+ serverAdapter.setBasePath(PATH_PREFIX)
45
+ return serverAdapter.registerPlugin()
29
46
  }
30
47
 
31
48
  exports.queue = automationQueue
@@ -1,5 +1,5 @@
1
1
  jest.mock("../../utilities/usageQuota")
2
- jest.mock("../thread")
2
+ jest.mock("../../threads/automation")
3
3
  jest.mock("../../utilities/redis", () => ({
4
4
  init: jest.fn(),
5
5
  checkTestFlag: () => {
@@ -11,8 +11,7 @@ jest.spyOn(global.console, "error")
11
11
 
12
12
  require("../../environment")
13
13
  const automation = require("../index")
14
- const usageQuota = require("../../utilities/usageQuota")
15
- const thread = require("../thread")
14
+ const thread = require("../../threads/automation")
16
15
  const triggers = require("../triggers")
17
16
  const { basicAutomation } = require("../../tests/utilities/structures")
18
17
  const { wait } = require("../../utilities")
@@ -62,7 +61,7 @@ describe("Run through some parts of the automations system", () => {
62
61
  }
63
62
  }
64
63
  }
65
- }))
64
+ }), expect.any(Function))
66
65
  })
67
66
 
68
67
  it("should be able to clean inputs with the utilities", () => {