@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
@@ -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,54 @@
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 {
6
+ FieldTypes,
7
+ BuildSchemaErrors,
8
+ InvalidColumns,
9
+ } = require("../constants")
6
10
 
7
11
  const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}`
8
12
  const ROW_ID_REGEX = /^\[.*]$/g
9
13
 
14
+ const SQL_TYPE_MAP = {
15
+ text: FieldTypes.LONGFORM,
16
+ varchar: FieldTypes.STRING,
17
+ integer: FieldTypes.NUMBER,
18
+ bigint: FieldTypes.NUMBER,
19
+ decimal: FieldTypes.NUMBER,
20
+ smallint: FieldTypes.NUMBER,
21
+ real: FieldTypes.NUMBER,
22
+ "double precision": FieldTypes.NUMBER,
23
+ timestamp: FieldTypes.DATETIME,
24
+ time: FieldTypes.DATETIME,
25
+ boolean: FieldTypes.BOOLEAN,
26
+ json: FieldTypes.JSON,
27
+ date: FieldTypes.DATETIME,
28
+ blob: FieldTypes.LONGFORM,
29
+ enum: FieldTypes.STRING,
30
+ float: FieldTypes.NUMBER,
31
+ int: FieldTypes.NUMBER,
32
+ numeric: FieldTypes.NUMBER,
33
+ mediumint: FieldTypes.NUMBER,
34
+ dec: FieldTypes.NUMBER,
35
+ double: FieldTypes.NUMBER,
36
+ fixed: FieldTypes.NUMBER,
37
+ datetime: FieldTypes.DATETIME,
38
+ tinyint: FieldTypes.BOOLEAN,
39
+ long: FieldTypes.LONGFORM,
40
+ number: FieldTypes.NUMBER,
41
+ binary_float: FieldTypes.NUMBER,
42
+ binary_double: FieldTypes.NUMBER,
43
+ }
44
+
45
+ export enum SqlClients {
46
+ MS_SQL = "mssql",
47
+ POSTGRES = "pg",
48
+ MY_SQL = "mysql",
49
+ ORACLE = "oracledb",
50
+ }
51
+
10
52
  export function isExternalTable(tableId: string) {
11
53
  return tableId.includes(DocumentTypes.DATASOURCE)
12
54
  }
@@ -68,8 +110,8 @@ export function breakRowIdField(_id: string | { _id: string }): any[] {
68
110
  }
69
111
  }
70
112
 
71
- export function convertType(type: string, map: { [key: string]: any }) {
72
- for (let [external, internal] of Object.entries(map)) {
113
+ export function convertSqlType(type: string) {
114
+ for (let [external, internal] of Object.entries(SQL_TYPE_MAP)) {
73
115
  if (type.toLowerCase().includes(external)) {
74
116
  return internal
75
117
  }
@@ -102,14 +144,14 @@ export function isIsoDateString(str: string) {
102
144
  }
103
145
 
104
146
  // add the existing relationships from the entities if they exist, to prevent them from being overridden
105
- export function copyExistingPropsOver(
147
+ function copyExistingPropsOver(
106
148
  tableName: string,
107
- tables: { [key: string]: any },
149
+ table: Table,
108
150
  entities: { [key: string]: any }
109
151
  ) {
110
152
  if (entities && entities[tableName]) {
111
153
  if (entities[tableName].primaryDisplay) {
112
- tables[tableName].primaryDisplay = entities[tableName].primaryDisplay
154
+ table.primaryDisplay = entities[tableName].primaryDisplay
113
155
  }
114
156
  const existingTableSchema = entities[tableName].schema
115
157
  for (let key in existingTableSchema) {
@@ -117,8 +159,36 @@ export function copyExistingPropsOver(
117
159
  continue
118
160
  }
119
161
  if (existingTableSchema[key].type === "link") {
120
- tables[tableName].schema[key] = existingTableSchema[key]
162
+ table.schema[key] = existingTableSchema[key]
121
163
  }
122
164
  }
123
165
  }
166
+ return table
167
+ }
168
+
169
+ export function finaliseExternalTables(
170
+ tables: { [key: string]: any },
171
+ entities: { [key: string]: any }
172
+ ) {
173
+ const invalidColumns = Object.values(InvalidColumns)
174
+ let finalTables: { [key: string]: any } = {}
175
+ const errors: { [key: string]: string } = {}
176
+ for (let [name, table] of Object.entries(tables)) {
177
+ const schemaFields = Object.keys(table.schema)
178
+ // make sure every table has a key
179
+ if (table.primary == null || table.primary.length === 0) {
180
+ errors[name] = BuildSchemaErrors.NO_KEY
181
+ continue
182
+ } else if (schemaFields.find(field => invalidColumns.includes(field))) {
183
+ errors[name] = BuildSchemaErrors.INVALID_COLUMN
184
+ continue
185
+ }
186
+ // make sure all previous props have been added back
187
+ finalTables[name] = copyExistingPropsOver(name, table, entities)
188
+ }
189
+ // sort the tables by name
190
+ finalTables = Object.entries(finalTables)
191
+ .sort(([a], [b]) => a.localeCompare(b))
192
+ .reduce((r, [k, v]) => ({ ...r, [k]: v }), {})
193
+ return { tables: finalTables, errors }
124
194
  }
@@ -18,7 +18,8 @@ module.exports =
18
18
  (permType, permLevel = null) =>
19
19
  async (ctx, next) => {
20
20
  // webhooks don't need authentication, each webhook unique
21
- if (isWebhookEndpoint(ctx)) {
21
+ // also internal requests (between services) don't need authorized
22
+ if (isWebhookEndpoint(ctx) || ctx.internal) {
22
23
  return next()
23
24
  }
24
25
 
@@ -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
  }
@@ -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
+ }
@@ -89,6 +89,13 @@ class InMemoryQueue {
89
89
  getRepeatableJobs() {
90
90
  return []
91
91
  }
92
+
93
+ /**
94
+ * Implemented for tests
95
+ */
96
+ async clean() {
97
+ return []
98
+ }
92
99
  }
93
100
 
94
101
  module.exports = InMemoryQueue
@@ -1,10 +1,13 @@
1
1
  const fetch = require("node-fetch")
2
2
  const { VM, VMScript } = require("vm2")
3
+ const JS_TIMEOUT_MS = 1000
3
4
 
4
5
  class ScriptRunner {
5
6
  constructor(script, context) {
6
7
  const code = `let fn = () => {\n${script}\n}; results.out = fn();`
7
- this.vm = new VM()
8
+ this.vm = new VM({
9
+ timeout: JS_TIMEOUT_MS,
10
+ })
8
11
  this.results = { out: "" }
9
12
  this.vm.setGlobals(context)
10
13
  this.vm.setGlobal("fetch", fetch)
@@ -3,19 +3,13 @@
3
3
  exports[`CSV Parser transformation transforms a CSV file into JSON 1`] = `
4
4
  Array [
5
5
  Object {
6
- "Address": "5 Sesame Street",
7
6
  "Age": 4324,
8
- "Name": "Bertå",
9
7
  },
10
8
  Object {
11
- "Address": "1 World Trade Center",
12
9
  "Age": 34,
13
- "Name": "Ernie",
14
10
  },
15
11
  Object {
16
- "Address": "44 Second Avenue",
17
12
  "Age": 23423,
18
- "Name": "Big Bird",
19
13
  },
20
14
  ]
21
15
  `;
@@ -24,6 +24,9 @@ const SCHEMAS = {
24
24
  Age: {
25
25
  type: "omit",
26
26
  },
27
+ Name: {
28
+ type: "string",
29
+ },
27
30
  },
28
31
  BROKEN: {
29
32
  Address: {
package/tsconfig.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "compilerOptions": {
3
3
  "target": "es6",
4
4
  "module": "commonjs",
5
- "lib": ["es6"],
5
+ "lib": ["es2019"],
6
6
  "allowJs": true,
7
7
  "outDir": "dist",
8
8
  "strict": true,