@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,10 +1,11 @@
1
1
  const { processString } = require("@budibase/string-templates")
2
2
  const CouchDB = require("../../db")
3
3
  const { generateQueryID, getQueryParams } = require("../../db/utils")
4
- const { integrations } = require("../../integrations")
5
4
  const { BaseQueryVerbs } = require("../../constants")
6
5
  const env = require("../../environment")
7
- const ScriptRunner = require("../../utilities/scriptRunner")
6
+ const { Thread, ThreadType } = require("../../threads")
7
+
8
+ const Runner = new Thread(ThreadType.QUERY, { timeoutMs: 10000 })
8
9
 
9
10
  // simple function to append "readable" to all read queries
10
11
  function enrichQueries(input) {
@@ -18,47 +19,6 @@ function enrichQueries(input) {
18
19
  return wasArray ? queries : queries[0]
19
20
  }
20
21
 
21
- function formatResponse(resp) {
22
- if (typeof resp === "string") {
23
- try {
24
- resp = JSON.parse(resp)
25
- } catch (err) {
26
- resp = { response: resp }
27
- }
28
- }
29
- return resp
30
- }
31
-
32
- async function runAndTransform(
33
- integration,
34
- queryVerb,
35
- enrichedQuery,
36
- transformer
37
- ) {
38
- let rows = formatResponse(await integration[queryVerb](enrichedQuery))
39
-
40
- // transform as required
41
- if (transformer) {
42
- const runner = new ScriptRunner(transformer, { data: rows })
43
- rows = runner.execute()
44
- }
45
-
46
- // needs to an array for next step
47
- if (!Array.isArray(rows)) {
48
- rows = [rows]
49
- }
50
-
51
- // map into JSON if just raw primitive here
52
- if (rows.find(row => typeof row !== "object")) {
53
- rows = rows.map(value => ({ value }))
54
- }
55
-
56
- // get all the potential fields in the schema
57
- let keys = rows.flatMap(Object.keys)
58
-
59
- return { rows, keys }
60
- }
61
-
62
22
  exports.fetch = async function (ctx) {
63
23
  const db = new CouchDB(ctx.appId)
64
24
 
@@ -98,7 +58,9 @@ async function enrichQueryFields(fields, parameters = {}) {
98
58
  enrichedQuery[key] = await enrichQueryFields(fields[key], parameters)
99
59
  } else if (typeof fields[key] === "string") {
100
60
  // enrich string value as normal
101
- enrichedQuery[key] = await processString(fields[key], parameters)
61
+ enrichedQuery[key] = await processString(fields[key], parameters, {
62
+ noHelpers: true,
63
+ })
102
64
  } else {
103
65
  enrichedQuery[key] = fields[key]
104
66
  }
@@ -141,30 +103,23 @@ exports.preview = async function (ctx) {
141
103
 
142
104
  const datasource = await db.get(ctx.request.body.datasourceId)
143
105
 
144
- const Integration = integrations[datasource.source]
145
-
146
- if (!Integration) {
147
- ctx.throw(400, "Integration type does not exist.")
148
- }
149
-
150
106
  const { fields, parameters, queryVerb, transformer } = ctx.request.body
151
107
  const enrichedQuery = await enrichQueryFields(fields, parameters)
152
- const integration = new Integration(datasource.config)
153
108
 
154
- const { rows, keys } = await runAndTransform(
155
- integration,
156
- queryVerb,
157
- enrichedQuery,
158
- transformer
159
- )
109
+ try {
110
+ const { rows, keys } = await Runner.run({
111
+ datasource,
112
+ queryVerb,
113
+ query: enrichedQuery,
114
+ transformer,
115
+ })
160
116
 
161
- ctx.body = {
162
- rows,
163
- schemaFields: [...new Set(keys)],
164
- }
165
- // cleanup
166
- if (integration.end) {
167
- integration.end()
117
+ ctx.body = {
118
+ rows,
119
+ schemaFields: [...new Set(keys)],
120
+ }
121
+ } catch (err) {
122
+ ctx.throw(400, err)
168
123
  }
169
124
  }
170
125
 
@@ -174,29 +129,22 @@ exports.execute = async function (ctx) {
174
129
  const query = await db.get(ctx.params.queryId)
175
130
  const datasource = await db.get(query.datasourceId)
176
131
 
177
- const Integration = integrations[datasource.source]
178
-
179
- if (!Integration) {
180
- ctx.throw(400, "Integration type does not exist.")
181
- }
182
-
183
132
  const enrichedQuery = await enrichQueryFields(
184
133
  query.fields,
185
134
  ctx.request.body.parameters
186
135
  )
187
- const integration = new Integration(datasource.config)
188
136
 
189
137
  // call the relevant CRUD method on the integration class
190
- const { rows } = await runAndTransform(
191
- integration,
192
- query.queryVerb,
193
- enrichedQuery,
194
- query.transformer
195
- )
196
- ctx.body = rows
197
- // cleanup
198
- if (integration.end) {
199
- integration.end()
138
+ try {
139
+ const { rows } = await Runner.run({
140
+ datasource,
141
+ queryVerb: query.queryVerb,
142
+ query: enrichedQuery,
143
+ transformer: query.transformer,
144
+ })
145
+ ctx.body = rows
146
+ } catch (err) {
147
+ ctx.throw(400, err)
200
148
  }
201
149
  }
202
150
 
@@ -33,10 +33,11 @@ interface RunConfig {
33
33
  sort?: SortJson
34
34
  paginate?: PaginationJson
35
35
  row?: Row
36
+ rows?: Row[]
36
37
  }
37
38
 
38
39
  module External {
39
- const { makeExternalQuery } = require("./utils")
40
+ const { getDatasourceAndQuery } = require("./utils")
40
41
  const {
41
42
  DataSourceOperation,
42
43
  FieldTypes,
@@ -46,6 +47,7 @@ module External {
46
47
  const { processObjectSync } = require("@budibase/string-templates")
47
48
  const { cloneDeep } = require("lodash/fp")
48
49
  const CouchDB = require("../../../db")
50
+ const { processFormulas } = require("../../../utilities/rowProcessor/utils")
49
51
 
50
52
  function buildFilters(
51
53
  id: string | undefined,
@@ -162,8 +164,8 @@ module External {
162
164
  }
163
165
  }
164
166
 
165
- function basicProcessing(row: Row, table: Table) {
166
- const thisRow: { [key: string]: any } = {}
167
+ function basicProcessing(row: Row, table: Table): Row {
168
+ const thisRow: Row = {}
167
169
  // filter the row down to what is actually the row (not joined)
168
170
  for (let fieldName of Object.keys(table.schema)) {
169
171
  const value = row[`${table.name}.${fieldName}`] || row[fieldName]
@@ -178,6 +180,23 @@ module External {
178
180
  return thisRow
179
181
  }
180
182
 
183
+ function fixArrayTypes(row: Row, table: Table) {
184
+ for (let [fieldName, schema] of Object.entries(table.schema)) {
185
+ if (
186
+ schema.type === FieldTypes.ARRAY &&
187
+ typeof row[fieldName] === "string"
188
+ ) {
189
+ try {
190
+ row[fieldName] = JSON.parse(row[fieldName])
191
+ } catch (err) {
192
+ // couldn't convert back to array, ignore
193
+ delete row[fieldName]
194
+ }
195
+ }
196
+ }
197
+ return row
198
+ }
199
+
181
200
  function isMany(field: FieldSchema) {
182
201
  return (
183
202
  field.relationshipType && field.relationshipType.split("-")[0] === "many"
@@ -225,7 +244,12 @@ module External {
225
244
  manyRelationships: ManyRelationship[] = []
226
245
  for (let [key, field] of Object.entries(table.schema)) {
227
246
  // if set already, or not set just skip it
228
- if ((!row[key] && row[key] !== "") || newRow[key] || field.autocolumn) {
247
+ if (
248
+ row[key] == null ||
249
+ newRow[key] ||
250
+ field.autocolumn ||
251
+ field.type === FieldTypes.FORMULA
252
+ ) {
229
253
  continue
230
254
  }
231
255
  // if its an empty string then it means return the column to null (if possible)
@@ -336,7 +360,7 @@ module External {
336
360
  table: Table,
337
361
  relationships: RelationshipsJson[]
338
362
  ) {
339
- if (rows[0].read === true) {
363
+ if (!rows || rows.length === 0 || rows[0].read === true) {
340
364
  return []
341
365
  }
342
366
  let finalRows: { [key: string]: Row } = {}
@@ -352,7 +376,10 @@ module External {
352
376
  )
353
377
  continue
354
378
  }
355
- const thisRow = basicProcessing(row, table)
379
+ const thisRow = fixArrayTypes(basicProcessing(row, table), table)
380
+ if (thisRow._id == null) {
381
+ throw "Unable to generate row ID for SQL rows"
382
+ }
356
383
  finalRows[thisRow._id] = thisRow
357
384
  // do this at end once its been added to the final rows
358
385
  finalRows = this.updateRelationshipColumns(
@@ -361,7 +388,7 @@ module External {
361
388
  relationships
362
389
  )
363
390
  }
364
- return Object.values(finalRows)
391
+ return processFormulas(table, Object.values(finalRows))
365
392
  }
366
393
 
367
394
  /**
@@ -428,7 +455,7 @@ module External {
428
455
  const tableId = isMany ? field.through : field.tableId
429
456
  const manyKey = field.throughFrom || primaryKey
430
457
  const fieldName = isMany ? manyKey : field.fieldName
431
- const response = await makeExternalQuery(this.appId, {
458
+ const response = await getDatasourceAndQuery(this.appId, {
432
459
  endpoint: getEndpoint(tableId, DataSourceOperation.READ),
433
460
  filters: {
434
461
  equal: {
@@ -479,7 +506,7 @@ module External {
479
506
  : DataSourceOperation.CREATE
480
507
  if (!found) {
481
508
  promises.push(
482
- makeExternalQuery(appId, {
509
+ getDatasourceAndQuery(appId, {
483
510
  endpoint: getEndpoint(tableId, operation),
484
511
  // if we're doing many relationships then we're writing, only one response
485
512
  body,
@@ -509,7 +536,7 @@ module External {
509
536
  : DataSourceOperation.UPDATE
510
537
  const body = isMany ? null : { [colName]: null }
511
538
  promises.push(
512
- makeExternalQuery(this.appId, {
539
+ getDatasourceAndQuery(this.appId, {
513
540
  endpoint: getEndpoint(tableId, op),
514
541
  body,
515
542
  filters,
@@ -532,16 +559,17 @@ module External {
532
559
  table: Table,
533
560
  includeRelations: IncludeRelationships = IncludeRelationships.INCLUDE
534
561
  ) {
535
- function extractNonLinkFieldNames(table: Table, existing: string[] = []) {
562
+ function extractRealFields(table: Table, existing: string[] = []) {
536
563
  return Object.entries(table.schema)
537
564
  .filter(
538
565
  column =>
539
566
  column[1].type !== FieldTypes.LINK &&
567
+ column[1].type !== FieldTypes.FORMULA &&
540
568
  !existing.find((field: string) => field === column[0])
541
569
  )
542
570
  .map(column => `${table.name}.${column[0]}`)
543
571
  }
544
- let fields = extractNonLinkFieldNames(table)
572
+ let fields = extractRealFields(table)
545
573
  for (let field of Object.values(table.schema)) {
546
574
  if (field.type !== FieldTypes.LINK || !includeRelations) {
547
575
  continue
@@ -549,7 +577,7 @@ module External {
549
577
  const { tableName: linkTableName } = breakExternalTableId(field.tableId)
550
578
  const linkTable = this.tables[linkTableName]
551
579
  if (linkTable) {
552
- const linkedFields = extractNonLinkFieldNames(linkTable, fields)
580
+ const linkedFields = extractRealFields(linkTable, fields)
553
581
  fields = fields.concat(linkedFields)
554
582
  }
555
583
  }
@@ -573,7 +601,10 @@ module External {
573
601
  throw `Unable to process query, table "${tableName}" not defined.`
574
602
  }
575
603
  // look for specific components of config which may not be considered acceptable
576
- let { id, row, filters, sort, paginate } = cleanupConfig(config, table)
604
+ let { id, row, filters, sort, paginate, rows } = cleanupConfig(
605
+ config,
606
+ table
607
+ )
577
608
  filters = buildFilters(id, filters || {}, table)
578
609
  const relationships = this.buildRelationships(table)
579
610
  // clean up row on ingress using schema
@@ -599,7 +630,7 @@ module External {
599
630
  sort,
600
631
  paginate,
601
632
  relationships,
602
- body: row,
633
+ body: row || rows,
603
634
  // pass an id filter into extra, purely for mysql/returning
604
635
  extra: {
605
636
  idFilter: buildFilters(id || generateIdForRow(row, table), {}, table),
@@ -609,7 +640,7 @@ module External {
609
640
  },
610
641
  }
611
642
  // can't really use response right now
612
- const response = await makeExternalQuery(appId, json)
643
+ const response = await getDatasourceAndQuery(appId, json)
613
644
  // handle many to many relationships now if we know the ID (could be auto increment)
614
645
  if (
615
646
  operation !== DataSourceOperation.READ &&
@@ -30,6 +30,8 @@ async function handleRequest(appId, operation, tableId, opts = {}) {
30
30
  )
31
31
  }
32
32
 
33
+ exports.handleRequest = handleRequest
34
+
33
35
  exports.patch = async ctx => {
34
36
  const appId = ctx.appId
35
37
  const inputs = ctx.request.body
@@ -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"
@@ -87,7 +87,7 @@ exports.serveApp = async function (ctx) {
87
87
  title: appInfo.name,
88
88
  production: env.isProd(),
89
89
  appId,
90
- clientLibPath: clientLibraryPath(appId),
90
+ clientLibPath: clientLibraryPath(appId, appInfo.version),
91
91
  })
92
92
 
93
93
  const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`)
@@ -0,0 +1,300 @@
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
165
+ delete table.dataImport
166
+ const datasourceId = getDatasourceId(ctx.request.body)
167
+ let tableToSave = {
168
+ type: "table",
169
+ _id: buildExternalTableId(datasourceId, table.name),
170
+ ...table,
171
+ }
172
+
173
+ let oldTable
174
+ if (ctx.request.body && ctx.request.body._id) {
175
+ oldTable = await getTable(appId, ctx.request.body._id)
176
+ }
177
+
178
+ if (hasTypeChanged(tableToSave, oldTable)) {
179
+ ctx.throw(400, "A column type has changed.")
180
+ }
181
+
182
+ const db = new CouchDB(appId)
183
+ const datasource = await db.get(datasourceId)
184
+ const oldTables = cloneDeep(datasource.entities)
185
+ const tables = datasource.entities
186
+
187
+ const extraTablesToUpdate = []
188
+
189
+ // check if relations need setup
190
+ for (let schema of Object.values(tableToSave.schema)) {
191
+ if (schema.type !== FieldTypes.LINK || isRelationshipSetup(schema)) {
192
+ continue
193
+ }
194
+ const relatedTable = Object.values(tables).find(
195
+ table => table._id === schema.tableId
196
+ )
197
+ const relatedColumnName = schema.fieldName
198
+ const relationType = schema.relationshipType
199
+ if (relationType === RelationshipTypes.MANY_TO_MANY) {
200
+ const junctionTable = generateManyLinkSchema(
201
+ datasource,
202
+ schema,
203
+ table,
204
+ relatedTable
205
+ )
206
+ if (tables[junctionTable.name]) {
207
+ throw "Junction table already exists, cannot create another relationship."
208
+ }
209
+ tables[junctionTable.name] = junctionTable
210
+ extraTablesToUpdate.push(junctionTable)
211
+ } else {
212
+ const fkTable =
213
+ relationType === RelationshipTypes.ONE_TO_MANY ? table : relatedTable
214
+ const foreignKey = generateLinkSchema(
215
+ schema,
216
+ table,
217
+ relatedTable,
218
+ relationType
219
+ )
220
+ fkTable.schema[foreignKey] = foreignKeyStructure(foreignKey)
221
+ if (fkTable.constrained == null) {
222
+ fkTable.constrained = []
223
+ }
224
+ if (fkTable.constrained.indexOf(foreignKey) === -1) {
225
+ fkTable.constrained.push(foreignKey)
226
+ }
227
+ // foreign key is in other table, need to save it to external
228
+ if (fkTable._id !== table._id) {
229
+ extraTablesToUpdate.push(fkTable)
230
+ }
231
+ }
232
+ generateRelatedSchema(schema, relatedTable, table, relatedColumnName)
233
+ schema.main = true
234
+ }
235
+
236
+ cleanupRelationships(tableToSave, tables, oldTable)
237
+
238
+ const operation = oldTable
239
+ ? DataSourceOperation.UPDATE_TABLE
240
+ : DataSourceOperation.CREATE_TABLE
241
+ await makeTableRequest(datasource, operation, tableToSave, tables, oldTable)
242
+ // update any extra tables (like foreign keys in other tables)
243
+ for (let extraTable of extraTablesToUpdate) {
244
+ const oldExtraTable = oldTables[extraTable.name]
245
+ let op = oldExtraTable
246
+ ? DataSourceOperation.UPDATE_TABLE
247
+ : DataSourceOperation.CREATE_TABLE
248
+ await makeTableRequest(datasource, op, extraTable, tables, oldExtraTable)
249
+ }
250
+
251
+ // make sure the constrained list, all still exist
252
+ if (Array.isArray(tableToSave.constrained)) {
253
+ tableToSave.constrained = tableToSave.constrained.filter(constraint =>
254
+ Object.keys(tableToSave.schema).includes(constraint)
255
+ )
256
+ }
257
+
258
+ // store it into couch now for budibase reference
259
+ datasource.entities[tableToSave.name] = tableToSave
260
+ await db.put(datasource)
261
+
262
+ return tableToSave
263
+ }
264
+
265
+ exports.destroy = async function (ctx) {
266
+ const appId = ctx.appId
267
+ const tableToDelete = await getTable(appId, ctx.params.tableId)
268
+ const datasourceId = getDatasourceId(tableToDelete)
269
+
270
+ const db = new CouchDB(appId)
271
+ const datasource = await db.get(datasourceId)
272
+ const tables = datasource.entities
273
+
274
+ const operation = DataSourceOperation.DELETE_TABLE
275
+ await makeTableRequest(datasource, operation, tableToDelete, tables)
276
+
277
+ cleanupRelationships(tableToDelete, tables)
278
+
279
+ delete datasource.entities[tableToDelete.name]
280
+ await db.put(datasource)
281
+
282
+ return tableToDelete
283
+ }
284
+
285
+ exports.bulkImport = async function (ctx) {
286
+ const appId = ctx.appId
287
+ const table = await getTable(appId, ctx.params.tableId)
288
+ const { dataImport } = ctx.request.body
289
+ if (!dataImport || !dataImport.schema || !dataImport.csvString) {
290
+ ctx.throw(400, "Provided data import information is invalid.")
291
+ }
292
+ const rows = await csvParser.transform({
293
+ ...dataImport,
294
+ existingTable: table,
295
+ })
296
+ await handleRequest(appId, DataSourceOperation.BULK_CREATE, table._id, {
297
+ rows,
298
+ })
299
+ return table
300
+ }