@budibase/server 0.9.186 → 0.9.190-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/Dockerfile +7 -1
  2. package/LICENSE +1 -1
  3. package/__mocks__/mssql.ts +1 -1
  4. package/__mocks__/oracledb.ts +31 -0
  5. package/__mocks__/pg.ts +3 -0
  6. package/builder/assets/bb-space-man.5be51a45.svg +290 -0
  7. package/builder/assets/index.43f4c913.js +1316 -0
  8. package/builder/assets/index.7d405e74.css +1 -0
  9. package/builder/assets/oracle.66592424.png +0 -0
  10. package/builder/assets/vendor.470042a4.js +152 -0
  11. package/builder/assets/vendor.5e1fe644.css +1 -0
  12. package/builder/index.html +4 -4
  13. package/coverage/clover.xml +1520 -1087
  14. package/coverage/coverage-final.json +93 -89
  15. package/coverage/lcov-report/api/controllers/analytics.js.html +2 -2
  16. package/coverage/lcov-report/api/controllers/apikeys.js.html +2 -2
  17. package/coverage/lcov-report/api/controllers/application.js.html +64 -13
  18. package/coverage/lcov-report/api/controllers/auth.js.html +19 -10
  19. package/coverage/lcov-report/api/controllers/automation.js.html +3 -3
  20. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/component.js.html +4 -4
  23. package/coverage/lcov-report/api/controllers/datasource.js.html +232 -28
  24. package/coverage/lcov-report/api/controllers/dev.js.html +16 -7
  25. package/coverage/lcov-report/api/controllers/hosting.js.html +10 -10
  26. package/coverage/lcov-report/api/controllers/index.html +91 -91
  27. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/layout.js.html +3 -3
  29. package/coverage/lcov-report/api/controllers/metadata.js.html +3 -3
  30. package/coverage/lcov-report/api/controllers/permission.js.html +3 -3
  31. package/coverage/lcov-report/api/controllers/query.js.html +53 -209
  32. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  33. package/coverage/lcov-report/api/controllers/routing.js.html +3 -3
  34. package/coverage/lcov-report/api/controllers/row/external.js.html +17 -8
  35. package/coverage/lcov-report/api/controllers/row/index.html +26 -26
  36. package/coverage/lcov-report/api/controllers/row/index.js.html +2 -2
  37. package/coverage/lcov-report/api/controllers/row/internal.js.html +9 -9
  38. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +2 -2
  39. package/coverage/lcov-report/api/controllers/row/utils.js.html +21 -30
  40. package/coverage/lcov-report/api/controllers/screen.js.html +2 -2
  41. package/coverage/lcov-report/api/controllers/script.js.html +3 -3
  42. package/coverage/lcov-report/api/controllers/table/external.js.html +1001 -0
  43. package/coverage/lcov-report/api/controllers/table/index.html +56 -26
  44. package/coverage/lcov-report/api/controllers/table/index.js.html +80 -350
  45. package/coverage/lcov-report/api/controllers/table/internal.js.html +545 -0
  46. package/coverage/lcov-report/api/controllers/table/utils.js.html +175 -52
  47. package/coverage/lcov-report/api/controllers/templates.js.html +4 -4
  48. package/coverage/lcov-report/api/controllers/user.js.html +390 -33
  49. package/coverage/lcov-report/api/controllers/view/exporters.js.html +19 -4
  50. package/coverage/lcov-report/api/controllers/view/index.html +22 -22
  51. package/coverage/lcov-report/api/controllers/view/index.js.html +52 -13
  52. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  53. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +3 -3
  54. package/coverage/lcov-report/api/controllers/webhook.js.html +4 -4
  55. package/coverage/lcov-report/api/index.html +1 -1
  56. package/coverage/lcov-report/api/index.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/dev.js.html +2 -2
  68. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/index.html +4 -4
  70. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/query.js.html +5 -2
  76. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  81. package/coverage/lcov-report/api/routes/static.js.html +2 -2
  82. package/coverage/lcov-report/api/routes/table.js.html +35 -2
  83. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  84. package/coverage/lcov-report/api/routes/user.js.html +17 -2
  85. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  86. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  87. package/coverage/lcov-report/automations/actions.js.html +2 -2
  88. package/coverage/lcov-report/automations/automationUtils.js.html +4 -4
  89. package/coverage/lcov-report/automations/bullboard.js.html +71 -20
  90. package/coverage/lcov-report/automations/index.html +35 -50
  91. package/coverage/lcov-report/automations/index.js.html +2 -2
  92. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  96. package/coverage/lcov-report/automations/steps/discord.js.html +3 -3
  97. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  98. package/coverage/lcov-report/automations/steps/executeScript.js.html +3 -3
  99. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  100. package/coverage/lcov-report/automations/steps/index.html +14 -14
  101. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  102. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +3 -3
  103. package/coverage/lcov-report/automations/steps/queryRows.js.html +3 -3
  104. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  105. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  106. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  107. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  108. package/coverage/lcov-report/automations/steps/utils.js.html +7 -7
  109. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  110. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  111. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  112. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  113. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  114. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  115. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  116. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  117. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  118. package/coverage/lcov-report/automations/triggers.js.html +22 -10
  119. package/coverage/lcov-report/automations/utils.js.html +42 -12
  120. package/coverage/lcov-report/constants/index.html +5 -5
  121. package/coverage/lcov-report/constants/index.js.html +87 -21
  122. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  123. package/coverage/lcov-report/constants/screens.js.html +1 -1
  124. package/coverage/lcov-report/db/client.js.html +2 -2
  125. package/coverage/lcov-report/db/inMemoryView.js.html +2 -2
  126. package/coverage/lcov-report/db/index.html +2 -2
  127. package/coverage/lcov-report/db/index.js.html +1 -1
  128. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +6 -6
  129. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  130. package/coverage/lcov-report/db/linkedRows/index.js.html +5 -5
  131. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +6 -6
  132. package/coverage/lcov-report/db/newid.js.html +3 -3
  133. package/coverage/lcov-report/db/utils.js.html +57 -57
  134. package/coverage/lcov-report/events/AutomationEmitter.js.html +6 -6
  135. package/coverage/lcov-report/events/index.html +6 -6
  136. package/coverage/lcov-report/events/index.js.html +1 -1
  137. package/coverage/lcov-report/events/utils.js.html +5 -5
  138. package/coverage/lcov-report/index.html +108 -93
  139. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  140. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  141. package/coverage/lcov-report/middleware/appInfo.js.html +2 -2
  142. package/coverage/lcov-report/middleware/authorized.js.html +58 -34
  143. package/coverage/lcov-report/middleware/builder.js.html +16 -7
  144. package/coverage/lcov-report/middleware/currentapp.js.html +6 -6
  145. package/coverage/lcov-report/middleware/index.html +27 -27
  146. package/coverage/lcov-report/middleware/joi-validator.js.html +2 -2
  147. package/coverage/lcov-report/middleware/resourceId.js.html +18 -18
  148. package/coverage/lcov-report/middleware/selfhost.js.html +2 -2
  149. package/coverage/lcov-report/middleware/usageQuota.js.html +4 -4
  150. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  151. package/coverage/lcov-report/{automations/thread.js.html → threads/automation.js.html} +90 -33
  152. package/coverage/lcov-report/threads/index.html +141 -0
  153. package/coverage/lcov-report/threads/index.js.html +278 -0
  154. package/coverage/lcov-report/threads/query.js.html +278 -0
  155. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  156. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  157. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  158. package/coverage/lcov-report/utilities/csvParser.js.html +144 -15
  159. package/coverage/lcov-report/utilities/global.js.html +26 -11
  160. package/coverage/lcov-report/utilities/index.html +48 -48
  161. package/coverage/lcov-report/utilities/index.js.html +47 -14
  162. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +30 -9
  163. package/coverage/lcov-report/utilities/queue/index.html +14 -14
  164. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  165. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  166. package/coverage/lcov-report/utilities/rowProcessor/index.html +4 -4
  167. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +2 -2
  168. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +3 -3
  169. package/coverage/lcov-report/utilities/scriptRunner.js.html +16 -7
  170. package/coverage/lcov-report/utilities/security.js.html +3 -3
  171. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  172. package/coverage/lcov-report/utilities/users.js.html +1 -1
  173. package/coverage/lcov-report/utilities/workerRequests.js.html +5 -5
  174. package/coverage/lcov.info +2558 -1787
  175. package/dist/api/controllers/application.js +15 -1
  176. package/dist/api/controllers/auth.js +3 -3
  177. package/dist/api/controllers/datasource.js +67 -16
  178. package/dist/api/controllers/deploy/index.js +2 -0
  179. package/dist/api/controllers/dev.js +4 -1
  180. package/dist/api/controllers/query.js +30 -58
  181. package/dist/api/controllers/row/ExternalRequest.js +37 -14
  182. package/dist/api/controllers/row/external.js +3 -1
  183. package/dist/api/controllers/row/utils.js +6 -12
  184. package/dist/api/controllers/table/external.js +249 -0
  185. package/dist/api/controllers/table/index.js +49 -123
  186. package/dist/api/controllers/table/internal.js +150 -0
  187. package/dist/api/controllers/table/utils.js +62 -28
  188. package/dist/api/controllers/user.js +123 -11
  189. package/dist/api/controllers/view/exporters.js +4 -0
  190. package/dist/api/controllers/view/index.js +14 -2
  191. package/dist/api/routes/query.js +1 -1
  192. package/dist/api/routes/table.js +7 -1
  193. package/dist/api/routes/user.js +2 -1
  194. package/dist/app.js +2 -11
  195. package/dist/automations/bullboard.js +37 -10
  196. package/dist/automations/triggers.js +7 -3
  197. package/dist/automations/utils.js +11 -2
  198. package/dist/constants/index.js +19 -0
  199. package/dist/definitions/datasource.js +5 -0
  200. package/dist/environment.js +13 -0
  201. package/dist/integrations/base/IntegrationBase.js +2 -0
  202. package/dist/integrations/base/datasourcePlus.js +2 -0
  203. package/dist/integrations/base/sql.js +338 -180
  204. package/dist/integrations/base/sqlTable.js +143 -0
  205. package/dist/integrations/base/utils.js +30 -0
  206. package/dist/integrations/index.js +6 -0
  207. package/dist/integrations/microsoftSqlServer.js +117 -9
  208. package/dist/integrations/mysql.js +14 -84
  209. package/dist/integrations/oracle.js +345 -0
  210. package/dist/integrations/postgres.js +35 -28
  211. package/dist/integrations/utils.js +71 -9
  212. package/dist/middleware/authorized.js +22 -11
  213. package/dist/middleware/builder.js +4 -1
  214. package/dist/package.json +13 -8
  215. package/dist/tests/utilities/TestConfiguration.js +2 -1
  216. package/dist/{automations/thread.js → threads/automation.js} +26 -8
  217. package/dist/threads/index.js +64 -0
  218. package/dist/threads/query.js +64 -0
  219. package/dist/tsconfig.tsbuildinfo +1 -1
  220. package/dist/utilities/csvParser.js +46 -5
  221. package/dist/utilities/global.js +6 -2
  222. package/dist/utilities/index.js +11 -0
  223. package/dist/utilities/queue/inMemoryQueue.js +8 -0
  224. package/dist/utilities/scriptRunner.js +4 -1
  225. package/package.json +14 -9
  226. package/scripts/dev/manage.js +1 -0
  227. package/scripts/integrations/mssql/data/Dockerfile +9 -0
  228. package/scripts/integrations/mssql/data/entrypoint.sh +24 -0
  229. package/scripts/integrations/mssql/data/setup.sql +54 -0
  230. package/scripts/integrations/mssql/docker-compose.yaml +12 -0
  231. package/scripts/integrations/mssql/reset.sh +3 -0
  232. package/scripts/integrations/oracle/docker-compose.yml +19 -0
  233. package/scripts/integrations/oracle/instantclient/linux/x86-64/basiclite-21.4.zip +0 -0
  234. package/scripts/integrations/oracle/instantclient/linux/x86-64/install.sh +23 -0
  235. package/scripts/integrations/oracle/oracle.md +90 -0
  236. package/scripts/integrations/postgres/docker-compose.yml +3 -3
  237. package/scripts/likeCypress.ts +33 -0
  238. package/src/api/controllers/application.js +18 -1
  239. package/src/api/controllers/auth.js +3 -0
  240. package/src/api/controllers/datasource.js +84 -16
  241. package/src/api/controllers/deploy/index.js +2 -0
  242. package/src/api/controllers/dev.js +4 -1
  243. package/src/api/controllers/query.js +29 -81
  244. package/src/api/controllers/row/ExternalRequest.ts +47 -16
  245. package/src/api/controllers/row/external.js +4 -1
  246. package/src/api/controllers/row/utils.js +8 -11
  247. package/src/api/controllers/table/external.js +307 -0
  248. package/src/api/controllers/table/index.js +47 -137
  249. package/src/api/controllers/table/internal.js +155 -0
  250. package/src/api/controllers/table/utils.js +74 -33
  251. package/src/api/controllers/user.js +132 -13
  252. package/src/api/controllers/view/exporters.js +5 -0
  253. package/src/api/controllers/view/index.js +15 -2
  254. package/src/api/routes/query.js +1 -0
  255. package/src/api/routes/table.js +11 -0
  256. package/src/api/routes/tests/datasource.spec.js +2 -2
  257. package/src/api/routes/tests/misc.spec.js +5 -1
  258. package/src/api/routes/tests/query.spec.js +0 -1
  259. package/src/api/routes/user.js +5 -0
  260. package/src/app.ts +2 -10
  261. package/src/automations/bullboard.js +28 -11
  262. package/src/automations/tests/automation.spec.js +3 -4
  263. package/src/automations/triggers.js +7 -3
  264. package/src/automations/utils.js +12 -2
  265. package/src/constants/index.js +22 -0
  266. package/src/definitions/common.ts +7 -1
  267. package/src/definitions/datasource.ts +10 -3
  268. package/src/environment.js +15 -0
  269. package/src/integrations/airtable.ts +2 -1
  270. package/src/integrations/arangodb.ts +2 -1
  271. package/src/integrations/base/IntegrationBase.ts +6 -0
  272. package/src/integrations/base/datasourcePlus.ts +9 -0
  273. package/src/integrations/base/sql.ts +362 -222
  274. package/src/integrations/base/sqlTable.ts +180 -0
  275. package/src/integrations/base/utils.ts +22 -0
  276. package/src/integrations/couchdb.ts +2 -1
  277. package/src/integrations/dynamodb.ts +2 -1
  278. package/src/integrations/elasticsearch.ts +2 -1
  279. package/src/integrations/index.ts +7 -0
  280. package/src/integrations/microsoftSqlServer.ts +145 -13
  281. package/src/integrations/mongodb.ts +2 -1
  282. package/src/integrations/mysql.ts +20 -94
  283. package/src/integrations/oracle.ts +445 -0
  284. package/src/integrations/postgres.ts +45 -35
  285. package/src/integrations/rest.ts +2 -1
  286. package/src/integrations/s3.ts +2 -1
  287. package/src/integrations/tests/microsoftSqlServer.spec.js +22 -15
  288. package/src/integrations/tests/oracle.spec.js +94 -0
  289. package/src/integrations/tests/postgres.spec.js +4 -0
  290. package/src/integrations/utils.ts +74 -8
  291. package/src/middleware/authorized.js +26 -18
  292. package/src/middleware/builder.js +4 -1
  293. package/src/middleware/tests/authorized.spec.js +2 -0
  294. package/src/tests/utilities/TestConfiguration.js +2 -1
  295. package/src/{automations/thread.js → threads/automation.js} +27 -8
  296. package/src/threads/index.js +66 -0
  297. package/src/threads/query.js +66 -0
  298. package/src/utilities/csvParser.js +48 -5
  299. package/src/utilities/global.js +7 -2
  300. package/src/utilities/index.js +11 -0
  301. package/src/utilities/queue/inMemoryQueue.js +7 -0
  302. package/src/utilities/scriptRunner.js +4 -1
  303. package/src/utilities/tests/__snapshots__/csvParser.spec.js.snap +0 -6
  304. package/src/utilities/tests/csvParser.spec.js +3 -0
  305. package/tsconfig.json +1 -1
  306. package/builder/assets/index.10556cc1.js +0 -201
  307. package/builder/assets/index.7469e1e4.css +0 -29
  308. package/builder/assets/vendor.278a305c.js +0 -9
  309. package/builder/assets/vendor.d75d5eb7.css +0 -1
@@ -0,0 +1,94 @@
1
+ const oracledb = require("oracledb")
2
+ const OracleIntegration = require("../oracle")
3
+ jest.mock("oracledb")
4
+
5
+ class TestConfiguration {
6
+ constructor(config = {}) {
7
+ this.integration = new OracleIntegration.integration(config)
8
+ }
9
+ }
10
+
11
+ const options = { autoCommit: true }
12
+
13
+ describe("Oracle Integration", () => {
14
+ let config
15
+
16
+ beforeEach(() => {
17
+ jest.clearAllMocks()
18
+ config = new TestConfiguration()
19
+ })
20
+
21
+ afterEach(() => {
22
+ expect(oracledb.closeMock).toHaveBeenCalled()
23
+ expect(oracledb.closeMock).toHaveBeenCalledTimes(1)
24
+ })
25
+
26
+ it("calls the create method with the correct params", async () => {
27
+ const sql = "insert into users (name, age) values ('Joe', 123);"
28
+ await config.integration.create({
29
+ sql
30
+ })
31
+ expect(oracledb.executeMock).toHaveBeenCalledWith(sql, [], options)
32
+ expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
33
+ })
34
+
35
+ it("calls the read method with the correct params", async () => {
36
+ const sql = "select * from users;"
37
+ await config.integration.read({
38
+ sql
39
+ })
40
+ expect(oracledb.executeMock).toHaveBeenCalledWith(sql, [], options)
41
+ expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
42
+ })
43
+
44
+ it("calls the update method with the correct params", async () => {
45
+ const sql = "update table users set name = 'test';"
46
+ const response = await config.integration.update({
47
+ sql
48
+ })
49
+ expect(oracledb.executeMock).toHaveBeenCalledWith(sql, [], options)
50
+ expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
51
+ })
52
+
53
+ it("calls the delete method with the correct params", async () => {
54
+ const sql = "delete from users where name = 'todelete';"
55
+ await config.integration.delete({
56
+ sql
57
+ })
58
+ expect(oracledb.executeMock).toHaveBeenCalledWith(sql, [], options)
59
+ expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
60
+ })
61
+
62
+ describe("no rows returned", () => {
63
+ beforeEach(() => {
64
+ oracledb.executeMock.mockImplementation(() => ({ rows: [] }))
65
+ })
66
+
67
+ it("returns the correct response when the create response has no rows", async () => {
68
+ const sql = "insert into users (name, age) values ('Joe', 123);"
69
+ const response = await config.integration.create({
70
+ sql
71
+ })
72
+ expect(response).toEqual([{ created: true }])
73
+ expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
74
+ })
75
+
76
+ it("returns the correct response when the update response has no rows", async () => {
77
+ const sql = "update table users set name = 'test';"
78
+ const response = await config.integration.update({
79
+ sql
80
+ })
81
+ expect(response).toEqual([{ updated: true }])
82
+ expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
83
+ })
84
+
85
+ it("returns the correct response when the delete response has no rows", async () => {
86
+ const sql = "delete from users where name = 'todelete';"
87
+ const response = await config.integration.delete({
88
+ sql
89
+ })
90
+ expect(response).toEqual([{ deleted: true }])
91
+ expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
92
+ })
93
+ })
94
+ })
@@ -15,6 +15,10 @@ describe("Postgres Integration", () => {
15
15
  config = new TestConfiguration()
16
16
  })
17
17
 
18
+ it("calls the connection callback", async () => {
19
+ expect(pg.on).toHaveBeenCalledWith('connect', expect.anything())
20
+ })
21
+
18
22
  it("calls the create method with the correct params", async () => {
19
23
  const sql = "insert into users (name, age) values ('Joe', 123);"
20
24
  await config.integration.create({
@@ -1,12 +1,50 @@
1
1
  import { SqlQuery } from "../definitions/datasource"
2
- import { Datasource } from "../definitions/common"
2
+ import { Datasource, Table } from "../definitions/common"
3
3
  import { SourceNames } from "../definitions/datasource"
4
4
  const { DocumentTypes, SEPARATOR } = require("../db/utils")
5
- const { FieldTypes } = require("../constants")
5
+ const { FieldTypes, BuildSchemaErrors, InvalidColumns } = require("../constants")
6
6
 
7
7
  const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}`
8
8
  const ROW_ID_REGEX = /^\[.*]$/g
9
9
 
10
+ const SQL_TYPE_MAP = {
11
+ text: FieldTypes.LONGFORM,
12
+ varchar: FieldTypes.STRING,
13
+ integer: FieldTypes.NUMBER,
14
+ bigint: FieldTypes.NUMBER,
15
+ decimal: FieldTypes.NUMBER,
16
+ smallint: FieldTypes.NUMBER,
17
+ real: FieldTypes.NUMBER,
18
+ "double precision": FieldTypes.NUMBER,
19
+ timestamp: FieldTypes.DATETIME,
20
+ time: FieldTypes.DATETIME,
21
+ boolean: FieldTypes.BOOLEAN,
22
+ json: FieldTypes.JSON,
23
+ date: FieldTypes.DATETIME,
24
+ blob: FieldTypes.LONGFORM,
25
+ enum: FieldTypes.STRING,
26
+ float: FieldTypes.NUMBER,
27
+ int: FieldTypes.NUMBER,
28
+ numeric: FieldTypes.NUMBER,
29
+ mediumint: FieldTypes.NUMBER,
30
+ dec: FieldTypes.NUMBER,
31
+ double: FieldTypes.NUMBER,
32
+ fixed: FieldTypes.NUMBER,
33
+ datetime: FieldTypes.DATETIME,
34
+ tinyint: FieldTypes.BOOLEAN,
35
+ long: FieldTypes.LONGFORM,
36
+ number: FieldTypes.NUMBER,
37
+ binary_float: FieldTypes.NUMBER,
38
+ binary_double: FieldTypes.NUMBER,
39
+ }
40
+
41
+ export enum SqlClients {
42
+ MS_SQL = "mssql",
43
+ POSTGRES = "pg",
44
+ MY_SQL = "mysql",
45
+ ORACLE = "oracledb"
46
+ }
47
+
10
48
  export function isExternalTable(tableId: string) {
11
49
  return tableId.includes(DocumentTypes.DATASOURCE)
12
50
  }
@@ -68,8 +106,8 @@ export function breakRowIdField(_id: string | { _id: string }): any[] {
68
106
  }
69
107
  }
70
108
 
71
- export function convertType(type: string, map: { [key: string]: any }) {
72
- for (let [external, internal] of Object.entries(map)) {
109
+ export function convertSqlType(type: string) {
110
+ for (let [external, internal] of Object.entries(SQL_TYPE_MAP)) {
73
111
  if (type.toLowerCase().includes(external)) {
74
112
  return internal
75
113
  }
@@ -102,14 +140,14 @@ export function isIsoDateString(str: string) {
102
140
  }
103
141
 
104
142
  // add the existing relationships from the entities if they exist, to prevent them from being overridden
105
- export function copyExistingPropsOver(
143
+ function copyExistingPropsOver(
106
144
  tableName: string,
107
- tables: { [key: string]: any },
145
+ table: Table,
108
146
  entities: { [key: string]: any }
109
147
  ) {
110
148
  if (entities && entities[tableName]) {
111
149
  if (entities[tableName].primaryDisplay) {
112
- tables[tableName].primaryDisplay = entities[tableName].primaryDisplay
150
+ table.primaryDisplay = entities[tableName].primaryDisplay
113
151
  }
114
152
  const existingTableSchema = entities[tableName].schema
115
153
  for (let key in existingTableSchema) {
@@ -117,8 +155,36 @@ export function copyExistingPropsOver(
117
155
  continue
118
156
  }
119
157
  if (existingTableSchema[key].type === "link") {
120
- tables[tableName].schema[key] = existingTableSchema[key]
158
+ table.schema[key] = existingTableSchema[key]
121
159
  }
122
160
  }
123
161
  }
162
+ return table
163
+ }
164
+
165
+ export function finaliseExternalTables(
166
+ tables: { [key: string]: any },
167
+ entities: { [key: string]: any }
168
+ ) {
169
+ const invalidColumns = Object.values(InvalidColumns)
170
+ let finalTables: { [key: string]: any } = {}
171
+ const errors: { [key: string]: string } = {}
172
+ for (let [name, table] of Object.entries(tables)) {
173
+ const schemaFields = Object.keys(table.schema)
174
+ // make sure every table has a key
175
+ if (table.primary == null || table.primary.length === 0) {
176
+ errors[name] = BuildSchemaErrors.NO_KEY
177
+ continue
178
+ } else if (schemaFields.find(field => invalidColumns.includes(field))) {
179
+ errors[name] = BuildSchemaErrors.INVALID_COLUMN
180
+ continue
181
+ }
182
+ // make sure all previous props have been added back
183
+ finalTables[name] = copyExistingPropsOver(name, table, entities)
184
+ }
185
+ // sort the tables by name
186
+ finalTables = Object.entries(finalTables)
187
+ .sort(([a], [b]) => a.localeCompare(b))
188
+ .reduce((r, [k, v]) => ({ ...r, [k]: v }), {})
189
+ return { tables: finalTables, errors }
124
190
  }
@@ -1,7 +1,10 @@
1
- const { getUserPermissions } = require("@budibase/auth/roles")
1
+ const {
2
+ getUserRoleHierarchy,
3
+ getRequiredResourceRole,
4
+ BUILTIN_ROLE_IDS,
5
+ } = require("@budibase/auth/roles")
2
6
  const {
3
7
  PermissionTypes,
4
- doesHaveResourcePermission,
5
8
  doesHaveBasePermission,
6
9
  } = require("@budibase/auth/permissions")
7
10
  const builderMiddleware = require("./builder")
@@ -15,7 +18,8 @@ module.exports =
15
18
  (permType, permLevel = null) =>
16
19
  async (ctx, next) => {
17
20
  // webhooks don't need authentication, each webhook unique
18
- if (isWebhookEndpoint(ctx)) {
21
+ // also internal requests (between services) don't need authorized
22
+ if (isWebhookEndpoint(ctx) || ctx.internal) {
19
23
  return next()
20
24
  }
21
25
 
@@ -28,13 +32,7 @@ module.exports =
28
32
  await builderMiddleware(ctx, permType)
29
33
 
30
34
  const isAuthed = ctx.isAuthenticated
31
- const { basePermissions, permissions } = await getUserPermissions(
32
- ctx.appId,
33
- ctx.roleId
34
- )
35
-
36
35
  // builders for now have permission to do anything
37
- // TODO: in future should consider separating permissions with an require("@budibase/auth").isClient check
38
36
  let isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global
39
37
  const isBuilderApi = permType === PermissionTypes.BUILDER
40
38
  if (isBuilder) {
@@ -43,20 +41,30 @@ module.exports =
43
41
  return ctx.throw(403, "Not Authorized")
44
42
  }
45
43
 
46
- if (
47
- hasResource(ctx) &&
48
- doesHaveResourcePermission(permissions, permLevel, ctx)
49
- ) {
50
- return next()
44
+ // need to check this first, in-case public access, don't check authed until last
45
+ const roleId = ctx.roleId || BUILTIN_ROLE_IDS.PUBLIC
46
+ const hierarchy = await getUserRoleHierarchy(ctx.appId, roleId, {
47
+ idOnly: false,
48
+ })
49
+ const permError = "User does not have permission"
50
+ let possibleRoleIds = []
51
+ if (hasResource(ctx)) {
52
+ possibleRoleIds = await getRequiredResourceRole(ctx.appId, permLevel, ctx)
53
+ }
54
+ // check if we found a role, if not fallback to base permissions
55
+ if (possibleRoleIds.length > 0) {
56
+ const found = hierarchy.find(
57
+ role => possibleRoleIds.indexOf(role._id) !== -1
58
+ )
59
+ return found ? next() : ctx.throw(403, permError)
60
+ } else if (!doesHaveBasePermission(permType, permLevel, hierarchy)) {
61
+ ctx.throw(403, permError)
51
62
  }
52
63
 
64
+ // if they are not authed, then anything using the authorized middleware will fail
53
65
  if (!isAuthed) {
54
66
  ctx.throw(403, "Session not authenticated")
55
67
  }
56
68
 
57
- if (!doesHaveBasePermission(permType, permLevel, basePermissions)) {
58
- ctx.throw(403, "User does not have permission")
59
- }
60
-
61
69
  return next()
62
70
  }
@@ -8,6 +8,7 @@ const {
8
8
  const CouchDB = require("../db")
9
9
  const { DocumentTypes } = require("../db/utils")
10
10
  const { PermissionTypes } = require("@budibase/auth/permissions")
11
+ const { app: appCache } = require("@budibase/auth/cache")
11
12
 
12
13
  const DEBOUNCE_TIME_SEC = 30
13
14
 
@@ -50,7 +51,9 @@ async function updateAppUpdatedAt(ctx) {
50
51
  const db = new CouchDB(appId)
51
52
  const metadata = await db.get(DocumentTypes.APP_METADATA)
52
53
  metadata.updatedAt = new Date().toISOString()
53
- await db.put(metadata)
54
+ const response = await db.put(metadata)
55
+ metadata._rev = response.rev
56
+ await appCache.invalidateAppMetadata(appId, metadata)
54
57
  // set a new debounce record with a short TTL
55
58
  await setDebounce(appId, DEBOUNCE_TIME_SEC)
56
59
  }
@@ -10,6 +10,7 @@ jest.mock("../../environment", () => ({
10
10
  const authorizedMiddleware = require("../authorized")
11
11
  const env = require("../../environment")
12
12
  const { PermissionTypes, PermissionLevels } = require("@budibase/auth/permissions")
13
+ require("@budibase/auth").init(require("../../db"))
13
14
 
14
15
  class TestConfiguration {
15
16
  constructor(role) {
@@ -21,6 +22,7 @@ class TestConfiguration {
21
22
  request: {
22
23
  url: ""
23
24
  },
25
+ appId: "",
24
26
  auth: {},
25
27
  next: this.next,
26
28
  throw: this.throw
@@ -311,7 +311,8 @@ class TestConfiguration {
311
311
 
312
312
  async createDatasource(config = null) {
313
313
  config = config || basicDatasource()
314
- this.datasource = await this._req(config, null, controllers.datasource.save)
314
+ const response = await this._req(config, null, controllers.datasource.save)
315
+ this.datasource = response.datasource
315
316
  return this.datasource
316
317
  }
317
318
 
@@ -1,15 +1,19 @@
1
- const actions = require("./actions")
2
- const automationUtils = require("./automationUtils")
1
+ // when thread starts, make sure it is recorded
2
+ const env = require("../environment")
3
+ env.setInThread()
4
+ const actions = require("../automations/actions")
5
+ const automationUtils = require("../automations/automationUtils")
3
6
  const AutomationEmitter = require("../events/AutomationEmitter")
4
7
  const { processObject } = require("@budibase/string-templates")
5
8
  const { DEFAULT_TENANT_ID } = require("@budibase/auth").constants
6
9
  const CouchDB = require("../db")
7
10
  const { DocumentTypes, isDevAppID } = require("../db/utils")
8
11
  const { doInTenant } = require("@budibase/auth/tenancy")
9
- const env = require("../environment")
10
12
  const usage = require("../utilities/usageQuota")
13
+ const { definitions: triggerDefs } = require("../automations/triggerInfo")
11
14
 
12
15
  const FILTER_STEP_ID = actions.ACTION_DEFINITIONS.FILTER.stepId
16
+ const CRON_STEP_ID = triggerDefs.CRON.stepId
13
17
  const STOPPED_STATUS = { success: false, status: "STOPPED" }
14
18
 
15
19
  /**
@@ -23,6 +27,8 @@ class Orchestrator {
23
27
  this._chainCount = this._metadata ? this._metadata.automationChainCount : 0
24
28
  this._appId = triggerOutput.appId
25
29
  this._app = null
30
+ const triggerStepId = automation.definition.trigger.stepId
31
+ triggerOutput = this.cleanupTriggerOutputs(triggerStepId, triggerOutput)
26
32
  // remove from context
27
33
  delete triggerOutput.appId
28
34
  delete triggerOutput.metadata
@@ -34,11 +40,17 @@ class Orchestrator {
34
40
  this._emitter = new AutomationEmitter(this._chainCount + 1)
35
41
  this.executionOutput = { trigger: {}, steps: [] }
36
42
  // setup the execution output
37
- const triggerStepId = automation.definition.trigger.stepId
38
43
  const triggerId = automation.definition.trigger.id
39
44
  this.updateExecutionOutput(triggerId, triggerStepId, null, triggerOutput)
40
45
  }
41
46
 
47
+ cleanupTriggerOutputs(stepId, triggerOutput) {
48
+ if (stepId === CRON_STEP_ID) {
49
+ triggerOutput.timestamp = Date.now()
50
+ }
51
+ return triggerOutput
52
+ }
53
+
42
54
  async getStepFunctionality(stepId) {
43
55
  let step = await actions.getAction(stepId)
44
56
  if (step == null) {
@@ -119,10 +131,17 @@ class Orchestrator {
119
131
  }
120
132
  }
121
133
 
122
- module.exports = async job => {
134
+ module.exports = (input, callback) => {
123
135
  const automationOrchestrator = new Orchestrator(
124
- job.data.automation,
125
- job.data.event
136
+ input.data.automation,
137
+ input.data.event
126
138
  )
127
- return automationOrchestrator.execute()
139
+ automationOrchestrator
140
+ .execute()
141
+ .then(response => {
142
+ callback(null, response)
143
+ })
144
+ .catch(err => {
145
+ callback(err)
146
+ })
128
147
  }
@@ -0,0 +1,66 @@
1
+ const workerFarm = require("worker-farm")
2
+ const env = require("../environment")
3
+
4
+ const ThreadType = {
5
+ QUERY: "query",
6
+ AUTOMATION: "automation",
7
+ }
8
+
9
+ function typeToFile(type) {
10
+ let filename = null
11
+ switch (type) {
12
+ case ThreadType.QUERY:
13
+ filename = "./query"
14
+ break
15
+ case ThreadType.AUTOMATION:
16
+ filename = "./automation"
17
+ break
18
+ default:
19
+ throw "Unknown thread type"
20
+ }
21
+ return require.resolve(filename)
22
+ }
23
+
24
+ class Thread {
25
+ constructor(type, opts = { timeoutMs: null, count: 1 }) {
26
+ this.type = type
27
+ this.count = opts.count ? opts.count : 1
28
+ this.disableThreading =
29
+ env.isTest() ||
30
+ env.DISABLE_THREADING ||
31
+ this.count === 0 ||
32
+ env.isInThread()
33
+ if (!this.disableThreading) {
34
+ const workerOpts = {
35
+ autoStart: true,
36
+ maxConcurrentWorkers: this.count,
37
+ }
38
+ if (opts.timeoutMs) {
39
+ workerOpts.maxCallTime = opts.timeoutMs
40
+ }
41
+ this.workers = workerFarm(workerOpts, typeToFile(type))
42
+ }
43
+ }
44
+
45
+ run(data) {
46
+ return new Promise((resolve, reject) => {
47
+ let fncToCall
48
+ // if in test then don't use threading
49
+ if (this.disableThreading) {
50
+ fncToCall = require(typeToFile(this.type))
51
+ } else {
52
+ fncToCall = this.workers
53
+ }
54
+ fncToCall(data, (err, response) => {
55
+ if (err) {
56
+ reject(err)
57
+ } else {
58
+ resolve(response)
59
+ }
60
+ })
61
+ })
62
+ }
63
+ }
64
+
65
+ module.exports.Thread = Thread
66
+ module.exports.ThreadType = ThreadType
@@ -0,0 +1,66 @@
1
+ // when thread starts, make sure it is recorded
2
+ const env = require("../environment")
3
+ env.setInThread()
4
+ const ScriptRunner = require("../utilities/scriptRunner")
5
+ const { integrations } = require("../integrations")
6
+
7
+ function formatResponse(resp) {
8
+ if (typeof resp === "string") {
9
+ try {
10
+ resp = JSON.parse(resp)
11
+ } catch (err) {
12
+ resp = { response: resp }
13
+ }
14
+ }
15
+ return resp
16
+ }
17
+
18
+ async function runAndTransform(datasource, queryVerb, query, transformer) {
19
+ const Integration = integrations[datasource.source]
20
+ if (!Integration) {
21
+ throw "Integration type does not exist."
22
+ }
23
+ const integration = new Integration(datasource.config)
24
+
25
+ let rows = formatResponse(await integration[queryVerb](query))
26
+
27
+ // transform as required
28
+ if (transformer) {
29
+ const runner = new ScriptRunner(transformer, { data: rows })
30
+ rows = runner.execute()
31
+ }
32
+
33
+ // needs to an array for next step
34
+ if (!Array.isArray(rows)) {
35
+ rows = [rows]
36
+ }
37
+
38
+ // map into JSON if just raw primitive here
39
+ if (rows.find(row => typeof row !== "object")) {
40
+ rows = rows.map(value => ({ value }))
41
+ }
42
+
43
+ // get all the potential fields in the schema
44
+ let keys = rows.flatMap(Object.keys)
45
+
46
+ if (integration.end) {
47
+ integration.end()
48
+ }
49
+
50
+ return { rows, keys }
51
+ }
52
+
53
+ module.exports = (input, callback) => {
54
+ runAndTransform(
55
+ input.datasource,
56
+ input.queryVerb,
57
+ input.query,
58
+ input.transformer
59
+ )
60
+ .then(response => {
61
+ callback(null, response)
62
+ })
63
+ .catch(err => {
64
+ callback(err)
65
+ })
66
+ }
@@ -51,7 +51,7 @@ function parse(csvString, parsers) {
51
51
  })
52
52
  result.subscribe(row => {
53
53
  // For each CSV row parse all the columns that need parsed
54
- for (let key in parsers) {
54
+ for (let key of Object.keys(parsers)) {
55
55
  if (!schema[key] || schema[key].success) {
56
56
  // get the validator for the column type
57
57
  const validator = VALIDATORS[parsers[key].type]
@@ -76,16 +76,58 @@ function parse(csvString, parsers) {
76
76
  })
77
77
  }
78
78
 
79
- async function transform({ schema, csvString }) {
79
+ function updateSchema({ schema, existingTable }) {
80
+ if (!schema) {
81
+ return schema
82
+ }
83
+ const finalSchema = {}
84
+ const schemaKeyMap = {}
85
+ Object.keys(schema).forEach(key => (schemaKeyMap[key.toLowerCase()] = key))
86
+ for (let [key, field] of Object.entries(existingTable.schema)) {
87
+ const lcKey = key.toLowerCase()
88
+ const foundKey = schemaKeyMap[lcKey]
89
+ if (foundKey) {
90
+ finalSchema[key] = schema[foundKey]
91
+ finalSchema[key].type = field.type
92
+ }
93
+ }
94
+ return finalSchema
95
+ }
96
+
97
+ async function transform({ schema, csvString, existingTable }) {
80
98
  const colParser = {}
81
99
 
82
- for (let key in schema) {
100
+ // make sure the table has all the columns required for import
101
+ if (existingTable) {
102
+ schema = updateSchema({ schema, existingTable })
103
+ }
104
+
105
+ for (let key of Object.keys(schema)) {
83
106
  colParser[key] = PARSERS[schema[key].type] || schema[key].type
84
107
  }
85
108
 
86
109
  try {
87
- const json = await csv({ colParser }).fromString(csvString)
88
- return json
110
+ const data = await csv({ colParser }).fromString(csvString)
111
+ const schemaKeyMap = {}
112
+ Object.keys(schema).forEach(key => (schemaKeyMap[key.toLowerCase()] = key))
113
+ for (let element of data) {
114
+ if (!data) {
115
+ continue
116
+ }
117
+ for (let key of Object.keys(element)) {
118
+ const mappedKey = schemaKeyMap[key.toLowerCase()]
119
+ // isn't a column in the table, remove it
120
+ if (mappedKey == null) {
121
+ delete element[key]
122
+ }
123
+ // casing is different, fix it in row
124
+ else if (key !== mappedKey) {
125
+ element[mappedKey] = element[key]
126
+ delete element[key]
127
+ }
128
+ }
129
+ }
130
+ return data
89
131
  } catch (err) {
90
132
  console.error(`Error transforming CSV to JSON for data import`, err)
91
133
  throw err
@@ -95,4 +137,5 @@ async function transform({ schema, csvString }) {
95
137
  module.exports = {
96
138
  parse,
97
139
  transform,
140
+ updateSchema,
98
141
  }
@@ -46,9 +46,13 @@ exports.getCachedSelf = async (ctx, appId) => {
46
46
  return processUser(appId, user)
47
47
  }
48
48
 
49
- exports.getGlobalUser = async (appId, userId) => {
49
+ exports.getRawGlobalUser = async userId => {
50
50
  const db = getGlobalDB()
51
- let user = await db.get(getGlobalIDFromUserMetadataID(userId))
51
+ return db.get(getGlobalIDFromUserMetadataID(userId))
52
+ }
53
+
54
+ exports.getGlobalUser = async (appId, userId) => {
55
+ let user = await exports.getRawGlobalUser(userId)
52
56
  return processUser(appId, user)
53
57
  }
54
58
 
@@ -73,6 +77,7 @@ exports.getGlobalUsers = async (appId = null, users = null) => {
73
77
  .filter(user => user != null)
74
78
  .map(user => {
75
79
  delete user.password
80
+ delete user.forceResetPassword
76
81
  return user
77
82
  })
78
83
  if (!appId) {
@@ -48,6 +48,7 @@ exports.objectStoreUrl = () => {
48
48
  * via a specific endpoint (under /api/assets/client).
49
49
  * @param {string} appId In production we need the appId to look up the correct bucket, as the
50
50
  * version of the client lib may differ between apps.
51
+ * @param {string} version The version to retrieve.
51
52
  * @return {string} The URL to be inserted into appPackage response or server rendered
52
53
  * app index file.
53
54
  */
@@ -139,3 +140,13 @@ exports.stringToReadStream = string => {
139
140
  },
140
141
  })
141
142
  }
143
+
144
+ exports.doesDatabaseExist = async dbName => {
145
+ try {
146
+ const db = new CouchDB(dbName, { skip_setup: true })
147
+ const info = await db.info()
148
+ return info && !info.error
149
+ } catch (err) {
150
+ return false
151
+ }
152
+ }