@budibase/server 1.0.78 → 1.0.79-alpha.10

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 (305) hide show
  1. package/__mocks__/node-fetch.ts +1 -0
  2. package/builder/assets/index.1a081633.js +1288 -0
  3. package/builder/assets/{index.c8d1e8f0.css → index.c57242d4.css} +2 -2
  4. package/builder/assets/{vendor.20f80cec.js → vendor.a7c6ad0a.js} +190 -216
  5. package/builder/index.html +3 -3
  6. package/coverage/clover.xml +2399 -2336
  7. package/coverage/coverage-final.json +138 -137
  8. package/coverage/lcov-report/api/controllers/analytics.js.html +6 -6
  9. package/coverage/lcov-report/api/controllers/apikeys.js.html +6 -6
  10. package/coverage/lcov-report/api/controllers/application.js.html +152 -149
  11. package/coverage/lcov-report/api/controllers/auth.js.html +7 -7
  12. package/coverage/lcov-report/api/controllers/automation.js.html +21 -21
  13. package/coverage/lcov-report/api/controllers/backup.js.html +3 -3
  14. package/coverage/lcov-report/api/controllers/cloud.js.html +11 -11
  15. package/coverage/lcov-report/api/controllers/component.js.html +5 -5
  16. package/coverage/lcov-report/api/controllers/datasource.js.html +16 -16
  17. package/coverage/lcov-report/api/controllers/dev.js.html +19 -19
  18. package/coverage/lcov-report/api/controllers/index.html +16 -16
  19. package/coverage/lcov-report/api/controllers/integration.js.html +4 -4
  20. package/coverage/lcov-report/api/controllers/layout.js.html +6 -6
  21. package/coverage/lcov-report/api/controllers/metadata.js.html +9 -9
  22. package/coverage/lcov-report/api/controllers/migrations.js.html +4 -4
  23. package/coverage/lcov-report/api/controllers/permission.js.html +15 -15
  24. package/coverage/lcov-report/api/controllers/query/index.html +1 -1
  25. package/coverage/lcov-report/api/controllers/query/index.js.html +26 -26
  26. package/coverage/lcov-report/api/controllers/query/validation.js.html +10 -10
  27. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  28. package/coverage/lcov-report/api/controllers/routing.js.html +9 -9
  29. package/coverage/lcov-report/api/controllers/row/external.js.html +16 -16
  30. package/coverage/lcov-report/api/controllers/row/index.html +27 -27
  31. package/coverage/lcov-report/api/controllers/row/index.js.html +46 -46
  32. package/coverage/lcov-report/api/controllers/row/internal.js.html +94 -82
  33. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +204 -204
  34. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +24 -24
  35. package/coverage/lcov-report/api/controllers/row/utils.js.html +29 -29
  36. package/coverage/lcov-report/api/controllers/screen.js.html +7 -7
  37. package/coverage/lcov-report/api/controllers/script.js.html +4 -4
  38. package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +49 -49
  39. package/coverage/lcov-report/api/controllers/table/external.js.html +12 -12
  40. package/coverage/lcov-report/api/controllers/table/index.html +11 -11
  41. package/coverage/lcov-report/api/controllers/table/index.js.html +47 -44
  42. package/coverage/lcov-report/api/controllers/table/internal.js.html +50 -50
  43. package/coverage/lcov-report/api/controllers/table/utils.js.html +80 -80
  44. package/coverage/lcov-report/api/controllers/templates.js.html +5 -5
  45. package/coverage/lcov-report/api/controllers/user.js.html +19 -19
  46. package/coverage/lcov-report/api/controllers/view/exporters.js.html +4 -4
  47. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  48. package/coverage/lcov-report/api/controllers/view/index.js.html +14 -14
  49. package/coverage/lcov-report/api/controllers/view/utils.js.html +12 -12
  50. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +7 -7
  51. package/coverage/lcov-report/api/controllers/webhook.js.html +18 -27
  52. package/coverage/lcov-report/api/index.html +10 -10
  53. package/coverage/lcov-report/api/index.js.html +37 -28
  54. package/coverage/lcov-report/api/routes/analytics.js.html +6 -6
  55. package/coverage/lcov-report/api/routes/apikeys.js.html +8 -8
  56. package/coverage/lcov-report/api/routes/application.js.html +9 -9
  57. package/coverage/lcov-report/api/routes/auth.js.html +6 -6
  58. package/coverage/lcov-report/api/routes/automation.js.html +19 -109
  59. package/coverage/lcov-report/api/routes/backup.js.html +8 -8
  60. package/coverage/lcov-report/api/routes/cloud.js.html +8 -8
  61. package/coverage/lcov-report/api/routes/component.js.html +8 -8
  62. package/coverage/lcov-report/api/routes/datasource.js.html +18 -156
  63. package/coverage/lcov-report/api/routes/deploy.js.html +8 -8
  64. package/coverage/lcov-report/api/routes/dev.js.html +13 -13
  65. package/coverage/lcov-report/api/routes/index.html +35 -35
  66. package/coverage/lcov-report/api/routes/index.js.html +38 -32
  67. package/coverage/lcov-report/api/routes/integration.js.html +8 -8
  68. package/coverage/lcov-report/api/routes/layout.js.html +8 -8
  69. package/coverage/lcov-report/api/routes/metadata.js.html +9 -9
  70. package/coverage/lcov-report/api/routes/migrations.js.html +7 -7
  71. package/coverage/lcov-report/api/routes/permission.js.html +16 -58
  72. package/coverage/lcov-report/api/routes/query.js.html +10 -10
  73. package/coverage/lcov-report/api/routes/role.js.html +15 -78
  74. package/coverage/lcov-report/api/routes/routing.js.html +8 -8
  75. package/coverage/lcov-report/api/routes/row.js.html +17 -14
  76. package/coverage/lcov-report/api/routes/screen.js.html +14 -95
  77. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/static.js.html +13 -13
  79. package/coverage/lcov-report/api/routes/table.js.html +17 -62
  80. package/coverage/lcov-report/api/routes/templates.js.html +8 -8
  81. package/coverage/lcov-report/api/routes/user.js.html +8 -8
  82. package/coverage/lcov-report/api/routes/utils/index.html +111 -0
  83. package/coverage/lcov-report/api/routes/utils/validators.js.html +737 -0
  84. package/coverage/lcov-report/api/routes/view.js.html +10 -10
  85. package/coverage/lcov-report/api/routes/webhook.js.html +14 -62
  86. package/coverage/lcov-report/automations/actions.js.html +24 -24
  87. package/coverage/lcov-report/automations/automationUtils.js.html +5 -5
  88. package/coverage/lcov-report/automations/bullboard.js.html +16 -16
  89. package/coverage/lcov-report/automations/index.html +1 -1
  90. package/coverage/lcov-report/automations/index.js.html +7 -7
  91. package/coverage/lcov-report/automations/steps/bash.js.html +6 -6
  92. package/coverage/lcov-report/automations/steps/createRow.js.html +7 -7
  93. package/coverage/lcov-report/automations/steps/delay.js.html +4 -4
  94. package/coverage/lcov-report/automations/steps/deleteRow.js.html +7 -7
  95. package/coverage/lcov-report/automations/steps/discord.js.html +7 -7
  96. package/coverage/lcov-report/automations/steps/executeQuery.js.html +6 -6
  97. package/coverage/lcov-report/automations/steps/executeScript.js.html +6 -6
  98. package/coverage/lcov-report/automations/steps/filter.js.html +12 -9
  99. package/coverage/lcov-report/automations/steps/index.html +1 -1
  100. package/coverage/lcov-report/automations/steps/integromat.js.html +5 -5
  101. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +8 -8
  102. package/coverage/lcov-report/automations/steps/queryRows.js.html +11 -11
  103. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +5 -5
  104. package/coverage/lcov-report/automations/steps/serverLog.js.html +3 -3
  105. package/coverage/lcov-report/automations/steps/slack.js.html +5 -5
  106. package/coverage/lcov-report/automations/steps/updateRow.js.html +6 -6
  107. package/coverage/lcov-report/automations/steps/utils.js.html +3 -3
  108. package/coverage/lcov-report/automations/steps/zapier.js.html +5 -5
  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 +26 -26
  118. package/coverage/lcov-report/automations/utils.js.html +35 -35
  119. package/coverage/lcov-report/constants/index.html +5 -5
  120. package/coverage/lcov-report/constants/index.js.html +40 -28
  121. package/coverage/lcov-report/constants/layouts.js.html +5 -5
  122. package/coverage/lcov-report/constants/screens.js.html +4 -4
  123. package/coverage/lcov-report/db/client.js.html +18 -18
  124. package/coverage/lcov-report/db/inMemoryView.js.html +7 -7
  125. package/coverage/lcov-report/db/index.html +15 -15
  126. package/coverage/lcov-report/db/index.js.html +3 -3
  127. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +21 -21
  128. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  129. package/coverage/lcov-report/db/linkedRows/index.js.html +36 -36
  130. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +30 -30
  131. package/coverage/lcov-report/db/newid.js.html +4 -4
  132. package/coverage/lcov-report/db/utils.js.html +123 -87
  133. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  134. package/coverage/lcov-report/events/index.html +1 -1
  135. package/coverage/lcov-report/events/index.js.html +8 -8
  136. package/coverage/lcov-report/events/utils.js.html +18 -18
  137. package/coverage/lcov-report/index.html +89 -74
  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 +5 -5
  141. package/coverage/lcov-report/middleware/authorized.js.html +29 -29
  142. package/coverage/lcov-report/middleware/builder.js.html +16 -16
  143. package/coverage/lcov-report/middleware/currentapp.js.html +44 -44
  144. package/coverage/lcov-report/middleware/index.html +1 -1
  145. package/coverage/lcov-report/middleware/joi-validator.js.html +14 -14
  146. package/coverage/lcov-report/middleware/resourceId.js.html +29 -29
  147. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  148. package/coverage/lcov-report/middleware/usageQuota.js.html +17 -17
  149. package/coverage/lcov-report/middleware/utils.js.html +4 -4
  150. package/coverage/lcov-report/threads/automation.js.html +1 -1
  151. package/coverage/lcov-report/threads/index.html +19 -19
  152. package/coverage/lcov-report/threads/index.js.html +10 -10
  153. package/coverage/lcov-report/threads/query.js.html +123 -39
  154. package/coverage/lcov-report/threads/utils.js.html +14 -14
  155. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  156. package/coverage/lcov-report/utilities/budibaseDir.js.html +7 -7
  157. package/coverage/lcov-report/utilities/centralPath.js.html +4 -4
  158. package/coverage/lcov-report/utilities/csvParser.js.html +6 -6
  159. package/coverage/lcov-report/utilities/global.js.html +28 -28
  160. package/coverage/lcov-report/utilities/index.html +18 -18
  161. package/coverage/lcov-report/utilities/index.js.html +24 -24
  162. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +9 -9
  163. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  164. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  165. package/coverage/lcov-report/utilities/routing/index.js.html +6 -6
  166. package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
  167. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +60 -60
  168. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +13 -13
  169. package/coverage/lcov-report/utilities/scriptRunner.js.html +5 -5
  170. package/coverage/lcov-report/utilities/security.js.html +8 -8
  171. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  172. package/coverage/lcov-report/utilities/usageQuota/index.html +1 -1
  173. package/coverage/lcov-report/utilities/usageQuota/index.js.html +12 -12
  174. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +8 -8
  175. package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +1 -1
  176. package/coverage/lcov-report/utilities/users.js.html +5 -5
  177. package/coverage/lcov-report/utilities/workerRequests.js.html +200 -53
  178. package/coverage/lcov.info +3323 -3224
  179. package/dist/api/controllers/application.js +1 -0
  180. package/dist/api/controllers/public/applications.js +94 -0
  181. package/dist/api/controllers/public/mapping/applications.js +29 -0
  182. package/dist/api/controllers/public/mapping/index.js +11 -0
  183. package/dist/api/controllers/public/mapping/queries.js +36 -0
  184. package/dist/api/controllers/public/mapping/rows.js +24 -0
  185. package/dist/api/controllers/public/mapping/tables.js +23 -0
  186. package/dist/api/controllers/public/mapping/types.js +2 -0
  187. package/dist/api/controllers/public/mapping/users.js +29 -0
  188. package/dist/api/controllers/public/queries.js +38 -0
  189. package/dist/api/controllers/public/rows.js +97 -0
  190. package/dist/api/controllers/public/tables.js +75 -0
  191. package/dist/api/controllers/public/users.js +95 -0
  192. package/dist/api/controllers/public/utils.js +49 -0
  193. package/dist/api/controllers/row/internal.js +4 -1
  194. package/dist/api/controllers/table/index.js +2 -1
  195. package/dist/api/controllers/webhook.js +3 -5
  196. package/dist/api/index.js +3 -1
  197. package/dist/api/routes/automation.js +3 -31
  198. package/dist/api/routes/datasource.js +3 -50
  199. package/dist/api/routes/index.js +2 -0
  200. package/dist/api/routes/permission.js +4 -14
  201. package/dist/api/routes/public/applications.js +141 -0
  202. package/dist/api/routes/public/index.js +68 -0
  203. package/dist/api/routes/public/middleware/mapper.js +94 -0
  204. package/dist/api/routes/public/queries.js +77 -0
  205. package/dist/api/routes/public/rows.js +229 -0
  206. package/dist/api/routes/public/tables.js +154 -0
  207. package/dist/api/routes/public/users.js +137 -0
  208. package/dist/api/routes/public/utils/Endpoint.js +36 -0
  209. package/dist/api/routes/role.js +3 -19
  210. package/dist/api/routes/row.js +3 -4
  211. package/dist/api/routes/screen.js +2 -23
  212. package/dist/api/routes/table.js +3 -17
  213. package/dist/api/routes/tests/utilities/index.js +23 -0
  214. package/dist/api/routes/utils/validators.js +200 -0
  215. package/dist/api/routes/webhook.js +2 -17
  216. package/dist/automations/steps/filter.js +2 -2
  217. package/dist/automations/steps/queryRows.js +1 -1
  218. package/dist/automations/utils.js +2 -2
  219. package/dist/constants/index.js +3 -0
  220. package/dist/db/utils.js +11 -0
  221. package/dist/definitions/openapi.js +6 -0
  222. package/dist/integrations/googlesheets.js +3 -0
  223. package/dist/integrations/microsoftSqlServer.js +35 -29
  224. package/dist/integrations/mysql.js +30 -26
  225. package/dist/integrations/oracle.js +14 -19
  226. package/dist/integrations/postgres.js +34 -28
  227. package/dist/package.json +8 -7
  228. package/dist/tests/utilities/TestConfiguration.js +25 -8
  229. package/dist/threads/query.js +33 -8
  230. package/dist/tsconfig.tsbuildinfo +1 -1
  231. package/dist/utilities/workerRequests.js +50 -15
  232. package/package.json +9 -8
  233. package/scripts/integrations/oracle/oracle.md +2 -2
  234. package/scripts/integrations/postgres/init.sql +5 -3
  235. package/scripts/likeCypress.ts +9 -6
  236. package/specs/generate.js +83 -0
  237. package/specs/openapi.json +2222 -0
  238. package/specs/openapi.yaml +1530 -0
  239. package/specs/parameters.js +59 -0
  240. package/specs/resources/application.js +101 -0
  241. package/specs/resources/index.js +24 -0
  242. package/specs/resources/misc.js +12 -0
  243. package/specs/resources/query.js +189 -0
  244. package/specs/resources/row.js +125 -0
  245. package/specs/resources/table.js +191 -0
  246. package/specs/resources/user.js +126 -0
  247. package/specs/resources/utils/Resource.js +26 -0
  248. package/specs/resources/utils/index.js +11 -0
  249. package/specs/security.js +7 -0
  250. package/src/api/controllers/application.js +1 -0
  251. package/src/api/controllers/public/applications.ts +74 -0
  252. package/src/api/controllers/public/mapping/applications.ts +32 -0
  253. package/src/api/controllers/public/mapping/index.ts +13 -0
  254. package/src/api/controllers/public/mapping/queries.ts +39 -0
  255. package/src/api/controllers/public/mapping/rows.ts +31 -0
  256. package/src/api/controllers/public/mapping/tables.ts +26 -0
  257. package/src/api/controllers/public/mapping/types.ts +13 -0
  258. package/src/api/controllers/public/mapping/users.ts +32 -0
  259. package/src/api/controllers/public/queries.ts +20 -0
  260. package/src/api/controllers/public/rows.ts +77 -0
  261. package/src/api/controllers/public/tables.ts +56 -0
  262. package/src/api/controllers/public/users.ts +82 -0
  263. package/src/api/controllers/public/utils.ts +38 -0
  264. package/src/api/controllers/row/internal.js +4 -0
  265. package/src/api/controllers/table/index.js +2 -1
  266. package/src/api/controllers/webhook.js +3 -6
  267. package/src/api/index.js +4 -1
  268. package/src/api/routes/automation.js +3 -33
  269. package/src/api/routes/datasource.js +6 -52
  270. package/src/api/routes/index.js +2 -0
  271. package/src/api/routes/permission.js +4 -18
  272. package/src/api/routes/public/applications.ts +156 -0
  273. package/src/api/routes/public/index.ts +82 -0
  274. package/src/api/routes/public/middleware/mapper.ts +81 -0
  275. package/src/api/routes/public/queries.ts +81 -0
  276. package/src/api/routes/public/rows.ts +242 -0
  277. package/src/api/routes/public/tables.ts +169 -0
  278. package/src/api/routes/public/tests/compare.spec.js +184 -0
  279. package/src/api/routes/public/users.ts +144 -0
  280. package/src/api/routes/public/utils/Endpoint.ts +51 -0
  281. package/src/api/routes/role.js +3 -24
  282. package/src/api/routes/row.js +3 -2
  283. package/src/api/routes/screen.js +2 -29
  284. package/src/api/routes/table.js +4 -19
  285. package/src/api/routes/tests/utilities/index.js +24 -0
  286. package/src/api/routes/utils/validators.js +219 -0
  287. package/src/api/routes/webhook.js +2 -18
  288. package/src/automations/steps/filter.js +3 -2
  289. package/src/automations/steps/queryRows.js +1 -1
  290. package/src/automations/utils.js +2 -2
  291. package/src/constants/index.js +4 -0
  292. package/src/db/utils.js +12 -0
  293. package/src/definitions/common.ts +4 -0
  294. package/src/definitions/openapi.ts +943 -0
  295. package/src/integrations/base/datasourcePlus.ts +3 -0
  296. package/src/integrations/googlesheets.ts +5 -2
  297. package/src/integrations/microsoftSqlServer.ts +38 -33
  298. package/src/integrations/mysql.ts +38 -36
  299. package/src/integrations/oracle.ts +27 -35
  300. package/src/integrations/postgres.ts +33 -26
  301. package/src/tests/utilities/TestConfiguration.js +23 -2
  302. package/src/threads/query.js +37 -9
  303. package/src/utilities/workerRequests.js +65 -16
  304. package/builder/assets/index.ab059cf2.js +0 -1352
  305. package/scripts/proxy/generateProxyConfig.js +0 -70
@@ -5,5 +5,8 @@ export interface DatasourcePlus extends IntegrationBase {
5
5
  tables: Record<string, Table>
6
6
  schemaErrors: Record<string, string>
7
7
 
8
+ // if the datasource supports the use of bindings directly (to protect against SQL injection)
9
+ // this returns the format of the identifier
10
+ getBindingIdentifier(): string
8
11
  buildSchema(datasourceId: string, entities: Record<string, Table>): any
9
12
  }
@@ -6,11 +6,10 @@ import {
6
6
  } from "../definitions/datasource"
7
7
  import { OAuth2Client } from "google-auth-library"
8
8
  import { DatasourcePlus } from "./base/datasourcePlus"
9
- import { Row, Table, TableSchema } from "../definitions/common"
9
+ import { Table, TableSchema } from "../definitions/common"
10
10
  import { buildExternalTableId } from "./utils"
11
11
  import { DataSourceOperation, FieldTypes } from "../constants"
12
12
  import { GoogleSpreadsheet } from "google-spreadsheet"
13
- import { table } from "console"
14
13
 
15
14
  module GoogleSheetsModule {
16
15
  const { getGlobalDB } = require("@budibase/backend-core/tenancy")
@@ -112,6 +111,10 @@ module GoogleSheetsModule {
112
111
  this.client = new GoogleSpreadsheet(spreadsheetId)
113
112
  }
114
113
 
114
+ getBindingIdentifier() {
115
+ return ""
116
+ }
117
+
115
118
  /**
116
119
  * Pull the spreadsheet ID out from a valid google sheets URL
117
120
  * @param spreadsheetId - the URL or standard spreadsheetId of the google sheet
@@ -79,34 +79,9 @@ module MSSQLModule {
79
79
  },
80
80
  }
81
81
 
82
- async function internalQuery(
83
- client: any,
84
- query: SqlQuery,
85
- operation: string | undefined = undefined
86
- ) {
87
- const request = client.request()
88
- try {
89
- if (Array.isArray(query.bindings)) {
90
- let count = 0
91
- for (let binding of query.bindings) {
92
- request.input(`p${count++}`, binding)
93
- }
94
- }
95
- // this is a hack to get the inserted ID back,
96
- // no way to do this with Knex nicely
97
- const sql =
98
- operation === Operation.CREATE
99
- ? `${query.sql}; SELECT SCOPE_IDENTITY() AS id;`
100
- : query.sql
101
- return await request.query(sql)
102
- } catch (err) {
103
- // @ts-ignore
104
- throw new Error(err)
105
- }
106
- }
107
-
108
82
  class SqlServerIntegration extends Sql implements DatasourcePlus {
109
83
  private readonly config: MSSQLConfig
84
+ private index: number = 0
110
85
  static pool: any
111
86
  public tables: Record<string, Table> = {}
112
87
  public schemaErrors: Record<string, string> = {}
@@ -121,6 +96,33 @@ module MSSQLModule {
121
96
  TABLES_SQL =
122
97
  "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'"
123
98
 
99
+ async internalQuery(
100
+ query: SqlQuery,
101
+ operation: string | undefined = undefined
102
+ ) {
103
+ const client = this.client
104
+ const request = client.request()
105
+ this.index = 0
106
+ try {
107
+ if (Array.isArray(query.bindings)) {
108
+ let count = 0
109
+ for (let binding of query.bindings) {
110
+ request.input(`p${count++}`, binding)
111
+ }
112
+ }
113
+ // this is a hack to get the inserted ID back,
114
+ // no way to do this with Knex nicely
115
+ const sql =
116
+ operation === Operation.CREATE
117
+ ? `${query.sql}; SELECT SCOPE_IDENTITY() AS id;`
118
+ : query.sql
119
+ return await request.query(sql)
120
+ } catch (err) {
121
+ // @ts-ignore
122
+ throw new Error(err)
123
+ }
124
+ }
125
+
124
126
  getDefinitionSQL(tableName: string) {
125
127
  return `select *
126
128
  from INFORMATION_SCHEMA.COLUMNS
@@ -165,6 +167,10 @@ module MSSQLModule {
165
167
  }
166
168
  }
167
169
 
170
+ getBindingIdentifier(): string {
171
+ return `(@p${this.index++})`
172
+ }
173
+
168
174
  async connect() {
169
175
  try {
170
176
  this.client = await this.pool.connect()
@@ -175,7 +181,7 @@ module MSSQLModule {
175
181
  }
176
182
 
177
183
  async runSQL(sql: string) {
178
- return (await internalQuery(this.client, getSqlQuery(sql))).recordset
184
+ return (await this.internalQuery(getSqlQuery(sql))).recordset
179
185
  }
180
186
 
181
187
  /**
@@ -238,33 +244,32 @@ module MSSQLModule {
238
244
 
239
245
  async read(query: SqlQuery | string) {
240
246
  await this.connect()
241
- const response = await internalQuery(this.client, getSqlQuery(query))
247
+ const response = await this.internalQuery(getSqlQuery(query))
242
248
  return response.recordset
243
249
  }
244
250
 
245
251
  async create(query: SqlQuery | string) {
246
252
  await this.connect()
247
- const response = await internalQuery(this.client, getSqlQuery(query))
253
+ const response = await this.internalQuery(getSqlQuery(query))
248
254
  return response.recordset || [{ created: true }]
249
255
  }
250
256
 
251
257
  async update(query: SqlQuery | string) {
252
258
  await this.connect()
253
- const response = await internalQuery(this.client, getSqlQuery(query))
259
+ const response = await this.internalQuery(getSqlQuery(query))
254
260
  return response.recordset || [{ updated: true }]
255
261
  }
256
262
 
257
263
  async delete(query: SqlQuery | string) {
258
264
  await this.connect()
259
- const response = await internalQuery(this.client, getSqlQuery(query))
265
+ const response = await this.internalQuery(getSqlQuery(query))
260
266
  return response.recordset || [{ deleted: true }]
261
267
  }
262
268
 
263
269
  async query(json: QueryJson) {
264
270
  await this.connect()
265
271
  const operation = this._operation(json)
266
- const queryFn = (query: any, op: string) =>
267
- internalQuery(this.client, query, op)
272
+ const queryFn = (query: any, op: string) => this.internalQuery(query, op)
268
273
  const processFn = (result: any) =>
269
274
  result.recordset ? result.recordset : [{ [operation]: true }]
270
275
  return this.queryWithReturning(json, queryFn, processFn)
@@ -80,33 +80,6 @@ module MySQLModule {
80
80
  },
81
81
  }
82
82
 
83
- function internalQuery(
84
- client: any,
85
- query: SqlQuery,
86
- connect: boolean = true
87
- ): Promise<any[] | any> {
88
- // Node MySQL is callback based, so we must wrap our call in a promise
89
- return new Promise((resolve, reject) => {
90
- if (connect) {
91
- client.connect()
92
- }
93
- return client.query(
94
- query.sql,
95
- query.bindings || {},
96
- (error: any, results: object[]) => {
97
- if (error) {
98
- reject(error)
99
- } else {
100
- resolve(results)
101
- }
102
- if (connect) {
103
- client.end()
104
- }
105
- }
106
- )
107
- })
108
- }
109
-
110
83
  class MySQLIntegration extends Sql implements DatasourcePlus {
111
84
  private config: MySQLConfig
112
85
  private readonly client: any
@@ -122,14 +95,44 @@ module MySQLModule {
122
95
  this.client = mysql.createConnection(config)
123
96
  }
124
97
 
98
+ getBindingIdentifier(): string {
99
+ return "?"
100
+ }
101
+
102
+ internalQuery(
103
+ query: SqlQuery,
104
+ connect: boolean = true
105
+ ): Promise<any[] | any> {
106
+ const client = this.client
107
+ // Node MySQL is callback based, so we must wrap our call in a promise
108
+ return new Promise((resolve, reject) => {
109
+ if (connect) {
110
+ client.connect()
111
+ }
112
+ return client.query(
113
+ query.sql,
114
+ query.bindings || {},
115
+ (error: any, results: object[]) => {
116
+ if (error) {
117
+ reject(error)
118
+ } else {
119
+ resolve(results)
120
+ }
121
+ if (connect) {
122
+ client.end()
123
+ }
124
+ }
125
+ )
126
+ })
127
+ }
128
+
125
129
  async buildSchema(datasourceId: string, entities: Record<string, Table>) {
126
130
  const tables: { [key: string]: Table } = {}
127
131
  const database = this.config.database
128
132
  this.client.connect()
129
133
 
130
134
  // get the tables first
131
- const tablesResp = await internalQuery(
132
- this.client,
135
+ const tablesResp = await this.internalQuery(
133
136
  { sql: "SHOW TABLES;" },
134
137
  false
135
138
  )
@@ -141,8 +144,7 @@ module MySQLModule {
141
144
  for (let tableName of tableNames) {
142
145
  const primaryKeys = []
143
146
  const schema: TableSchema = {}
144
- const descResp = await internalQuery(
145
- this.client,
147
+ const descResp = await this.internalQuery(
146
148
  { sql: `DESCRIBE \`${tableName}\`;` },
147
149
  false
148
150
  )
@@ -182,27 +184,27 @@ module MySQLModule {
182
184
  }
183
185
 
184
186
  async create(query: SqlQuery | string) {
185
- const results = await internalQuery(this.client, getSqlQuery(query))
187
+ const results = await this.internalQuery(getSqlQuery(query))
186
188
  return results.length ? results : [{ created: true }]
187
189
  }
188
190
 
189
191
  async read(query: SqlQuery | string) {
190
- return internalQuery(this.client, getSqlQuery(query))
192
+ return this.internalQuery(getSqlQuery(query))
191
193
  }
192
194
 
193
195
  async update(query: SqlQuery | string) {
194
- const results = await internalQuery(this.client, getSqlQuery(query))
196
+ const results = await this.internalQuery(getSqlQuery(query))
195
197
  return results.length ? results : [{ updated: true }]
196
198
  }
197
199
 
198
200
  async delete(query: SqlQuery | string) {
199
- const results = await internalQuery(this.client, getSqlQuery(query))
201
+ const results = await this.internalQuery(getSqlQuery(query))
200
202
  return results.length ? results : [{ deleted: true }]
201
203
  }
202
204
 
203
205
  async query(json: QueryJson) {
204
206
  this.client.connect()
205
- const queryFn = (query: any) => internalQuery(this.client, query, false)
207
+ const queryFn = (query: any) => this.internalQuery(query, false)
206
208
  const output = await this.queryWithReturning(json, queryFn)
207
209
  this.client.end()
208
210
  return output
@@ -1,24 +1,24 @@
1
1
  import {
2
- Integration,
3
2
  DatasourceFieldTypes,
3
+ Integration,
4
+ Operation,
5
+ QueryJson,
4
6
  QueryTypes,
5
7
  SqlQuery,
6
- QueryJson,
7
- Operation,
8
8
  } from "../definitions/datasource"
9
9
  import {
10
- finaliseExternalTables,
11
- getSqlQuery,
12
10
  buildExternalTableId,
13
11
  convertSqlType,
12
+ finaliseExternalTables,
13
+ getSqlQuery,
14
14
  SqlClients,
15
15
  } from "./utils"
16
16
  import oracledb, {
17
- ExecuteOptions,
18
- Result,
17
+ BindParameters,
19
18
  Connection,
20
19
  ConnectionAttributes,
21
- BindParameters,
20
+ ExecuteOptions,
21
+ Result,
22
22
  } from "oracledb"
23
23
  import Sql from "./base/sql"
24
24
  import { Table } from "../definitions/common"
@@ -137,6 +137,7 @@ module OracleModule {
137
137
 
138
138
  class OracleIntegration extends Sql implements DatasourcePlus {
139
139
  private readonly config: OracleConfig
140
+ private index: number = 1
140
141
 
141
142
  public tables: Record<string, Table> = {}
142
143
  public schemaErrors: Record<string, string> = {}
@@ -174,6 +175,10 @@ module OracleModule {
174
175
  this.config = config
175
176
  }
176
177
 
178
+ getBindingIdentifier(): string {
179
+ return `:${this.index++}`
180
+ }
181
+
177
182
  /**
178
183
  * Map the flat tabular columns and constraints data into a nested object
179
184
  */
@@ -233,20 +238,14 @@ module OracleModule {
233
238
  return oracleTables
234
239
  }
235
240
 
236
- private isSupportedColumn(column: OracleColumn) {
237
- if (UNSUPPORTED_TYPES.includes(column.type)) {
238
- return false
239
- }
240
-
241
- return true
241
+ private static isSupportedColumn(column: OracleColumn) {
242
+ return !UNSUPPORTED_TYPES.includes(column.type)
242
243
  }
243
244
 
244
- private isAutoColumn(column: OracleColumn) {
245
- if (column.default && column.default.toLowerCase().includes("nextval")) {
246
- return true
247
- }
248
-
249
- return false
245
+ private static isAutoColumn(column: OracleColumn) {
246
+ return !!(
247
+ column.default && column.default.toLowerCase().includes("nextval")
248
+ )
250
249
  }
251
250
 
252
251
  /**
@@ -254,7 +253,7 @@ module OracleModule {
254
253
  * This matches the default behaviour for generating DDL used in knex.
255
254
  */
256
255
  private isBooleanType(column: OracleColumn): boolean {
257
- if (
256
+ return (
258
257
  column.type.toLowerCase() === "number" &&
259
258
  Object.values(column.constraints).filter(c => {
260
259
  if (
@@ -273,11 +272,7 @@ module OracleModule {
273
272
  }
274
273
  return false
275
274
  }).length > 0
276
- ) {
277
- return true
278
- }
279
-
280
- return false
275
+ )
281
276
  }
282
277
 
283
278
  private internalConvertType(column: OracleColumn): string {
@@ -317,7 +312,9 @@ module OracleModule {
317
312
  // iterate each column on the table
318
313
  Object.values(oracleTable.columns)
319
314
  // remove columns that we can't read / save
320
- .filter(oracleColumn => this.isSupportedColumn(oracleColumn))
315
+ .filter(oracleColumn =>
316
+ OracleIntegration.isSupportedColumn(oracleColumn)
317
+ )
321
318
  // match the order of the columns in the db
322
319
  .sort((c1, c2) => c1.id - c2.id)
323
320
  .forEach(oracleColumn => {
@@ -325,7 +322,7 @@ module OracleModule {
325
322
  let fieldSchema = table.schema[columnName]
326
323
  if (!fieldSchema) {
327
324
  fieldSchema = {
328
- autocolumn: this.isAutoColumn(oracleColumn),
325
+ autocolumn: OracleIntegration.isAutoColumn(oracleColumn),
329
326
  name: columnName,
330
327
  type: this.internalConvertType(oracleColumn),
331
328
  }
@@ -351,18 +348,13 @@ module OracleModule {
351
348
  private async internalQuery<T>(query: SqlQuery): Promise<Result<T>> {
352
349
  let connection
353
350
  try {
351
+ this.index = 1
354
352
  connection = await this.getConnection()
355
353
 
356
354
  const options: ExecuteOptions = { autoCommit: true }
357
355
  const bindings: BindParameters = query.bindings || []
358
356
 
359
- const result: Result<T> = await connection.execute<T>(
360
- query.sql,
361
- bindings,
362
- options
363
- )
364
-
365
- return result
357
+ return await connection.execute<T>(query.sql, bindings, options)
366
358
  } finally {
367
359
  if (connection) {
368
360
  try {
@@ -103,30 +103,11 @@ module PostgresModule {
103
103
  },
104
104
  }
105
105
 
106
- async function internalQuery(client: any, query: SqlQuery) {
107
- // need to handle a specific issue with json data types in postgres,
108
- // new lines inside the JSON data will break it
109
- if (query && query.sql) {
110
- const matches = query.sql.match(JSON_REGEX)
111
- if (matches && matches.length > 0) {
112
- for (let match of matches) {
113
- const escaped = escapeDangerousCharacters(match)
114
- query.sql = query.sql.replace(match, escaped)
115
- }
116
- }
117
- }
118
- try {
119
- return await client.query(query.sql, query.bindings || [])
120
- } catch (err) {
121
- // @ts-ignore
122
- throw new Error(err)
123
- }
124
- }
125
-
126
106
  class PostgresIntegration extends Sql implements DatasourcePlus {
127
107
  static pool: any
128
108
  private readonly client: any
129
109
  private readonly config: PostgresConfig
110
+ private index: number = 1
130
111
  public tables: Record<string, Table> = {}
131
112
  public schemaErrors: Record<string, string> = {}
132
113
 
@@ -163,6 +144,32 @@ module PostgresModule {
163
144
  this.setSchema()
164
145
  }
165
146
 
147
+ getBindingIdentifier(): string {
148
+ return `$${this.index++}`
149
+ }
150
+
151
+ async internalQuery(query: SqlQuery) {
152
+ const client = this.client
153
+ this.index = 1
154
+ // need to handle a specific issue with json data types in postgres,
155
+ // new lines inside the JSON data will break it
156
+ if (query && query.sql) {
157
+ const matches = query.sql.match(JSON_REGEX)
158
+ if (matches && matches.length > 0) {
159
+ for (let match of matches) {
160
+ const escaped = escapeDangerousCharacters(match)
161
+ query.sql = query.sql.replace(match, escaped)
162
+ }
163
+ }
164
+ }
165
+ try {
166
+ return await client.query(query.sql, query.bindings || [])
167
+ } catch (err) {
168
+ // @ts-ignore
169
+ throw new Error(err)
170
+ }
171
+ }
172
+
166
173
  setSchema() {
167
174
  if (!this.config.schema) {
168
175
  this.config.schema = "public"
@@ -241,22 +248,22 @@ module PostgresModule {
241
248
  }
242
249
 
243
250
  async create(query: SqlQuery | string) {
244
- const response = await internalQuery(this.client, getSqlQuery(query))
251
+ const response = await this.internalQuery(getSqlQuery(query))
245
252
  return response.rows.length ? response.rows : [{ created: true }]
246
253
  }
247
254
 
248
255
  async read(query: SqlQuery | string) {
249
- const response = await internalQuery(this.client, getSqlQuery(query))
256
+ const response = await this.internalQuery(getSqlQuery(query))
250
257
  return response.rows
251
258
  }
252
259
 
253
260
  async update(query: SqlQuery | string) {
254
- const response = await internalQuery(this.client, getSqlQuery(query))
261
+ const response = await this.internalQuery(getSqlQuery(query))
255
262
  return response.rows.length ? response.rows : [{ updated: true }]
256
263
  }
257
264
 
258
265
  async delete(query: SqlQuery | string) {
259
- const response = await internalQuery(this.client, getSqlQuery(query))
266
+ const response = await this.internalQuery(getSqlQuery(query))
260
267
  return response.rows.length ? response.rows : [{ deleted: true }]
261
268
  }
262
269
 
@@ -266,11 +273,11 @@ module PostgresModule {
266
273
  if (Array.isArray(input)) {
267
274
  const responses = []
268
275
  for (let query of input) {
269
- responses.push(await internalQuery(this.client, query))
276
+ responses.push(await this.internalQuery(query))
270
277
  }
271
278
  return responses
272
279
  } else {
273
- const response = await internalQuery(this.client, input)
280
+ const response = await this.internalQuery(input)
274
281
  return response.rows.length ? response.rows : [{ [operation]: true }]
275
282
  }
276
283
  }
@@ -25,6 +25,8 @@ const { createASession } = require("@budibase/backend-core/sessions")
25
25
  const { user: userCache } = require("@budibase/backend-core/cache")
26
26
  const newid = require("../../db/newid")
27
27
  const context = require("@budibase/backend-core/context")
28
+ const { generateDevInfoID, SEPARATOR } = require("@budibase/backend-core/db")
29
+ const { encrypt } = require("@budibase/backend-core/encryption")
28
30
 
29
31
  const GLOBAL_USER_ID = "us_uuid1"
30
32
  const EMAIL = "babs@babs.com"
@@ -47,6 +49,10 @@ class TestConfiguration {
47
49
  return this.request
48
50
  }
49
51
 
52
+ getApp() {
53
+ return this.app
54
+ }
55
+
50
56
  getAppId() {
51
57
  return this.appId
52
58
  }
@@ -83,6 +89,20 @@ class TestConfiguration {
83
89
  }
84
90
  }
85
91
 
92
+ async generateApiKey(userId = GLOBAL_USER_ID) {
93
+ const db = getGlobalDB(TENANT_ID)
94
+ const id = generateDevInfoID(userId)
95
+ let devInfo
96
+ try {
97
+ devInfo = await db.get(id)
98
+ } catch (err) {
99
+ devInfo = { _id: id, userId }
100
+ }
101
+ devInfo.apiKey = encrypt(`${TENANT_ID}${SEPARATOR}${newid()}`)
102
+ await db.put(devInfo)
103
+ return devInfo.apiKey
104
+ }
105
+
86
106
  async globalUser({
87
107
  id = GLOBAL_USER_ID,
88
108
  builder = true,
@@ -135,7 +155,7 @@ class TestConfiguration {
135
155
  cleanup(this.allApps.map(app => app.appId))
136
156
  }
137
157
 
138
- defaultHeaders() {
158
+ defaultHeaders(extras = {}) {
139
159
  const auth = {
140
160
  userId: GLOBAL_USER_ID,
141
161
  sessionId: "sessionid",
@@ -154,6 +174,7 @@ class TestConfiguration {
154
174
  `${Cookies.CurrentApp}=${appToken}`,
155
175
  ],
156
176
  [Headers.CSRF_TOKEN]: CSRF_TOKEN,
177
+ ...extras,
157
178
  }
158
179
  if (this.appId) {
159
180
  headers[Headers.APP_ID] = this.appId
@@ -226,7 +247,7 @@ class TestConfiguration {
226
247
 
227
248
  async getTable(tableId = null) {
228
249
  tableId = tableId || this.table._id
229
- return this._req(null, { id: tableId }, controllers.table.find)
250
+ return this._req(null, { tableId }, controllers.table.find)
230
251
  }
231
252
 
232
253
  async createLinkedTable(relationshipType = null, links = ["link"]) {
@@ -2,8 +2,12 @@ const threadUtils = require("./utils")
2
2
  threadUtils.threadSetup()
3
3
  const ScriptRunner = require("../utilities/scriptRunner")
4
4
  const { integrations } = require("../integrations")
5
- const { processStringSync } = require("@budibase/string-templates")
5
+ const {
6
+ processStringSync,
7
+ findHBSBlocks,
8
+ } = require("@budibase/string-templates")
6
9
  const { doInAppContext, getAppDB } = require("@budibase/backend-core/context")
10
+ const { isSQL } = require("../integrations/utils")
7
11
 
8
12
  class QueryRunner {
9
13
  constructor(input, flags = { noRecursiveQuery: false }) {
@@ -23,23 +27,47 @@ class QueryRunner {
23
27
  this.hasRerun = false
24
28
  }
25
29
 
30
+ interpolateSQL(fields, parameters, integration) {
31
+ let sql = fields.sql
32
+ if (!sql) {
33
+ return fields
34
+ }
35
+ const bindings = findHBSBlocks(sql)
36
+ let variables = []
37
+ for (let binding of bindings) {
38
+ let variable = integration.getBindingIdentifier()
39
+ variables.push(binding)
40
+ sql = sql.replace(binding, variable)
41
+ }
42
+ // replicate the knex structure
43
+ fields.sql = sql
44
+ fields.bindings = this.enrichQueryFields(variables, parameters)
45
+ return fields
46
+ }
47
+
26
48
  async execute() {
27
49
  let { datasource, fields, queryVerb, transformer } = this
50
+ const Integration = integrations[datasource.source]
51
+ if (!Integration) {
52
+ throw "Integration type does not exist."
53
+ }
54
+ const integration = new Integration(datasource.config)
55
+
28
56
  // pre-query, make sure datasource variables are added to parameters
29
57
  const parameters = await this.addDatasourceVariables()
30
- let query = this.enrichQueryFields(fields, parameters)
58
+ let query
59
+ // handle SQL injections by interpolating the variables
60
+ if (isSQL(datasource)) {
61
+ query = this.interpolateSQL(fields, parameters, integration)
62
+ } else {
63
+ query = this.enrichQueryFields(fields, parameters)
64
+ }
31
65
 
32
66
  // Add pagination values for REST queries
33
67
  if (this.pagination) {
34
68
  query.paginationValues = this.pagination
35
69
  }
36
70
 
37
- const Integration = integrations[datasource.source]
38
- if (!Integration) {
39
- throw "Integration type does not exist."
40
- }
41
- const integration = new Integration(datasource.config)
42
-
43
71
  let output = threadUtils.formatResponse(await integration[queryVerb](query))
44
72
  let rows = output,
45
73
  info = undefined,
@@ -179,7 +207,7 @@ class QueryRunner {
179
207
  }
180
208
 
181
209
  enrichQueryFields(fields, parameters = {}) {
182
- const enrichedQuery = {}
210
+ const enrichedQuery = Array.isArray(fields) ? [] : {}
183
211
 
184
212
  // enrich the fields with dynamic parameters
185
213
  for (let key of Object.keys(fields)) {