@budibase/server 0.9.186 → 0.9.190-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/Dockerfile +7 -1
  2. package/LICENSE +1 -1
  3. package/__mocks__/mssql.ts +1 -1
  4. package/__mocks__/oracledb.ts +31 -0
  5. package/__mocks__/pg.ts +3 -0
  6. package/builder/assets/bb-space-man.5be51a45.svg +290 -0
  7. package/builder/assets/index.43f4c913.js +1316 -0
  8. package/builder/assets/index.7d405e74.css +1 -0
  9. package/builder/assets/oracle.66592424.png +0 -0
  10. package/builder/assets/vendor.470042a4.js +152 -0
  11. package/builder/assets/vendor.5e1fe644.css +1 -0
  12. package/builder/index.html +4 -4
  13. package/coverage/clover.xml +1520 -1087
  14. package/coverage/coverage-final.json +93 -89
  15. package/coverage/lcov-report/api/controllers/analytics.js.html +2 -2
  16. package/coverage/lcov-report/api/controllers/apikeys.js.html +2 -2
  17. package/coverage/lcov-report/api/controllers/application.js.html +64 -13
  18. package/coverage/lcov-report/api/controllers/auth.js.html +19 -10
  19. package/coverage/lcov-report/api/controllers/automation.js.html +3 -3
  20. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/component.js.html +4 -4
  23. package/coverage/lcov-report/api/controllers/datasource.js.html +232 -28
  24. package/coverage/lcov-report/api/controllers/dev.js.html +16 -7
  25. package/coverage/lcov-report/api/controllers/hosting.js.html +10 -10
  26. package/coverage/lcov-report/api/controllers/index.html +91 -91
  27. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/layout.js.html +3 -3
  29. package/coverage/lcov-report/api/controllers/metadata.js.html +3 -3
  30. package/coverage/lcov-report/api/controllers/permission.js.html +3 -3
  31. package/coverage/lcov-report/api/controllers/query.js.html +53 -209
  32. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  33. package/coverage/lcov-report/api/controllers/routing.js.html +3 -3
  34. package/coverage/lcov-report/api/controllers/row/external.js.html +17 -8
  35. package/coverage/lcov-report/api/controllers/row/index.html +26 -26
  36. package/coverage/lcov-report/api/controllers/row/index.js.html +2 -2
  37. package/coverage/lcov-report/api/controllers/row/internal.js.html +9 -9
  38. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +2 -2
  39. package/coverage/lcov-report/api/controllers/row/utils.js.html +21 -30
  40. package/coverage/lcov-report/api/controllers/screen.js.html +2 -2
  41. package/coverage/lcov-report/api/controllers/script.js.html +3 -3
  42. package/coverage/lcov-report/api/controllers/table/external.js.html +1001 -0
  43. package/coverage/lcov-report/api/controllers/table/index.html +56 -26
  44. package/coverage/lcov-report/api/controllers/table/index.js.html +80 -350
  45. package/coverage/lcov-report/api/controllers/table/internal.js.html +545 -0
  46. package/coverage/lcov-report/api/controllers/table/utils.js.html +175 -52
  47. package/coverage/lcov-report/api/controllers/templates.js.html +4 -4
  48. package/coverage/lcov-report/api/controllers/user.js.html +390 -33
  49. package/coverage/lcov-report/api/controllers/view/exporters.js.html +19 -4
  50. package/coverage/lcov-report/api/controllers/view/index.html +22 -22
  51. package/coverage/lcov-report/api/controllers/view/index.js.html +52 -13
  52. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  53. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +3 -3
  54. package/coverage/lcov-report/api/controllers/webhook.js.html +4 -4
  55. package/coverage/lcov-report/api/index.html +1 -1
  56. package/coverage/lcov-report/api/index.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/dev.js.html +2 -2
  68. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/index.html +4 -4
  70. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/query.js.html +5 -2
  76. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  81. package/coverage/lcov-report/api/routes/static.js.html +2 -2
  82. package/coverage/lcov-report/api/routes/table.js.html +35 -2
  83. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  84. package/coverage/lcov-report/api/routes/user.js.html +17 -2
  85. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  86. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  87. package/coverage/lcov-report/automations/actions.js.html +2 -2
  88. package/coverage/lcov-report/automations/automationUtils.js.html +4 -4
  89. package/coverage/lcov-report/automations/bullboard.js.html +71 -20
  90. package/coverage/lcov-report/automations/index.html +35 -50
  91. package/coverage/lcov-report/automations/index.js.html +2 -2
  92. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  96. package/coverage/lcov-report/automations/steps/discord.js.html +3 -3
  97. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  98. package/coverage/lcov-report/automations/steps/executeScript.js.html +3 -3
  99. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  100. package/coverage/lcov-report/automations/steps/index.html +14 -14
  101. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  102. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +3 -3
  103. package/coverage/lcov-report/automations/steps/queryRows.js.html +3 -3
  104. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  105. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  106. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  107. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  108. package/coverage/lcov-report/automations/steps/utils.js.html +7 -7
  109. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  110. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  111. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  112. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  113. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  114. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  115. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  116. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  117. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  118. package/coverage/lcov-report/automations/triggers.js.html +22 -10
  119. package/coverage/lcov-report/automations/utils.js.html +42 -12
  120. package/coverage/lcov-report/constants/index.html +5 -5
  121. package/coverage/lcov-report/constants/index.js.html +87 -21
  122. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  123. package/coverage/lcov-report/constants/screens.js.html +1 -1
  124. package/coverage/lcov-report/db/client.js.html +2 -2
  125. package/coverage/lcov-report/db/inMemoryView.js.html +2 -2
  126. package/coverage/lcov-report/db/index.html +2 -2
  127. package/coverage/lcov-report/db/index.js.html +1 -1
  128. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +6 -6
  129. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  130. package/coverage/lcov-report/db/linkedRows/index.js.html +5 -5
  131. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +6 -6
  132. package/coverage/lcov-report/db/newid.js.html +3 -3
  133. package/coverage/lcov-report/db/utils.js.html +57 -57
  134. package/coverage/lcov-report/events/AutomationEmitter.js.html +6 -6
  135. package/coverage/lcov-report/events/index.html +6 -6
  136. package/coverage/lcov-report/events/index.js.html +1 -1
  137. package/coverage/lcov-report/events/utils.js.html +5 -5
  138. package/coverage/lcov-report/index.html +108 -93
  139. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  140. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  141. package/coverage/lcov-report/middleware/appInfo.js.html +2 -2
  142. package/coverage/lcov-report/middleware/authorized.js.html +58 -34
  143. package/coverage/lcov-report/middleware/builder.js.html +16 -7
  144. package/coverage/lcov-report/middleware/currentapp.js.html +6 -6
  145. package/coverage/lcov-report/middleware/index.html +27 -27
  146. package/coverage/lcov-report/middleware/joi-validator.js.html +2 -2
  147. package/coverage/lcov-report/middleware/resourceId.js.html +18 -18
  148. package/coverage/lcov-report/middleware/selfhost.js.html +2 -2
  149. package/coverage/lcov-report/middleware/usageQuota.js.html +4 -4
  150. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  151. package/coverage/lcov-report/{automations/thread.js.html → threads/automation.js.html} +90 -33
  152. package/coverage/lcov-report/threads/index.html +141 -0
  153. package/coverage/lcov-report/threads/index.js.html +278 -0
  154. package/coverage/lcov-report/threads/query.js.html +278 -0
  155. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  156. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  157. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  158. package/coverage/lcov-report/utilities/csvParser.js.html +144 -15
  159. package/coverage/lcov-report/utilities/global.js.html +26 -11
  160. package/coverage/lcov-report/utilities/index.html +48 -48
  161. package/coverage/lcov-report/utilities/index.js.html +47 -14
  162. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +30 -9
  163. package/coverage/lcov-report/utilities/queue/index.html +14 -14
  164. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  165. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  166. package/coverage/lcov-report/utilities/rowProcessor/index.html +4 -4
  167. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +2 -2
  168. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +3 -3
  169. package/coverage/lcov-report/utilities/scriptRunner.js.html +16 -7
  170. package/coverage/lcov-report/utilities/security.js.html +3 -3
  171. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  172. package/coverage/lcov-report/utilities/users.js.html +1 -1
  173. package/coverage/lcov-report/utilities/workerRequests.js.html +5 -5
  174. package/coverage/lcov.info +2558 -1787
  175. package/dist/api/controllers/application.js +15 -1
  176. package/dist/api/controllers/auth.js +3 -3
  177. package/dist/api/controllers/datasource.js +67 -16
  178. package/dist/api/controllers/deploy/index.js +2 -0
  179. package/dist/api/controllers/dev.js +4 -1
  180. package/dist/api/controllers/query.js +30 -58
  181. package/dist/api/controllers/row/ExternalRequest.js +37 -14
  182. package/dist/api/controllers/row/external.js +3 -1
  183. package/dist/api/controllers/row/utils.js +6 -12
  184. package/dist/api/controllers/table/external.js +249 -0
  185. package/dist/api/controllers/table/index.js +49 -123
  186. package/dist/api/controllers/table/internal.js +150 -0
  187. package/dist/api/controllers/table/utils.js +62 -28
  188. package/dist/api/controllers/user.js +123 -11
  189. package/dist/api/controllers/view/exporters.js +4 -0
  190. package/dist/api/controllers/view/index.js +14 -2
  191. package/dist/api/routes/query.js +1 -1
  192. package/dist/api/routes/table.js +7 -1
  193. package/dist/api/routes/user.js +2 -1
  194. package/dist/app.js +2 -11
  195. package/dist/automations/bullboard.js +37 -10
  196. package/dist/automations/triggers.js +7 -3
  197. package/dist/automations/utils.js +11 -2
  198. package/dist/constants/index.js +19 -0
  199. package/dist/definitions/datasource.js +5 -0
  200. package/dist/environment.js +13 -0
  201. package/dist/integrations/base/IntegrationBase.js +2 -0
  202. package/dist/integrations/base/datasourcePlus.js +2 -0
  203. package/dist/integrations/base/sql.js +338 -180
  204. package/dist/integrations/base/sqlTable.js +143 -0
  205. package/dist/integrations/base/utils.js +30 -0
  206. package/dist/integrations/index.js +6 -0
  207. package/dist/integrations/microsoftSqlServer.js +117 -9
  208. package/dist/integrations/mysql.js +14 -84
  209. package/dist/integrations/oracle.js +345 -0
  210. package/dist/integrations/postgres.js +35 -28
  211. package/dist/integrations/utils.js +71 -9
  212. package/dist/middleware/authorized.js +22 -11
  213. package/dist/middleware/builder.js +4 -1
  214. package/dist/package.json +13 -8
  215. package/dist/tests/utilities/TestConfiguration.js +2 -1
  216. package/dist/{automations/thread.js → threads/automation.js} +26 -8
  217. package/dist/threads/index.js +64 -0
  218. package/dist/threads/query.js +64 -0
  219. package/dist/tsconfig.tsbuildinfo +1 -1
  220. package/dist/utilities/csvParser.js +46 -5
  221. package/dist/utilities/global.js +6 -2
  222. package/dist/utilities/index.js +11 -0
  223. package/dist/utilities/queue/inMemoryQueue.js +8 -0
  224. package/dist/utilities/scriptRunner.js +4 -1
  225. package/package.json +14 -9
  226. package/scripts/dev/manage.js +1 -0
  227. package/scripts/integrations/mssql/data/Dockerfile +9 -0
  228. package/scripts/integrations/mssql/data/entrypoint.sh +24 -0
  229. package/scripts/integrations/mssql/data/setup.sql +54 -0
  230. package/scripts/integrations/mssql/docker-compose.yaml +12 -0
  231. package/scripts/integrations/mssql/reset.sh +3 -0
  232. package/scripts/integrations/oracle/docker-compose.yml +19 -0
  233. package/scripts/integrations/oracle/instantclient/linux/x86-64/basiclite-21.4.zip +0 -0
  234. package/scripts/integrations/oracle/instantclient/linux/x86-64/install.sh +23 -0
  235. package/scripts/integrations/oracle/oracle.md +90 -0
  236. package/scripts/integrations/postgres/docker-compose.yml +3 -3
  237. package/scripts/likeCypress.ts +33 -0
  238. package/src/api/controllers/application.js +18 -1
  239. package/src/api/controllers/auth.js +3 -0
  240. package/src/api/controllers/datasource.js +84 -16
  241. package/src/api/controllers/deploy/index.js +2 -0
  242. package/src/api/controllers/dev.js +4 -1
  243. package/src/api/controllers/query.js +29 -81
  244. package/src/api/controllers/row/ExternalRequest.ts +47 -16
  245. package/src/api/controllers/row/external.js +4 -1
  246. package/src/api/controllers/row/utils.js +8 -11
  247. package/src/api/controllers/table/external.js +307 -0
  248. package/src/api/controllers/table/index.js +47 -137
  249. package/src/api/controllers/table/internal.js +155 -0
  250. package/src/api/controllers/table/utils.js +74 -33
  251. package/src/api/controllers/user.js +132 -13
  252. package/src/api/controllers/view/exporters.js +5 -0
  253. package/src/api/controllers/view/index.js +15 -2
  254. package/src/api/routes/query.js +1 -0
  255. package/src/api/routes/table.js +11 -0
  256. package/src/api/routes/tests/datasource.spec.js +2 -2
  257. package/src/api/routes/tests/misc.spec.js +5 -1
  258. package/src/api/routes/tests/query.spec.js +0 -1
  259. package/src/api/routes/user.js +5 -0
  260. package/src/app.ts +2 -10
  261. package/src/automations/bullboard.js +28 -11
  262. package/src/automations/tests/automation.spec.js +3 -4
  263. package/src/automations/triggers.js +7 -3
  264. package/src/automations/utils.js +12 -2
  265. package/src/constants/index.js +22 -0
  266. package/src/definitions/common.ts +7 -1
  267. package/src/definitions/datasource.ts +10 -3
  268. package/src/environment.js +15 -0
  269. package/src/integrations/airtable.ts +2 -1
  270. package/src/integrations/arangodb.ts +2 -1
  271. package/src/integrations/base/IntegrationBase.ts +6 -0
  272. package/src/integrations/base/datasourcePlus.ts +9 -0
  273. package/src/integrations/base/sql.ts +362 -222
  274. package/src/integrations/base/sqlTable.ts +180 -0
  275. package/src/integrations/base/utils.ts +22 -0
  276. package/src/integrations/couchdb.ts +2 -1
  277. package/src/integrations/dynamodb.ts +2 -1
  278. package/src/integrations/elasticsearch.ts +2 -1
  279. package/src/integrations/index.ts +7 -0
  280. package/src/integrations/microsoftSqlServer.ts +145 -13
  281. package/src/integrations/mongodb.ts +2 -1
  282. package/src/integrations/mysql.ts +20 -94
  283. package/src/integrations/oracle.ts +445 -0
  284. package/src/integrations/postgres.ts +45 -35
  285. package/src/integrations/rest.ts +2 -1
  286. package/src/integrations/s3.ts +2 -1
  287. package/src/integrations/tests/microsoftSqlServer.spec.js +22 -15
  288. package/src/integrations/tests/oracle.spec.js +94 -0
  289. package/src/integrations/tests/postgres.spec.js +4 -0
  290. package/src/integrations/utils.ts +74 -8
  291. package/src/middleware/authorized.js +26 -18
  292. package/src/middleware/builder.js +4 -1
  293. package/src/middleware/tests/authorized.spec.js +2 -0
  294. package/src/tests/utilities/TestConfiguration.js +2 -1
  295. package/src/{automations/thread.js → threads/automation.js} +27 -8
  296. package/src/threads/index.js +66 -0
  297. package/src/threads/query.js +66 -0
  298. package/src/utilities/csvParser.js +48 -5
  299. package/src/utilities/global.js +7 -2
  300. package/src/utilities/index.js +11 -0
  301. package/src/utilities/queue/inMemoryQueue.js +7 -0
  302. package/src/utilities/scriptRunner.js +4 -1
  303. package/src/utilities/tests/__snapshots__/csvParser.spec.js.snap +0 -6
  304. package/src/utilities/tests/csvParser.spec.js +3 -0
  305. package/tsconfig.json +1 -1
  306. package/builder/assets/index.10556cc1.js +0 -201
  307. package/builder/assets/index.7469e1e4.css +0 -29
  308. package/builder/assets/vendor.278a305c.js +0 -9
  309. package/builder/assets/vendor.d75d5eb7.css +0 -1
@@ -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,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
@@ -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
 
@@ -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"