@budibase/server 0.9.186 → 0.9.190-alpha.0

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 (309) hide show
  1. package/Dockerfile +7 -1
  2. package/LICENSE +1 -1
  3. package/__mocks__/mssql.ts +1 -1
  4. package/__mocks__/oracledb.ts +31 -0
  5. package/__mocks__/pg.ts +3 -0
  6. package/builder/assets/bb-space-man.5be51a45.svg +290 -0
  7. package/builder/assets/index.43f4c913.js +1316 -0
  8. package/builder/assets/index.7d405e74.css +1 -0
  9. package/builder/assets/oracle.66592424.png +0 -0
  10. package/builder/assets/vendor.470042a4.js +152 -0
  11. package/builder/assets/vendor.5e1fe644.css +1 -0
  12. package/builder/index.html +4 -4
  13. package/coverage/clover.xml +1520 -1087
  14. package/coverage/coverage-final.json +93 -89
  15. package/coverage/lcov-report/api/controllers/analytics.js.html +2 -2
  16. package/coverage/lcov-report/api/controllers/apikeys.js.html +2 -2
  17. package/coverage/lcov-report/api/controllers/application.js.html +64 -13
  18. package/coverage/lcov-report/api/controllers/auth.js.html +19 -10
  19. package/coverage/lcov-report/api/controllers/automation.js.html +3 -3
  20. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/component.js.html +4 -4
  23. package/coverage/lcov-report/api/controllers/datasource.js.html +232 -28
  24. package/coverage/lcov-report/api/controllers/dev.js.html +16 -7
  25. package/coverage/lcov-report/api/controllers/hosting.js.html +10 -10
  26. package/coverage/lcov-report/api/controllers/index.html +91 -91
  27. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/layout.js.html +3 -3
  29. package/coverage/lcov-report/api/controllers/metadata.js.html +3 -3
  30. package/coverage/lcov-report/api/controllers/permission.js.html +3 -3
  31. package/coverage/lcov-report/api/controllers/query.js.html +53 -209
  32. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  33. package/coverage/lcov-report/api/controllers/routing.js.html +3 -3
  34. package/coverage/lcov-report/api/controllers/row/external.js.html +17 -8
  35. package/coverage/lcov-report/api/controllers/row/index.html +26 -26
  36. package/coverage/lcov-report/api/controllers/row/index.js.html +2 -2
  37. package/coverage/lcov-report/api/controllers/row/internal.js.html +9 -9
  38. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +2 -2
  39. package/coverage/lcov-report/api/controllers/row/utils.js.html +21 -30
  40. package/coverage/lcov-report/api/controllers/screen.js.html +2 -2
  41. package/coverage/lcov-report/api/controllers/script.js.html +3 -3
  42. package/coverage/lcov-report/api/controllers/table/external.js.html +1001 -0
  43. package/coverage/lcov-report/api/controllers/table/index.html +56 -26
  44. package/coverage/lcov-report/api/controllers/table/index.js.html +80 -350
  45. package/coverage/lcov-report/api/controllers/table/internal.js.html +545 -0
  46. package/coverage/lcov-report/api/controllers/table/utils.js.html +175 -52
  47. package/coverage/lcov-report/api/controllers/templates.js.html +4 -4
  48. package/coverage/lcov-report/api/controllers/user.js.html +390 -33
  49. package/coverage/lcov-report/api/controllers/view/exporters.js.html +19 -4
  50. package/coverage/lcov-report/api/controllers/view/index.html +22 -22
  51. package/coverage/lcov-report/api/controllers/view/index.js.html +52 -13
  52. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  53. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +3 -3
  54. package/coverage/lcov-report/api/controllers/webhook.js.html +4 -4
  55. package/coverage/lcov-report/api/index.html +1 -1
  56. package/coverage/lcov-report/api/index.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/dev.js.html +2 -2
  68. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/index.html +4 -4
  70. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/query.js.html +5 -2
  76. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  81. package/coverage/lcov-report/api/routes/static.js.html +2 -2
  82. package/coverage/lcov-report/api/routes/table.js.html +35 -2
  83. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  84. package/coverage/lcov-report/api/routes/user.js.html +17 -2
  85. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  86. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  87. package/coverage/lcov-report/automations/actions.js.html +2 -2
  88. package/coverage/lcov-report/automations/automationUtils.js.html +4 -4
  89. package/coverage/lcov-report/automations/bullboard.js.html +71 -20
  90. package/coverage/lcov-report/automations/index.html +35 -50
  91. package/coverage/lcov-report/automations/index.js.html +2 -2
  92. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  96. package/coverage/lcov-report/automations/steps/discord.js.html +3 -3
  97. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  98. package/coverage/lcov-report/automations/steps/executeScript.js.html +3 -3
  99. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  100. package/coverage/lcov-report/automations/steps/index.html +14 -14
  101. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  102. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +3 -3
  103. package/coverage/lcov-report/automations/steps/queryRows.js.html +3 -3
  104. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  105. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  106. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  107. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  108. package/coverage/lcov-report/automations/steps/utils.js.html +7 -7
  109. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  110. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  111. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  112. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  113. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  114. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  115. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  116. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  117. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  118. package/coverage/lcov-report/automations/triggers.js.html +22 -10
  119. package/coverage/lcov-report/automations/utils.js.html +42 -12
  120. package/coverage/lcov-report/constants/index.html +5 -5
  121. package/coverage/lcov-report/constants/index.js.html +87 -21
  122. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  123. package/coverage/lcov-report/constants/screens.js.html +1 -1
  124. package/coverage/lcov-report/db/client.js.html +2 -2
  125. package/coverage/lcov-report/db/inMemoryView.js.html +2 -2
  126. package/coverage/lcov-report/db/index.html +2 -2
  127. package/coverage/lcov-report/db/index.js.html +1 -1
  128. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +6 -6
  129. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  130. package/coverage/lcov-report/db/linkedRows/index.js.html +5 -5
  131. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +6 -6
  132. package/coverage/lcov-report/db/newid.js.html +3 -3
  133. package/coverage/lcov-report/db/utils.js.html +57 -57
  134. package/coverage/lcov-report/events/AutomationEmitter.js.html +6 -6
  135. package/coverage/lcov-report/events/index.html +6 -6
  136. package/coverage/lcov-report/events/index.js.html +1 -1
  137. package/coverage/lcov-report/events/utils.js.html +5 -5
  138. package/coverage/lcov-report/index.html +108 -93
  139. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  140. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  141. package/coverage/lcov-report/middleware/appInfo.js.html +2 -2
  142. package/coverage/lcov-report/middleware/authorized.js.html +58 -34
  143. package/coverage/lcov-report/middleware/builder.js.html +16 -7
  144. package/coverage/lcov-report/middleware/currentapp.js.html +6 -6
  145. package/coverage/lcov-report/middleware/index.html +27 -27
  146. package/coverage/lcov-report/middleware/joi-validator.js.html +2 -2
  147. package/coverage/lcov-report/middleware/resourceId.js.html +18 -18
  148. package/coverage/lcov-report/middleware/selfhost.js.html +2 -2
  149. package/coverage/lcov-report/middleware/usageQuota.js.html +4 -4
  150. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  151. package/coverage/lcov-report/{automations/thread.js.html → threads/automation.js.html} +90 -33
  152. package/coverage/lcov-report/threads/index.html +141 -0
  153. package/coverage/lcov-report/threads/index.js.html +278 -0
  154. package/coverage/lcov-report/threads/query.js.html +278 -0
  155. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  156. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  157. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  158. package/coverage/lcov-report/utilities/csvParser.js.html +144 -15
  159. package/coverage/lcov-report/utilities/global.js.html +26 -11
  160. package/coverage/lcov-report/utilities/index.html +48 -48
  161. package/coverage/lcov-report/utilities/index.js.html +47 -14
  162. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +30 -9
  163. package/coverage/lcov-report/utilities/queue/index.html +14 -14
  164. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  165. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  166. package/coverage/lcov-report/utilities/rowProcessor/index.html +4 -4
  167. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +2 -2
  168. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +3 -3
  169. package/coverage/lcov-report/utilities/scriptRunner.js.html +16 -7
  170. package/coverage/lcov-report/utilities/security.js.html +3 -3
  171. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  172. package/coverage/lcov-report/utilities/users.js.html +1 -1
  173. package/coverage/lcov-report/utilities/workerRequests.js.html +5 -5
  174. package/coverage/lcov.info +2558 -1787
  175. package/dist/api/controllers/application.js +15 -1
  176. package/dist/api/controllers/auth.js +3 -3
  177. package/dist/api/controllers/datasource.js +67 -16
  178. package/dist/api/controllers/deploy/index.js +2 -0
  179. package/dist/api/controllers/dev.js +4 -1
  180. package/dist/api/controllers/query.js +30 -58
  181. package/dist/api/controllers/row/ExternalRequest.js +37 -14
  182. package/dist/api/controllers/row/external.js +3 -1
  183. package/dist/api/controllers/row/utils.js +6 -12
  184. package/dist/api/controllers/table/external.js +249 -0
  185. package/dist/api/controllers/table/index.js +49 -123
  186. package/dist/api/controllers/table/internal.js +150 -0
  187. package/dist/api/controllers/table/utils.js +62 -28
  188. package/dist/api/controllers/user.js +123 -11
  189. package/dist/api/controllers/view/exporters.js +4 -0
  190. package/dist/api/controllers/view/index.js +14 -2
  191. package/dist/api/routes/query.js +1 -1
  192. package/dist/api/routes/table.js +7 -1
  193. package/dist/api/routes/user.js +2 -1
  194. package/dist/app.js +2 -11
  195. package/dist/automations/bullboard.js +37 -10
  196. package/dist/automations/triggers.js +7 -3
  197. package/dist/automations/utils.js +11 -2
  198. package/dist/constants/index.js +19 -0
  199. package/dist/definitions/datasource.js +5 -0
  200. package/dist/environment.js +13 -0
  201. package/dist/integrations/base/IntegrationBase.js +2 -0
  202. package/dist/integrations/base/datasourcePlus.js +2 -0
  203. package/dist/integrations/base/sql.js +338 -180
  204. package/dist/integrations/base/sqlTable.js +143 -0
  205. package/dist/integrations/base/utils.js +30 -0
  206. package/dist/integrations/index.js +6 -0
  207. package/dist/integrations/microsoftSqlServer.js +117 -9
  208. package/dist/integrations/mysql.js +14 -84
  209. package/dist/integrations/oracle.js +345 -0
  210. package/dist/integrations/postgres.js +35 -28
  211. package/dist/integrations/utils.js +71 -9
  212. package/dist/middleware/authorized.js +22 -11
  213. package/dist/middleware/builder.js +4 -1
  214. package/dist/package.json +13 -8
  215. package/dist/tests/utilities/TestConfiguration.js +2 -1
  216. package/dist/{automations/thread.js → threads/automation.js} +26 -8
  217. package/dist/threads/index.js +64 -0
  218. package/dist/threads/query.js +64 -0
  219. package/dist/tsconfig.tsbuildinfo +1 -1
  220. package/dist/utilities/csvParser.js +46 -5
  221. package/dist/utilities/global.js +6 -2
  222. package/dist/utilities/index.js +11 -0
  223. package/dist/utilities/queue/inMemoryQueue.js +8 -0
  224. package/dist/utilities/scriptRunner.js +4 -1
  225. package/package.json +14 -9
  226. package/scripts/dev/manage.js +1 -0
  227. package/scripts/integrations/mssql/data/Dockerfile +9 -0
  228. package/scripts/integrations/mssql/data/entrypoint.sh +24 -0
  229. package/scripts/integrations/mssql/data/setup.sql +54 -0
  230. package/scripts/integrations/mssql/docker-compose.yaml +12 -0
  231. package/scripts/integrations/mssql/reset.sh +3 -0
  232. package/scripts/integrations/oracle/docker-compose.yml +19 -0
  233. package/scripts/integrations/oracle/instantclient/linux/x86-64/basiclite-21.4.zip +0 -0
  234. package/scripts/integrations/oracle/instantclient/linux/x86-64/install.sh +23 -0
  235. package/scripts/integrations/oracle/oracle.md +90 -0
  236. package/scripts/integrations/postgres/docker-compose.yml +3 -3
  237. package/scripts/likeCypress.ts +33 -0
  238. package/src/api/controllers/application.js +18 -1
  239. package/src/api/controllers/auth.js +3 -0
  240. package/src/api/controllers/datasource.js +84 -16
  241. package/src/api/controllers/deploy/index.js +2 -0
  242. package/src/api/controllers/dev.js +4 -1
  243. package/src/api/controllers/query.js +29 -81
  244. package/src/api/controllers/row/ExternalRequest.ts +47 -16
  245. package/src/api/controllers/row/external.js +4 -1
  246. package/src/api/controllers/row/utils.js +8 -11
  247. package/src/api/controllers/table/external.js +307 -0
  248. package/src/api/controllers/table/index.js +47 -137
  249. package/src/api/controllers/table/internal.js +155 -0
  250. package/src/api/controllers/table/utils.js +74 -33
  251. package/src/api/controllers/user.js +132 -13
  252. package/src/api/controllers/view/exporters.js +5 -0
  253. package/src/api/controllers/view/index.js +15 -2
  254. package/src/api/routes/query.js +1 -0
  255. package/src/api/routes/table.js +11 -0
  256. package/src/api/routes/tests/datasource.spec.js +2 -2
  257. package/src/api/routes/tests/misc.spec.js +5 -1
  258. package/src/api/routes/tests/query.spec.js +0 -1
  259. package/src/api/routes/user.js +5 -0
  260. package/src/app.ts +2 -10
  261. package/src/automations/bullboard.js +28 -11
  262. package/src/automations/tests/automation.spec.js +3 -4
  263. package/src/automations/triggers.js +7 -3
  264. package/src/automations/utils.js +12 -2
  265. package/src/constants/index.js +22 -0
  266. package/src/definitions/common.ts +7 -1
  267. package/src/definitions/datasource.ts +10 -3
  268. package/src/environment.js +15 -0
  269. package/src/integrations/airtable.ts +2 -1
  270. package/src/integrations/arangodb.ts +2 -1
  271. package/src/integrations/base/IntegrationBase.ts +6 -0
  272. package/src/integrations/base/datasourcePlus.ts +9 -0
  273. package/src/integrations/base/sql.ts +362 -222
  274. package/src/integrations/base/sqlTable.ts +180 -0
  275. package/src/integrations/base/utils.ts +22 -0
  276. package/src/integrations/couchdb.ts +2 -1
  277. package/src/integrations/dynamodb.ts +2 -1
  278. package/src/integrations/elasticsearch.ts +2 -1
  279. package/src/integrations/index.ts +7 -0
  280. package/src/integrations/microsoftSqlServer.ts +145 -13
  281. package/src/integrations/mongodb.ts +2 -1
  282. package/src/integrations/mysql.ts +20 -94
  283. package/src/integrations/oracle.ts +445 -0
  284. package/src/integrations/postgres.ts +45 -35
  285. package/src/integrations/rest.ts +2 -1
  286. package/src/integrations/s3.ts +2 -1
  287. package/src/integrations/tests/microsoftSqlServer.spec.js +22 -15
  288. package/src/integrations/tests/oracle.spec.js +94 -0
  289. package/src/integrations/tests/postgres.spec.js +4 -0
  290. package/src/integrations/utils.ts +74 -8
  291. package/src/middleware/authorized.js +26 -18
  292. package/src/middleware/builder.js +4 -1
  293. package/src/middleware/tests/authorized.spec.js +2 -0
  294. package/src/tests/utilities/TestConfiguration.js +2 -1
  295. package/src/{automations/thread.js → threads/automation.js} +27 -8
  296. package/src/threads/index.js +66 -0
  297. package/src/threads/query.js +66 -0
  298. package/src/utilities/csvParser.js +48 -5
  299. package/src/utilities/global.js +7 -2
  300. package/src/utilities/index.js +11 -0
  301. package/src/utilities/queue/inMemoryQueue.js +7 -0
  302. package/src/utilities/scriptRunner.js +4 -1
  303. package/src/utilities/tests/__snapshots__/csvParser.spec.js.snap +0 -6
  304. package/src/utilities/tests/csvParser.spec.js +3 -0
  305. package/tsconfig.json +1 -1
  306. package/builder/assets/index.10556cc1.js +0 -201
  307. package/builder/assets/index.7469e1e4.css +0 -29
  308. package/builder/assets/vendor.278a305c.js +0 -9
  309. package/builder/assets/vendor.d75d5eb7.css +0 -1
@@ -0,0 +1,307 @@
1
+ const CouchDB = require("../../../db")
2
+ const {
3
+ buildExternalTableId,
4
+ breakExternalTableId,
5
+ } = require("../../../integrations/utils")
6
+ const {
7
+ getTable,
8
+ generateForeignKey,
9
+ generateJunctionTableName,
10
+ foreignKeyStructure,
11
+ hasTypeChanged,
12
+ } = require("./utils")
13
+ const {
14
+ DataSourceOperation,
15
+ FieldTypes,
16
+ RelationshipTypes,
17
+ } = require("../../../constants")
18
+ const { makeExternalQuery } = require("../../../integrations/base/utils")
19
+ const { cloneDeep } = require("lodash/fp")
20
+ const csvParser = require("../../../utilities/csvParser")
21
+ const { handleRequest } = require("../row/external")
22
+
23
+ async function makeTableRequest(
24
+ datasource,
25
+ operation,
26
+ table,
27
+ tables,
28
+ oldTable = null
29
+ ) {
30
+ const json = {
31
+ endpoint: {
32
+ datasourceId: datasource._id,
33
+ entityId: table._id,
34
+ operation,
35
+ },
36
+ meta: {
37
+ tables,
38
+ },
39
+ table,
40
+ }
41
+ if (oldTable) {
42
+ json.meta.table = oldTable
43
+ }
44
+ return makeExternalQuery(datasource, json)
45
+ }
46
+
47
+ function cleanupRelationships(table, tables, oldTable = null) {
48
+ const tableToIterate = oldTable ? oldTable : table
49
+ // clean up relationships in couch table schemas
50
+ for (let [key, schema] of Object.entries(tableToIterate.schema)) {
51
+ if (
52
+ schema.type === FieldTypes.LINK &&
53
+ (!oldTable || table.schema[key] == null)
54
+ ) {
55
+ const relatedTable = Object.values(tables).find(
56
+ table => table._id === schema.tableId
57
+ )
58
+ const foreignKey = schema.foreignKey
59
+ if (!relatedTable || !foreignKey) {
60
+ continue
61
+ }
62
+ for (let [relatedKey, relatedSchema] of Object.entries(
63
+ relatedTable.schema
64
+ )) {
65
+ if (
66
+ relatedSchema.type === FieldTypes.LINK &&
67
+ relatedSchema.fieldName === foreignKey
68
+ ) {
69
+ delete relatedTable.schema[relatedKey]
70
+ }
71
+ }
72
+ }
73
+ }
74
+ }
75
+
76
+ function getDatasourceId(table) {
77
+ if (!table) {
78
+ throw "No table supplied"
79
+ }
80
+ if (table.sourceId) {
81
+ return table.sourceId
82
+ }
83
+ return breakExternalTableId(table._id).datasourceId
84
+ }
85
+
86
+ function otherRelationshipType(type) {
87
+ if (type === RelationshipTypes.MANY_TO_MANY) {
88
+ return RelationshipTypes.MANY_TO_MANY
89
+ }
90
+ return type === RelationshipTypes.ONE_TO_MANY
91
+ ? RelationshipTypes.MANY_TO_ONE
92
+ : RelationshipTypes.ONE_TO_MANY
93
+ }
94
+
95
+ function generateManyLinkSchema(datasource, column, table, relatedTable) {
96
+ const primary = table.name + table.primary[0]
97
+ const relatedPrimary = relatedTable.name + relatedTable.primary[0]
98
+ const jcTblName = generateJunctionTableName(column, table, relatedTable)
99
+ // first create the new table
100
+ const junctionTable = {
101
+ _id: buildExternalTableId(datasource._id, jcTblName),
102
+ name: jcTblName,
103
+ primary: [primary, relatedPrimary],
104
+ constrained: [primary, relatedPrimary],
105
+ schema: {
106
+ [primary]: foreignKeyStructure(primary, {
107
+ toTable: table.name,
108
+ toKey: table.primary[0],
109
+ }),
110
+ [relatedPrimary]: foreignKeyStructure(relatedPrimary, {
111
+ toTable: relatedTable.name,
112
+ toKey: relatedTable.primary[0],
113
+ }),
114
+ },
115
+ }
116
+ column.through = junctionTable._id
117
+ column.throughFrom = primary
118
+ column.throughTo = relatedPrimary
119
+ column.fieldName = relatedPrimary
120
+ return junctionTable
121
+ }
122
+
123
+ function generateLinkSchema(column, table, relatedTable, type) {
124
+ const isOneSide = type === RelationshipTypes.ONE_TO_MANY
125
+ const primary = isOneSide ? relatedTable.primary[0] : table.primary[0]
126
+ // generate a foreign key
127
+ const foreignKey = generateForeignKey(column, relatedTable)
128
+ column.relationshipType = type
129
+ column.foreignKey = isOneSide ? foreignKey : primary
130
+ column.fieldName = isOneSide ? primary : foreignKey
131
+ return foreignKey
132
+ }
133
+
134
+ function generateRelatedSchema(linkColumn, table, relatedTable, columnName) {
135
+ // generate column for other table
136
+ const relatedSchema = cloneDeep(linkColumn)
137
+ // swap them from the main link
138
+ if (linkColumn.foreignKey) {
139
+ relatedSchema.fieldName = linkColumn.foreignKey
140
+ relatedSchema.foreignKey = linkColumn.fieldName
141
+ }
142
+ // is many to many
143
+ else {
144
+ // don't need to copy through, already got it
145
+ relatedSchema.fieldName = linkColumn.throughFrom
146
+ relatedSchema.throughTo = linkColumn.throughFrom
147
+ relatedSchema.throughFrom = linkColumn.throughTo
148
+ }
149
+ relatedSchema.relationshipType = otherRelationshipType(
150
+ linkColumn.relationshipType
151
+ )
152
+ relatedSchema.tableId = relatedTable._id
153
+ relatedSchema.name = columnName
154
+ table.schema[columnName] = relatedSchema
155
+ }
156
+
157
+ function isRelationshipSetup(column) {
158
+ return column.foreignKey || column.through
159
+ }
160
+
161
+ exports.save = async function (ctx) {
162
+ const appId = ctx.appId
163
+ const table = ctx.request.body
164
+ // can't do this right now
165
+ delete table.dataImport
166
+ const datasourceId = getDatasourceId(ctx.request.body)
167
+ // table doesn't exist already, note that it is created
168
+ if (!table._id) {
169
+ table.created = true
170
+ }
171
+ let tableToSave = {
172
+ type: "table",
173
+ _id: buildExternalTableId(datasourceId, table.name),
174
+ ...table,
175
+ }
176
+
177
+ let oldTable
178
+ if (ctx.request.body && ctx.request.body._id) {
179
+ oldTable = await getTable(appId, ctx.request.body._id)
180
+ }
181
+
182
+ if (hasTypeChanged(tableToSave, oldTable)) {
183
+ ctx.throw(400, "A column type has changed.")
184
+ }
185
+
186
+ const db = new CouchDB(appId)
187
+ const datasource = await db.get(datasourceId)
188
+ const oldTables = cloneDeep(datasource.entities)
189
+ const tables = datasource.entities
190
+
191
+ const extraTablesToUpdate = []
192
+
193
+ // check if relations need setup
194
+ for (let schema of Object.values(tableToSave.schema)) {
195
+ if (schema.type !== FieldTypes.LINK || isRelationshipSetup(schema)) {
196
+ continue
197
+ }
198
+ const relatedTable = Object.values(tables).find(
199
+ table => table._id === schema.tableId
200
+ )
201
+ const relatedColumnName = schema.fieldName
202
+ const relationType = schema.relationshipType
203
+ if (relationType === RelationshipTypes.MANY_TO_MANY) {
204
+ const junctionTable = generateManyLinkSchema(
205
+ datasource,
206
+ schema,
207
+ table,
208
+ relatedTable
209
+ )
210
+ if (tables[junctionTable.name]) {
211
+ throw "Junction table already exists, cannot create another relationship."
212
+ }
213
+ tables[junctionTable.name] = junctionTable
214
+ extraTablesToUpdate.push(junctionTable)
215
+ } else {
216
+ const fkTable =
217
+ relationType === RelationshipTypes.ONE_TO_MANY ? table : relatedTable
218
+ const foreignKey = generateLinkSchema(
219
+ schema,
220
+ table,
221
+ relatedTable,
222
+ relationType
223
+ )
224
+ fkTable.schema[foreignKey] = foreignKeyStructure(foreignKey)
225
+ if (fkTable.constrained == null) {
226
+ fkTable.constrained = []
227
+ }
228
+ if (fkTable.constrained.indexOf(foreignKey) === -1) {
229
+ fkTable.constrained.push(foreignKey)
230
+ }
231
+ // foreign key is in other table, need to save it to external
232
+ if (fkTable._id !== table._id) {
233
+ extraTablesToUpdate.push(fkTable)
234
+ }
235
+ }
236
+ generateRelatedSchema(schema, relatedTable, table, relatedColumnName)
237
+ schema.main = true
238
+ }
239
+
240
+ cleanupRelationships(tableToSave, tables, oldTable)
241
+
242
+ const operation = oldTable
243
+ ? DataSourceOperation.UPDATE_TABLE
244
+ : DataSourceOperation.CREATE_TABLE
245
+ await makeTableRequest(datasource, operation, tableToSave, tables, oldTable)
246
+ // update any extra tables (like foreign keys in other tables)
247
+ for (let extraTable of extraTablesToUpdate) {
248
+ const oldExtraTable = oldTables[extraTable.name]
249
+ let op = oldExtraTable
250
+ ? DataSourceOperation.UPDATE_TABLE
251
+ : DataSourceOperation.CREATE_TABLE
252
+ await makeTableRequest(datasource, op, extraTable, tables, oldExtraTable)
253
+ }
254
+
255
+ // make sure the constrained list, all still exist
256
+ if (Array.isArray(tableToSave.constrained)) {
257
+ tableToSave.constrained = tableToSave.constrained.filter(constraint =>
258
+ Object.keys(tableToSave.schema).includes(constraint)
259
+ )
260
+ }
261
+
262
+ // store it into couch now for budibase reference
263
+ datasource.entities[tableToSave.name] = tableToSave
264
+ await db.put(datasource)
265
+
266
+ return tableToSave
267
+ }
268
+
269
+ exports.destroy = async function (ctx) {
270
+ const appId = ctx.appId
271
+ const tableToDelete = await getTable(appId, ctx.params.tableId)
272
+ if (!tableToDelete || !tableToDelete.created) {
273
+ ctx.throw(400, "Cannot delete tables which weren't created in Budibase.")
274
+ }
275
+ const datasourceId = getDatasourceId(tableToDelete)
276
+
277
+ const db = new CouchDB(appId)
278
+ const datasource = await db.get(datasourceId)
279
+ const tables = datasource.entities
280
+
281
+ const operation = DataSourceOperation.DELETE_TABLE
282
+ await makeTableRequest(datasource, operation, tableToDelete, tables)
283
+
284
+ cleanupRelationships(tableToDelete, tables)
285
+
286
+ delete datasource.entities[tableToDelete.name]
287
+ await db.put(datasource)
288
+
289
+ return tableToDelete
290
+ }
291
+
292
+ exports.bulkImport = async function (ctx) {
293
+ const appId = ctx.appId
294
+ const table = await getTable(appId, ctx.params.tableId)
295
+ const { dataImport } = ctx.request.body
296
+ if (!dataImport || !dataImport.schema || !dataImport.csvString) {
297
+ ctx.throw(400, "Provided data import information is invalid.")
298
+ }
299
+ const rows = await csvParser.transform({
300
+ ...dataImport,
301
+ existingTable: table,
302
+ })
303
+ await handleRequest(appId, DataSourceOperation.BULK_CREATE, table._id, {
304
+ rows,
305
+ })
306
+ return table
307
+ }
@@ -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
+ }