@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
@@ -7,8 +7,9 @@ const {
7
7
  BudibaseInternalDB,
8
8
  getTableParams,
9
9
  } = require("../../db/utils")
10
+ const { BuildSchemaErrors, InvalidColumns } = require("../../constants")
10
11
  const { integrations } = require("../../integrations")
11
- const { makeExternalQuery } = require("./row/utils")
12
+ const { getDatasourceAndQuery } = require("./row/utils")
12
13
 
13
14
  exports.fetch = async function (ctx) {
14
15
  const database = new CouchDB(ctx.appId)
@@ -43,13 +44,17 @@ exports.buildSchemaFromDb = async function (ctx) {
43
44
  const db = new CouchDB(ctx.appId)
44
45
  const datasource = await db.get(ctx.params.datasourceId)
45
46
 
46
- const tables = await buildSchemaHelper(datasource)
47
+ const { tables, error } = await buildSchemaHelper(datasource)
47
48
  datasource.entities = tables
48
49
 
49
- const response = await db.put(datasource)
50
- datasource._rev = response.rev
50
+ const dbResp = await db.put(datasource)
51
+ datasource._rev = dbResp.rev
51
52
 
52
- ctx.body = datasource
53
+ const response = { datasource }
54
+ if (error) {
55
+ response.error = error
56
+ }
57
+ ctx.body = response
53
58
  }
54
59
 
55
60
  exports.update = async function (ctx) {
@@ -71,7 +76,7 @@ exports.update = async function (ctx) {
71
76
 
72
77
  ctx.status = 200
73
78
  ctx.message = "Datasource saved successfully."
74
- ctx.body = datasource
79
+ ctx.body = { datasource }
75
80
  }
76
81
 
77
82
  exports.save = async function (ctx) {
@@ -85,13 +90,15 @@ exports.save = async function (ctx) {
85
90
  ...ctx.request.body.datasource,
86
91
  }
87
92
 
93
+ let schemaError = null
88
94
  if (fetchSchema) {
89
- let tables = await buildSchemaHelper(datasource)
95
+ const { tables, error } = await buildSchemaHelper(datasource)
96
+ schemaError = error
90
97
  datasource.entities = tables
91
98
  }
92
99
 
93
- const response = await db.put(datasource)
94
- datasource._rev = response.rev
100
+ const dbResp = await db.put(datasource)
101
+ datasource._rev = dbResp.rev
95
102
 
96
103
  // Drain connection pools when configuration is changed
97
104
  if (datasource.source) {
@@ -101,17 +108,27 @@ exports.save = async function (ctx) {
101
108
  }
102
109
  }
103
110
 
104
- ctx.status = 200
105
- ctx.message = "Datasource saved successfully."
106
- ctx.body = datasource
111
+ const response = { datasource }
112
+ if (schemaError) {
113
+ response.error = schemaError
114
+ }
115
+ ctx.body = response
107
116
  }
108
117
 
109
118
  exports.destroy = async function (ctx) {
110
119
  const db = new CouchDB(ctx.appId)
111
120
 
112
121
  // Delete all queries for the datasource
113
- const rows = await db.allDocs(getQueryParams(ctx.params.datasourceId, null))
114
- await db.bulkDocs(rows.rows.map(row => ({ ...row.doc, _deleted: true })))
122
+ const queries = await db.allDocs(
123
+ getQueryParams(ctx.params.datasourceId, null)
124
+ )
125
+ await db.bulkDocs(
126
+ queries.rows.map(row => ({
127
+ _id: row.id,
128
+ _rev: row.value.rev,
129
+ _deleted: true,
130
+ }))
131
+ )
115
132
 
116
133
  // delete the datasource
117
134
  await db.remove(ctx.params.datasourceId, ctx.params.revId)
@@ -129,12 +146,29 @@ exports.find = async function (ctx) {
129
146
  exports.query = async function (ctx) {
130
147
  const queryJson = ctx.request.body
131
148
  try {
132
- ctx.body = await makeExternalQuery(ctx.appId, queryJson)
149
+ ctx.body = await getDatasourceAndQuery(ctx.appId, queryJson)
133
150
  } catch (err) {
134
151
  ctx.throw(400, err)
135
152
  }
136
153
  }
137
154
 
155
+ function getErrorTables(errors, errorType) {
156
+ return Object.entries(errors)
157
+ .filter(entry => entry[1] === errorType)
158
+ .map(([name]) => name)
159
+ }
160
+
161
+ function updateError(error, newError, tables) {
162
+ if (!error) {
163
+ error = ""
164
+ }
165
+ if (error.length > 0) {
166
+ error += "\n"
167
+ }
168
+ error += `${newError} ${tables.join(", ")}`
169
+ return error
170
+ }
171
+
138
172
  const buildSchemaHelper = async datasource => {
139
173
  const Connector = integrations[datasource.source]
140
174
 
@@ -143,5 +177,39 @@ const buildSchemaHelper = async datasource => {
143
177
  await connector.buildSchema(datasource._id, datasource.entities)
144
178
  datasource.entities = connector.tables
145
179
 
146
- return connector.tables
180
+ // make sure they all have a display name selected
181
+ for (let entity of Object.values(datasource.entities)) {
182
+ if (entity.primaryDisplay) {
183
+ continue
184
+ }
185
+ const notAutoColumn = Object.values(entity.schema).find(
186
+ schema => !schema.autocolumn
187
+ )
188
+ if (notAutoColumn) {
189
+ entity.primaryDisplay = notAutoColumn.name
190
+ }
191
+ }
192
+
193
+ const errors = connector.schemaErrors
194
+ let error = null
195
+ if (errors && Object.keys(errors).length > 0) {
196
+ const noKey = getErrorTables(errors, BuildSchemaErrors.NO_KEY)
197
+ const invalidCol = getErrorTables(errors, BuildSchemaErrors.INVALID_COLUMN)
198
+ if (noKey.length) {
199
+ error = updateError(
200
+ error,
201
+ "No primary key constraint found for the following:",
202
+ noKey
203
+ )
204
+ }
205
+ if (invalidCol.length) {
206
+ const invalidCols = Object.values(InvalidColumns).join(", ")
207
+ error = updateError(
208
+ error,
209
+ `Cannot use columns ${invalidCols} found in following:`,
210
+ invalidCol
211
+ )
212
+ }
213
+ }
214
+ return { tables: connector.tables, error }
147
215
  }
@@ -6,6 +6,7 @@ const {
6
6
  disableAllCrons,
7
7
  enableCronTrigger,
8
8
  } = require("../../../automations/utils")
9
+ const { app: appCache } = require("@budibase/auth/cache")
9
10
 
10
11
  // the max time we can wait for an invalidation to complete before considering it failed
11
12
  const MAX_PENDING_TIME_MS = 30 * 60000
@@ -103,6 +104,7 @@ async function deployApp(deployment) {
103
104
  appDoc.appId = productionAppId
104
105
  appDoc.instance._id = productionAppId
105
106
  await db.put(appDoc)
107
+ await appCache.invalidateAppMetadata(productionAppId)
106
108
  console.log("New app doc written successfully.")
107
109
  await initDeployedApp(productionAppId)
108
110
  console.log("Deployed app initialised, setting deployment to successful")
@@ -6,6 +6,7 @@ const { request } = require("../../utilities/workerRequests")
6
6
  const { clearLock } = require("../../utilities/redis")
7
7
  const { Replication } = require("@budibase/auth").db
8
8
  const { DocumentTypes } = require("../../db/utils")
9
+ const { app: appCache } = require("@budibase/auth/cache")
9
10
 
10
11
  async function redirect(ctx, method, path = "global") {
11
12
  const { devPath } = ctx.params
@@ -24,7 +25,8 @@ async function redirect(ctx, method, path = "global") {
24
25
  )
25
26
  )
26
27
  if (response.status !== 200) {
27
- ctx.throw(response.status, response.statusText)
28
+ const err = await response.text()
29
+ ctx.throw(400, err)
28
30
  }
29
31
  const cookie = response.headers.get("set-cookie")
30
32
  if (cookie) {
@@ -106,6 +108,7 @@ exports.revert = async ctx => {
106
108
  appDoc.appId = appId
107
109
  appDoc.instance._id = appId
108
110
  await db.put(appDoc)
111
+ await appCache.invalidateAppMetadata(appId)
109
112
  ctx.body = {
110
113
  message: "Reverted changes successfully.",
111
114
  }
@@ -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,9 +180,26 @@ module External {
178
180
  return thisRow
179
181
  }
180
182
 
181
- function isMany(field: FieldSchema) {
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
+
200
+ function isOneSide(field: FieldSchema) {
182
201
  return (
183
- field.relationshipType && field.relationshipType.split("-")[0] === "many"
202
+ field.relationshipType && field.relationshipType.split("-")[0] === "one"
184
203
  )
185
204
  }
186
205
 
@@ -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)
@@ -250,25 +274,37 @@ module External {
250
274
  const linkTable = this.tables[linkTableName]
251
275
  // @ts-ignore
252
276
  const linkTablePrimary = linkTable.primary[0]
253
- if (!isMany(field)) {
277
+ // one to many
278
+ if (isOneSide(field)) {
254
279
  newRow[field.foreignKey || linkTablePrimary] = breakRowIdField(
255
280
  row[key][0]
256
281
  )[0]
257
- } else {
282
+ }
283
+ // many to many
284
+ else if (field.through) {
258
285
  // we're not inserting a doc, will be a bunch of update calls
259
- const isUpdate = !field.through
260
- const thisKey: string = isUpdate
261
- ? "id"
262
- : field.throughTo || linkTablePrimary
263
- // @ts-ignore
264
- const otherKey: string = isUpdate
265
- ? field.fieldName
266
- : field.throughFrom || tablePrimary
286
+ const otherKey: string = field.throughFrom || linkTablePrimary
287
+ const thisKey: string = field.throughTo || tablePrimary
267
288
  row[key].map((relationship: any) => {
268
- // we don't really support composite keys for relationships, this is why [0] is used
269
289
  manyRelationships.push({
270
290
  tableId: field.through || field.tableId,
271
- isUpdate,
291
+ isUpdate: false,
292
+ key: otherKey,
293
+ [otherKey]: breakRowIdField(relationship)[0],
294
+ // leave the ID for enrichment later
295
+ [thisKey]: `{{ literal ${tablePrimary} }}`,
296
+ })
297
+ })
298
+ }
299
+ // many to one
300
+ else {
301
+ const thisKey: string = "id"
302
+ // @ts-ignore
303
+ const otherKey: string = field.fieldName
304
+ row[key].map((relationship: any) => {
305
+ manyRelationships.push({
306
+ tableId: field.tableId,
307
+ isUpdate: true,
272
308
  key: otherKey,
273
309
  [thisKey]: breakRowIdField(relationship)[0],
274
310
  // leave the ID for enrichment later
@@ -336,7 +372,7 @@ module External {
336
372
  table: Table,
337
373
  relationships: RelationshipsJson[]
338
374
  ) {
339
- if (rows[0].read === true) {
375
+ if (!rows || rows.length === 0 || rows[0].read === true) {
340
376
  return []
341
377
  }
342
378
  let finalRows: { [key: string]: Row } = {}
@@ -352,7 +388,10 @@ module External {
352
388
  )
353
389
  continue
354
390
  }
355
- const thisRow = basicProcessing(row, table)
391
+ const thisRow = fixArrayTypes(basicProcessing(row, table), table)
392
+ if (thisRow._id == null) {
393
+ throw "Unable to generate row ID for SQL rows"
394
+ }
356
395
  finalRows[thisRow._id] = thisRow
357
396
  // do this at end once its been added to the final rows
358
397
  finalRows = this.updateRelationshipColumns(
@@ -361,7 +400,7 @@ module External {
361
400
  relationships
362
401
  )
363
402
  }
364
- return Object.values(finalRows)
403
+ return processFormulas(table, Object.values(finalRows))
365
404
  }
366
405
 
367
406
  /**
@@ -398,8 +437,8 @@ module External {
398
437
  )
399
438
  definition.through = throughTableName
400
439
  // don't support composite keys for relationships
401
- definition.from = field.throughFrom || table.primary[0]
402
- definition.to = field.throughTo || linkTable.primary[0]
440
+ definition.from = field.throughTo || table.primary[0]
441
+ definition.to = field.throughFrom || linkTable.primary[0]
403
442
  definition.fromPrimary = table.primary[0]
404
443
  definition.toPrimary = linkTable.primary[0]
405
444
  }
@@ -421,24 +460,36 @@ module External {
421
460
  // make a new request to get the row with all its relationships
422
461
  // we need this to work out if any relationships need removed
423
462
  for (let field of Object.values(table.schema)) {
424
- if (field.type !== FieldTypes.LINK || !field.fieldName) {
463
+ if (
464
+ field.type !== FieldTypes.LINK ||
465
+ !field.fieldName ||
466
+ isOneSide(field)
467
+ ) {
425
468
  continue
426
469
  }
427
470
  const isMany = field.relationshipType === RelationshipTypes.MANY_TO_MANY
428
471
  const tableId = isMany ? field.through : field.tableId
429
- const manyKey = field.throughFrom || primaryKey
472
+ const { tableName: relatedTableName } = breakExternalTableId(tableId)
473
+ // @ts-ignore
474
+ const linkPrimaryKey = this.tables[relatedTableName].primary[0]
475
+ const manyKey = field.throughTo || primaryKey
476
+ const lookupField = isMany ? primaryKey : field.foreignKey
430
477
  const fieldName = isMany ? manyKey : field.fieldName
431
- const response = await makeExternalQuery(this.appId, {
478
+ if (!lookupField || !row[lookupField]) {
479
+ continue
480
+ }
481
+ const response = await getDatasourceAndQuery(this.appId, {
432
482
  endpoint: getEndpoint(tableId, DataSourceOperation.READ),
433
483
  filters: {
434
484
  equal: {
435
- [fieldName]: row[primaryKey],
485
+ [fieldName]: row[lookupField],
436
486
  },
437
487
  },
438
488
  })
439
489
  // this is the response from knex if no rows found
440
490
  const rows = !response[0].read ? response : []
441
- related[fieldName] = { rows, isMany, tableId }
491
+ const storeTo = isMany ? field.throughFrom || linkPrimaryKey : manyKey
492
+ related[storeTo] = { rows, isMany, tableId }
442
493
  }
443
494
  return related
444
495
  }
@@ -479,7 +530,7 @@ module External {
479
530
  : DataSourceOperation.CREATE
480
531
  if (!found) {
481
532
  promises.push(
482
- makeExternalQuery(appId, {
533
+ getDatasourceAndQuery(appId, {
483
534
  endpoint: getEndpoint(tableId, operation),
484
535
  // if we're doing many relationships then we're writing, only one response
485
536
  body,
@@ -509,7 +560,7 @@ module External {
509
560
  : DataSourceOperation.UPDATE
510
561
  const body = isMany ? null : { [colName]: null }
511
562
  promises.push(
512
- makeExternalQuery(this.appId, {
563
+ getDatasourceAndQuery(this.appId, {
513
564
  endpoint: getEndpoint(tableId, op),
514
565
  body,
515
566
  filters,
@@ -532,16 +583,17 @@ module External {
532
583
  table: Table,
533
584
  includeRelations: IncludeRelationships = IncludeRelationships.INCLUDE
534
585
  ) {
535
- function extractNonLinkFieldNames(table: Table, existing: string[] = []) {
586
+ function extractRealFields(table: Table, existing: string[] = []) {
536
587
  return Object.entries(table.schema)
537
588
  .filter(
538
589
  column =>
539
590
  column[1].type !== FieldTypes.LINK &&
591
+ column[1].type !== FieldTypes.FORMULA &&
540
592
  !existing.find((field: string) => field === column[0])
541
593
  )
542
594
  .map(column => `${table.name}.${column[0]}`)
543
595
  }
544
- let fields = extractNonLinkFieldNames(table)
596
+ let fields = extractRealFields(table)
545
597
  for (let field of Object.values(table.schema)) {
546
598
  if (field.type !== FieldTypes.LINK || !includeRelations) {
547
599
  continue
@@ -549,7 +601,7 @@ module External {
549
601
  const { tableName: linkTableName } = breakExternalTableId(field.tableId)
550
602
  const linkTable = this.tables[linkTableName]
551
603
  if (linkTable) {
552
- const linkedFields = extractNonLinkFieldNames(linkTable, fields)
604
+ const linkedFields = extractRealFields(linkTable, fields)
553
605
  fields = fields.concat(linkedFields)
554
606
  }
555
607
  }
@@ -573,7 +625,10 @@ module External {
573
625
  throw `Unable to process query, table "${tableName}" not defined.`
574
626
  }
575
627
  // look for specific components of config which may not be considered acceptable
576
- let { id, row, filters, sort, paginate } = cleanupConfig(config, table)
628
+ let { id, row, filters, sort, paginate, rows } = cleanupConfig(
629
+ config,
630
+ table
631
+ )
577
632
  filters = buildFilters(id, filters || {}, table)
578
633
  const relationships = this.buildRelationships(table)
579
634
  // clean up row on ingress using schema
@@ -599,7 +654,7 @@ module External {
599
654
  sort,
600
655
  paginate,
601
656
  relationships,
602
- body: row,
657
+ body: row || rows,
603
658
  // pass an id filter into extra, purely for mysql/returning
604
659
  extra: {
605
660
  idFilter: buildFilters(id || generateIdForRow(row, table), {}, table),
@@ -609,7 +664,7 @@ module External {
609
664
  },
610
665
  }
611
666
  // can't really use response right now
612
- const response = await makeExternalQuery(appId, json)
667
+ const response = await getDatasourceAndQuery(appId, json)
613
668
  // handle many to many relationships now if we know the ID (could be auto increment)
614
669
  if (
615
670
  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
@@ -55,7 +57,8 @@ exports.save = async ctx => {
55
57
  exports.fetchView = async ctx => {
56
58
  // there are no views in external data sources, shouldn't ever be called
57
59
  // for now just fetch
58
- ctx.params.tableId = ctx.params.viewName.split("all_")[1]
60
+ const split = ctx.params.viewName.split("all_")
61
+ ctx.params.tableId = split[1] ? split[1] : split[0]
59
62
  return exports.fetch(ctx)
60
63
  }
61
64