@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
@@ -4,8 +4,8 @@ const CouchDB = require("../../../db")
4
4
  const { InternalTables } = require("../../../db/utils")
5
5
  const userController = require("../user")
6
6
  const { FieldTypes } = require("../../../constants")
7
- const { integrations } = require("../../../integrations")
8
7
  const { processStringSync } = require("@budibase/string-templates")
8
+ const { makeExternalQuery } = require("../../../integrations/base/utils")
9
9
 
10
10
  validateJs.extend(validateJs.validators.datetime, {
11
11
  parse: function (value) {
@@ -17,18 +17,11 @@ validateJs.extend(validateJs.validators.datetime, {
17
17
  },
18
18
  })
19
19
 
20
- exports.makeExternalQuery = async (appId, json) => {
20
+ exports.getDatasourceAndQuery = async (appId, json) => {
21
21
  const datasourceId = json.endpoint.datasourceId
22
22
  const db = new CouchDB(appId)
23
23
  const datasource = await db.get(datasourceId)
24
- const Integration = integrations[datasource.source]
25
- // query is the opinionated function
26
- if (Integration.prototype.query) {
27
- const integration = new Integration(datasource.config)
28
- return integration.query(json)
29
- } else {
30
- throw "Datasource does not support query."
31
- }
24
+ return makeExternalQuery(datasource, json)
32
25
  }
33
26
 
34
27
  exports.findRow = async (ctx, db, tableId, rowId) => {
@@ -68,7 +61,11 @@ exports.validate = async ({ appId, tableId, row, table }) => {
68
61
  let res
69
62
 
70
63
  // Validate.js doesn't seem to handle array
71
- if (table.schema[fieldName].type === FieldTypes.ARRAY) {
64
+ if (
65
+ table.schema[fieldName].type === FieldTypes.ARRAY &&
66
+ row[fieldName] &&
67
+ row[fieldName].length
68
+ ) {
72
69
  row[fieldName].map(val => {
73
70
  if (!constraints.inclusion.includes(val)) {
74
71
  errors[fieldName] = "Field not in list"
@@ -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
  }