@budibase/server 0.9.184 → 0.9.185-alpha.11

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 (290) hide show
  1. package/Dockerfile +1 -1
  2. package/__mocks__/mssql.ts +1 -1
  3. package/__mocks__/pg.ts +3 -0
  4. package/builder/assets/bb-space-man.5be51a45.svg +290 -0
  5. package/builder/assets/index.053c6f7a.js +1321 -0
  6. package/builder/assets/index.4a7c5636.css +1 -0
  7. package/builder/assets/vendor.470042a4.js +152 -0
  8. package/builder/assets/vendor.5e1fe644.css +1 -0
  9. package/builder/index.html +4 -4
  10. package/coverage/clover.xml +1357 -959
  11. package/coverage/coverage-final.json +91 -87
  12. package/coverage/lcov-report/api/controllers/analytics.js.html +2 -2
  13. package/coverage/lcov-report/api/controllers/apikeys.js.html +2 -2
  14. package/coverage/lcov-report/api/controllers/application.js.html +65 -14
  15. package/coverage/lcov-report/api/controllers/auth.js.html +19 -10
  16. package/coverage/lcov-report/api/controllers/automation.js.html +3 -3
  17. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  18. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/component.js.html +4 -4
  20. package/coverage/lcov-report/api/controllers/datasource.js.html +147 -27
  21. package/coverage/lcov-report/api/controllers/dev.js.html +16 -7
  22. package/coverage/lcov-report/api/controllers/hosting.js.html +10 -10
  23. package/coverage/lcov-report/api/controllers/index.html +90 -90
  24. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  25. package/coverage/lcov-report/api/controllers/layout.js.html +3 -3
  26. package/coverage/lcov-report/api/controllers/metadata.js.html +3 -3
  27. package/coverage/lcov-report/api/controllers/permission.js.html +3 -3
  28. package/coverage/lcov-report/api/controllers/query.js.html +53 -209
  29. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  30. package/coverage/lcov-report/api/controllers/routing.js.html +3 -3
  31. package/coverage/lcov-report/api/controllers/row/external.js.html +12 -6
  32. package/coverage/lcov-report/api/controllers/row/index.html +26 -26
  33. package/coverage/lcov-report/api/controllers/row/index.js.html +2 -2
  34. package/coverage/lcov-report/api/controllers/row/internal.js.html +9 -9
  35. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +2 -2
  36. package/coverage/lcov-report/api/controllers/row/utils.js.html +21 -30
  37. package/coverage/lcov-report/api/controllers/screen.js.html +2 -2
  38. package/coverage/lcov-report/api/controllers/script.js.html +3 -3
  39. package/coverage/lcov-report/api/controllers/table/external.js.html +980 -0
  40. package/coverage/lcov-report/api/controllers/table/index.html +56 -26
  41. package/coverage/lcov-report/api/controllers/table/index.js.html +80 -350
  42. package/coverage/lcov-report/api/controllers/table/internal.js.html +545 -0
  43. package/coverage/lcov-report/api/controllers/table/utils.js.html +172 -49
  44. package/coverage/lcov-report/api/controllers/templates.js.html +4 -4
  45. package/coverage/lcov-report/api/controllers/user.js.html +390 -33
  46. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  47. package/coverage/lcov-report/api/controllers/view/index.html +9 -9
  48. package/coverage/lcov-report/api/controllers/view/index.js.html +5 -5
  49. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  50. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +3 -3
  51. package/coverage/lcov-report/api/controllers/webhook.js.html +4 -4
  52. package/coverage/lcov-report/api/index.html +1 -1
  53. package/coverage/lcov-report/api/index.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/dev.js.html +2 -2
  65. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/index.html +4 -4
  67. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/query.js.html +5 -2
  73. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/static.js.html +2 -2
  79. package/coverage/lcov-report/api/routes/table.js.html +35 -2
  80. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  81. package/coverage/lcov-report/api/routes/user.js.html +17 -2
  82. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  83. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  84. package/coverage/lcov-report/automations/actions.js.html +2 -2
  85. package/coverage/lcov-report/automations/automationUtils.js.html +3 -3
  86. package/coverage/lcov-report/automations/bullboard.js.html +71 -20
  87. package/coverage/lcov-report/automations/index.html +33 -48
  88. package/coverage/lcov-report/automations/index.js.html +2 -2
  89. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/discord.js.html +3 -3
  94. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/executeScript.js.html +3 -3
  96. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/index.html +14 -14
  98. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  99. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +3 -3
  100. package/coverage/lcov-report/automations/steps/queryRows.js.html +3 -3
  101. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  102. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  103. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  104. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  105. package/coverage/lcov-report/automations/steps/utils.js.html +7 -7
  106. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  107. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  108. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  109. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  110. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  111. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  112. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  113. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  114. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  115. package/coverage/lcov-report/automations/triggers.js.html +22 -10
  116. package/coverage/lcov-report/automations/utils.js.html +42 -12
  117. package/coverage/lcov-report/constants/index.html +5 -5
  118. package/coverage/lcov-report/constants/index.js.html +49 -4
  119. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  120. package/coverage/lcov-report/constants/screens.js.html +1 -1
  121. package/coverage/lcov-report/db/client.js.html +2 -2
  122. package/coverage/lcov-report/db/inMemoryView.js.html +2 -2
  123. package/coverage/lcov-report/db/index.html +2 -2
  124. package/coverage/lcov-report/db/index.js.html +1 -1
  125. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +6 -6
  126. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  127. package/coverage/lcov-report/db/linkedRows/index.js.html +5 -5
  128. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +6 -6
  129. package/coverage/lcov-report/db/newid.js.html +1 -1
  130. package/coverage/lcov-report/db/utils.js.html +1 -1
  131. package/coverage/lcov-report/events/AutomationEmitter.js.html +6 -6
  132. package/coverage/lcov-report/events/index.html +6 -6
  133. package/coverage/lcov-report/events/index.js.html +1 -1
  134. package/coverage/lcov-report/events/utils.js.html +5 -5
  135. package/coverage/lcov-report/index.html +98 -83
  136. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  137. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  138. package/coverage/lcov-report/middleware/appInfo.js.html +2 -2
  139. package/coverage/lcov-report/middleware/authorized.js.html +11 -8
  140. package/coverage/lcov-report/middleware/builder.js.html +16 -7
  141. package/coverage/lcov-report/middleware/currentapp.js.html +6 -6
  142. package/coverage/lcov-report/middleware/index.html +23 -23
  143. package/coverage/lcov-report/middleware/joi-validator.js.html +2 -2
  144. package/coverage/lcov-report/middleware/resourceId.js.html +18 -18
  145. package/coverage/lcov-report/middleware/selfhost.js.html +2 -2
  146. package/coverage/lcov-report/middleware/usageQuota.js.html +4 -4
  147. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  148. package/coverage/lcov-report/{automations/thread.js.html → threads/automation.js.html} +83 -32
  149. package/coverage/lcov-report/threads/index.html +141 -0
  150. package/coverage/lcov-report/threads/index.js.html +260 -0
  151. package/coverage/lcov-report/threads/query.js.html +269 -0
  152. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  153. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  154. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  155. package/coverage/lcov-report/utilities/csvParser.js.html +144 -15
  156. package/coverage/lcov-report/utilities/global.js.html +26 -11
  157. package/coverage/lcov-report/utilities/index.html +49 -49
  158. package/coverage/lcov-report/utilities/index.js.html +65 -17
  159. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +30 -9
  160. package/coverage/lcov-report/utilities/queue/index.html +14 -14
  161. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  162. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  163. package/coverage/lcov-report/utilities/rowProcessor/index.html +4 -4
  164. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +2 -2
  165. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +3 -3
  166. package/coverage/lcov-report/utilities/scriptRunner.js.html +16 -7
  167. package/coverage/lcov-report/utilities/security.js.html +3 -3
  168. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  169. package/coverage/lcov-report/utilities/users.js.html +1 -1
  170. package/coverage/lcov-report/utilities/workerRequests.js.html +5 -5
  171. package/coverage/lcov.info +2301 -1599
  172. package/dist/api/controllers/application.js +16 -2
  173. package/dist/api/controllers/auth.js +3 -3
  174. package/dist/api/controllers/datasource.js +47 -16
  175. package/dist/api/controllers/deploy/index.js +2 -0
  176. package/dist/api/controllers/dev.js +4 -1
  177. package/dist/api/controllers/query.js +30 -58
  178. package/dist/api/controllers/row/ExternalRequest.js +37 -14
  179. package/dist/api/controllers/row/external.js +1 -0
  180. package/dist/api/controllers/row/utils.js +6 -12
  181. package/dist/api/controllers/static/index.js +1 -1
  182. package/dist/api/controllers/table/external.js +242 -0
  183. package/dist/api/controllers/table/index.js +49 -123
  184. package/dist/api/controllers/table/internal.js +150 -0
  185. package/dist/api/controllers/table/utils.js +62 -28
  186. package/dist/api/controllers/user.js +123 -11
  187. package/dist/api/routes/query.js +1 -1
  188. package/dist/api/routes/table.js +7 -1
  189. package/dist/api/routes/user.js +2 -1
  190. package/dist/app.js +2 -11
  191. package/dist/automations/bullboard.js +37 -10
  192. package/dist/automations/triggers.js +7 -3
  193. package/dist/automations/utils.js +11 -2
  194. package/dist/constants/index.js +13 -0
  195. package/dist/definitions/datasource.js +4 -0
  196. package/dist/integrations/base/IntegrationBase.js +2 -0
  197. package/dist/integrations/base/datasourcePlus.js +2 -0
  198. package/dist/integrations/base/sql.js +334 -180
  199. package/dist/integrations/base/sqlTable.js +143 -0
  200. package/dist/integrations/base/utils.js +30 -0
  201. package/dist/integrations/microsoftSqlServer.js +117 -9
  202. package/dist/integrations/mysql.js +14 -84
  203. package/dist/integrations/postgres.js +35 -28
  204. package/dist/integrations/utils.js +56 -9
  205. package/dist/middleware/authorized.js +2 -1
  206. package/dist/middleware/builder.js +4 -1
  207. package/dist/package.json +7 -6
  208. package/dist/tests/utilities/TestConfiguration.js +2 -1
  209. package/dist/{automations/thread.js → threads/automation.js} +23 -7
  210. package/dist/threads/index.js +58 -0
  211. package/dist/threads/query.js +61 -0
  212. package/dist/tsconfig.tsbuildinfo +1 -1
  213. package/dist/utilities/csvParser.js +46 -5
  214. package/dist/utilities/global.js +6 -2
  215. package/dist/utilities/index.js +18 -2
  216. package/dist/utilities/queue/inMemoryQueue.js +8 -0
  217. package/dist/utilities/scriptRunner.js +4 -1
  218. package/package.json +8 -7
  219. package/scripts/integrations/mssql/data/Dockerfile +9 -0
  220. package/scripts/integrations/mssql/data/entrypoint.sh +24 -0
  221. package/scripts/integrations/mssql/data/setup.sql +54 -0
  222. package/scripts/integrations/mssql/docker-compose.yaml +12 -0
  223. package/scripts/integrations/mssql/reset.sh +3 -0
  224. package/scripts/likeCypress.ts +33 -0
  225. package/src/api/controllers/application.js +19 -2
  226. package/src/api/controllers/auth.js +3 -0
  227. package/src/api/controllers/datasource.js +56 -16
  228. package/src/api/controllers/deploy/index.js +2 -0
  229. package/src/api/controllers/dev.js +4 -1
  230. package/src/api/controllers/query.js +29 -81
  231. package/src/api/controllers/row/ExternalRequest.ts +47 -16
  232. package/src/api/controllers/row/external.js +2 -0
  233. package/src/api/controllers/row/utils.js +8 -11
  234. package/src/api/controllers/static/index.js +1 -1
  235. package/src/api/controllers/table/external.js +300 -0
  236. package/src/api/controllers/table/index.js +47 -137
  237. package/src/api/controllers/table/internal.js +155 -0
  238. package/src/api/controllers/table/utils.js +74 -33
  239. package/src/api/controllers/user.js +132 -13
  240. package/src/api/routes/query.js +1 -0
  241. package/src/api/routes/table.js +11 -0
  242. package/src/api/routes/tests/datasource.spec.js +2 -2
  243. package/src/api/routes/tests/misc.spec.js +5 -1
  244. package/src/api/routes/tests/query.spec.js +0 -1
  245. package/src/api/routes/user.js +5 -0
  246. package/src/app.ts +2 -10
  247. package/src/automations/bullboard.js +28 -11
  248. package/src/automations/tests/automation.spec.js +3 -4
  249. package/src/automations/triggers.js +7 -3
  250. package/src/automations/utils.js +12 -2
  251. package/src/constants/index.js +15 -0
  252. package/src/definitions/common.ts +7 -1
  253. package/src/definitions/datasource.ts +9 -3
  254. package/src/integrations/airtable.ts +2 -1
  255. package/src/integrations/arangodb.ts +2 -1
  256. package/src/integrations/base/IntegrationBase.ts +6 -0
  257. package/src/integrations/base/datasourcePlus.ts +9 -0
  258. package/src/integrations/base/sql.ts +359 -222
  259. package/src/integrations/base/sqlTable.ts +180 -0
  260. package/src/integrations/base/utils.ts +22 -0
  261. package/src/integrations/couchdb.ts +2 -1
  262. package/src/integrations/dynamodb.ts +2 -1
  263. package/src/integrations/elasticsearch.ts +2 -1
  264. package/src/integrations/microsoftSqlServer.ts +145 -13
  265. package/src/integrations/mongodb.ts +2 -1
  266. package/src/integrations/mysql.ts +20 -94
  267. package/src/integrations/postgres.ts +45 -35
  268. package/src/integrations/rest.ts +2 -1
  269. package/src/integrations/s3.ts +2 -1
  270. package/src/integrations/tests/microsoftSqlServer.spec.js +22 -15
  271. package/src/integrations/tests/postgres.spec.js +4 -0
  272. package/src/integrations/utils.ts +60 -8
  273. package/src/middleware/authorized.js +2 -1
  274. package/src/middleware/builder.js +4 -1
  275. package/src/tests/utilities/TestConfiguration.js +2 -1
  276. package/src/{automations/thread.js → threads/automation.js} +24 -7
  277. package/src/threads/index.js +60 -0
  278. package/src/threads/query.js +63 -0
  279. package/src/utilities/csvParser.js +48 -5
  280. package/src/utilities/global.js +7 -2
  281. package/src/utilities/index.js +18 -2
  282. package/src/utilities/queue/inMemoryQueue.js +7 -0
  283. package/src/utilities/scriptRunner.js +4 -1
  284. package/src/utilities/tests/__snapshots__/csvParser.spec.js.snap +0 -6
  285. package/src/utilities/tests/csvParser.spec.js +3 -0
  286. package/tsconfig.json +1 -1
  287. package/builder/assets/index.34964222.js +0 -201
  288. package/builder/assets/index.7469e1e4.css +0 -29
  289. package/builder/assets/vendor.278a305c.js +0 -9
  290. package/builder/assets/vendor.d75d5eb7.css +0 -1
@@ -1,16 +1,28 @@
1
1
  const CouchDB = require("../../../db")
2
- const linkRows = require("../../../db/linkedRows")
2
+ const internal = require("./internal")
3
+ const external = require("./external")
3
4
  const csvParser = require("../../../utilities/csvParser")
5
+ const { isExternalTable } = require("../../../integrations/utils")
4
6
  const {
5
- getRowParams,
6
7
  getTableParams,
7
- generateTableID,
8
8
  getDatasourceParams,
9
9
  BudibaseInternalDB,
10
10
  } = require("../../../db/utils")
11
- const { FieldTypes } = require("../../../constants")
12
- const { TableSaveFunctions, getTable } = require("./utils")
11
+ const { getTable } = require("./utils")
13
12
 
13
+ function pickApi({ tableId, table }) {
14
+ if (table && !tableId) {
15
+ tableId = table._id
16
+ }
17
+ if (table && table.type === "external") {
18
+ return external
19
+ } else if (tableId && isExternalTable(tableId)) {
20
+ return external
21
+ }
22
+ return internal
23
+ }
24
+
25
+ // covers both internal and external
14
26
  exports.fetch = async function (ctx) {
15
27
  const db = new CouchDB(ctx.appId)
16
28
 
@@ -50,147 +62,45 @@ exports.find = async function (ctx) {
50
62
 
51
63
  exports.save = async function (ctx) {
52
64
  const appId = ctx.appId
53
- const db = new CouchDB(appId)
54
- const { dataImport, ...rest } = ctx.request.body
55
- let tableToSave = {
56
- type: "table",
57
- _id: generateTableID(),
58
- views: {},
59
- ...rest,
60
- }
61
-
62
- // if the table obj had an _id then it will have been retrieved
63
- let oldTable
64
- if (ctx.request.body && ctx.request.body._id) {
65
- oldTable = await db.get(ctx.request.body._id)
66
- }
67
-
68
- // saving a table is a complex operation, involving many different steps, this
69
- // has been broken out into a utility to make it more obvious/easier to manipulate
70
- const tableSaveFunctions = new TableSaveFunctions({
71
- db,
72
- ctx,
73
- oldTable,
74
- dataImport,
75
- })
76
- tableToSave = await tableSaveFunctions.before(tableToSave)
77
-
78
- // make sure that types don't change of a column, have to remove
79
- // the column if you want to change the type
80
- if (oldTable && oldTable.schema) {
81
- for (let propKey of Object.keys(tableToSave.schema)) {
82
- let column = tableToSave.schema[propKey]
83
- let oldColumn = oldTable.schema[propKey]
84
- if (oldColumn && oldColumn.type === "internal") {
85
- oldColumn.type = "auto"
86
- }
87
- if (oldColumn && oldColumn.type !== column.type) {
88
- ctx.throw(400, "Cannot change the type of a column")
89
- }
90
- }
91
- }
92
-
93
- // Don't rename if the name is the same
94
- let { _rename } = tableToSave
95
- /* istanbul ignore next */
96
- if (_rename && _rename.old === _rename.updated) {
97
- _rename = null
98
- delete tableToSave._rename
99
- }
100
-
101
- // rename row fields when table column is renamed
102
- /* istanbul ignore next */
103
- if (_rename && tableToSave.schema[_rename.updated].type === FieldTypes.LINK) {
104
- ctx.throw(400, "Cannot rename a linked column.")
105
- }
106
-
107
- tableToSave = await tableSaveFunctions.mid(tableToSave)
108
-
109
- // update schema of non-statistics views when new columns are added
110
- for (let view in tableToSave.views) {
111
- const tableView = tableToSave.views[view]
112
- if (!tableView) continue
113
-
114
- if (tableView.schema.group || tableView.schema.field) continue
115
- tableView.schema = tableToSave.schema
116
- }
117
-
118
- // update linked rows
119
- try {
120
- const linkResp = await linkRows.updateLinks({
121
- appId,
122
- eventType: oldTable
123
- ? linkRows.EventType.TABLE_UPDATED
124
- : linkRows.EventType.TABLE_SAVE,
125
- table: tableToSave,
126
- oldTable: oldTable,
127
- })
128
- if (linkResp != null && linkResp._rev) {
129
- tableToSave._rev = linkResp._rev
130
- }
131
- } catch (err) {
132
- ctx.throw(400, err)
133
- }
134
-
135
- // don't perform any updates until relationships have been
136
- // checked by the updateLinks function
137
- const updatedRows = tableSaveFunctions.getUpdatedRows()
138
- if (updatedRows && updatedRows.length !== 0) {
139
- await db.bulkDocs(updatedRows)
140
- }
141
- const result = await db.put(tableToSave)
142
- tableToSave._rev = result.rev
143
-
144
- tableToSave = await tableSaveFunctions.after(tableToSave)
145
-
146
- ctx.eventEmitter &&
147
- ctx.eventEmitter.emitTable(`table:save`, appId, tableToSave)
148
-
65
+ const table = ctx.request.body
66
+ const savedTable = await pickApi({ table }).save(ctx)
149
67
  ctx.status = 200
150
- ctx.message = `Table ${ctx.request.body.name} saved successfully.`
151
- ctx.body = tableToSave
68
+ ctx.message = `Table ${table.name} saved successfully.`
69
+ ctx.eventEmitter &&
70
+ ctx.eventEmitter.emitTable(`table:save`, appId, savedTable)
71
+ ctx.body = savedTable
152
72
  }
153
73
 
154
74
  exports.destroy = async function (ctx) {
155
75
  const appId = ctx.appId
156
- const db = new CouchDB(appId)
157
- const tableToDelete = await db.get(ctx.params.tableId)
158
-
159
- // Delete all rows for that table
160
- const rows = await db.allDocs(
161
- getRowParams(ctx.params.tableId, null, {
162
- include_docs: true,
163
- })
164
- )
165
- await db.bulkDocs(rows.rows.map(row => ({ ...row.doc, _deleted: true })))
166
-
167
- // update linked rows
168
- await linkRows.updateLinks({
169
- appId,
170
- eventType: linkRows.EventType.TABLE_DELETE,
171
- table: tableToDelete,
172
- })
173
-
174
- // don't remove the table itself until very end
175
- await db.remove(tableToDelete)
176
-
177
- // remove table search index
178
- const currentIndexes = await db.getIndexes()
179
- const existingIndex = currentIndexes.indexes.find(
180
- existing => existing.name === `search:${ctx.params.tableId}`
181
- )
182
- if (existingIndex) {
183
- await db.deleteIndex(existingIndex)
184
- }
185
-
76
+ const tableId = ctx.params.tableId
77
+ const deletedTable = await pickApi({ tableId }).destroy(ctx)
186
78
  ctx.eventEmitter &&
187
- ctx.eventEmitter.emitTable(`table:delete`, appId, tableToDelete)
79
+ ctx.eventEmitter.emitTable(`table:delete`, appId, deletedTable)
188
80
  ctx.status = 200
189
- ctx.body = { message: `Table ${ctx.params.tableId} deleted.` }
81
+ ctx.body = { message: `Table ${tableId} deleted.` }
82
+ }
83
+
84
+ exports.bulkImport = async function (ctx) {
85
+ const tableId = ctx.params.tableId
86
+ await pickApi({ tableId }).bulkImport(ctx)
87
+ // right now we don't trigger anything for bulk import because it
88
+ // can only be done in the builder, but in the future we may need to
89
+ // think about events for bulk items
90
+ ctx.status = 200
91
+ ctx.body = { message: `Bulk rows created.` }
190
92
  }
191
93
 
192
94
  exports.validateCSVSchema = async function (ctx) {
193
- const { csvString, schema = {} } = ctx.request.body
194
- const result = await csvParser.parse(csvString, schema)
95
+ // tableId being specified means its an import to an existing table
96
+ const { csvString, schema = {}, tableId } = ctx.request.body
97
+ let existingTable
98
+ if (tableId) {
99
+ existingTable = await getTable(ctx.appId, tableId)
100
+ }
101
+ let result = await csvParser.parse(csvString, schema)
102
+ if (existingTable) {
103
+ result = csvParser.updateSchema({ schema: result, existingTable })
104
+ }
195
105
  ctx.body = { schema: result }
196
106
  }
@@ -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,
@@ -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