@budibase/server 0.9.184 → 0.9.185-alpha.11

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 (290) hide show
  1. package/Dockerfile +1 -1
  2. package/__mocks__/mssql.ts +1 -1
  3. package/__mocks__/pg.ts +3 -0
  4. package/builder/assets/bb-space-man.5be51a45.svg +290 -0
  5. package/builder/assets/index.053c6f7a.js +1321 -0
  6. package/builder/assets/index.4a7c5636.css +1 -0
  7. package/builder/assets/vendor.470042a4.js +152 -0
  8. package/builder/assets/vendor.5e1fe644.css +1 -0
  9. package/builder/index.html +4 -4
  10. package/coverage/clover.xml +1357 -959
  11. package/coverage/coverage-final.json +91 -87
  12. package/coverage/lcov-report/api/controllers/analytics.js.html +2 -2
  13. package/coverage/lcov-report/api/controllers/apikeys.js.html +2 -2
  14. package/coverage/lcov-report/api/controllers/application.js.html +65 -14
  15. package/coverage/lcov-report/api/controllers/auth.js.html +19 -10
  16. package/coverage/lcov-report/api/controllers/automation.js.html +3 -3
  17. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  18. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/component.js.html +4 -4
  20. package/coverage/lcov-report/api/controllers/datasource.js.html +147 -27
  21. package/coverage/lcov-report/api/controllers/dev.js.html +16 -7
  22. package/coverage/lcov-report/api/controllers/hosting.js.html +10 -10
  23. package/coverage/lcov-report/api/controllers/index.html +90 -90
  24. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  25. package/coverage/lcov-report/api/controllers/layout.js.html +3 -3
  26. package/coverage/lcov-report/api/controllers/metadata.js.html +3 -3
  27. package/coverage/lcov-report/api/controllers/permission.js.html +3 -3
  28. package/coverage/lcov-report/api/controllers/query.js.html +53 -209
  29. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  30. package/coverage/lcov-report/api/controllers/routing.js.html +3 -3
  31. package/coverage/lcov-report/api/controllers/row/external.js.html +12 -6
  32. package/coverage/lcov-report/api/controllers/row/index.html +26 -26
  33. package/coverage/lcov-report/api/controllers/row/index.js.html +2 -2
  34. package/coverage/lcov-report/api/controllers/row/internal.js.html +9 -9
  35. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +2 -2
  36. package/coverage/lcov-report/api/controllers/row/utils.js.html +21 -30
  37. package/coverage/lcov-report/api/controllers/screen.js.html +2 -2
  38. package/coverage/lcov-report/api/controllers/script.js.html +3 -3
  39. package/coverage/lcov-report/api/controllers/table/external.js.html +980 -0
  40. package/coverage/lcov-report/api/controllers/table/index.html +56 -26
  41. package/coverage/lcov-report/api/controllers/table/index.js.html +80 -350
  42. package/coverage/lcov-report/api/controllers/table/internal.js.html +545 -0
  43. package/coverage/lcov-report/api/controllers/table/utils.js.html +172 -49
  44. package/coverage/lcov-report/api/controllers/templates.js.html +4 -4
  45. package/coverage/lcov-report/api/controllers/user.js.html +390 -33
  46. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  47. package/coverage/lcov-report/api/controllers/view/index.html +9 -9
  48. package/coverage/lcov-report/api/controllers/view/index.js.html +5 -5
  49. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  50. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +3 -3
  51. package/coverage/lcov-report/api/controllers/webhook.js.html +4 -4
  52. package/coverage/lcov-report/api/index.html +1 -1
  53. package/coverage/lcov-report/api/index.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/dev.js.html +2 -2
  65. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/index.html +4 -4
  67. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/query.js.html +5 -2
  73. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/static.js.html +2 -2
  79. package/coverage/lcov-report/api/routes/table.js.html +35 -2
  80. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  81. package/coverage/lcov-report/api/routes/user.js.html +17 -2
  82. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  83. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  84. package/coverage/lcov-report/automations/actions.js.html +2 -2
  85. package/coverage/lcov-report/automations/automationUtils.js.html +3 -3
  86. package/coverage/lcov-report/automations/bullboard.js.html +71 -20
  87. package/coverage/lcov-report/automations/index.html +33 -48
  88. package/coverage/lcov-report/automations/index.js.html +2 -2
  89. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/discord.js.html +3 -3
  94. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/executeScript.js.html +3 -3
  96. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/index.html +14 -14
  98. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  99. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +3 -3
  100. package/coverage/lcov-report/automations/steps/queryRows.js.html +3 -3
  101. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  102. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  103. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  104. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  105. package/coverage/lcov-report/automations/steps/utils.js.html +7 -7
  106. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  107. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  108. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  109. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  110. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  111. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  112. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  113. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  114. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  115. package/coverage/lcov-report/automations/triggers.js.html +22 -10
  116. package/coverage/lcov-report/automations/utils.js.html +42 -12
  117. package/coverage/lcov-report/constants/index.html +5 -5
  118. package/coverage/lcov-report/constants/index.js.html +49 -4
  119. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  120. package/coverage/lcov-report/constants/screens.js.html +1 -1
  121. package/coverage/lcov-report/db/client.js.html +2 -2
  122. package/coverage/lcov-report/db/inMemoryView.js.html +2 -2
  123. package/coverage/lcov-report/db/index.html +2 -2
  124. package/coverage/lcov-report/db/index.js.html +1 -1
  125. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +6 -6
  126. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  127. package/coverage/lcov-report/db/linkedRows/index.js.html +5 -5
  128. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +6 -6
  129. package/coverage/lcov-report/db/newid.js.html +1 -1
  130. package/coverage/lcov-report/db/utils.js.html +1 -1
  131. package/coverage/lcov-report/events/AutomationEmitter.js.html +6 -6
  132. package/coverage/lcov-report/events/index.html +6 -6
  133. package/coverage/lcov-report/events/index.js.html +1 -1
  134. package/coverage/lcov-report/events/utils.js.html +5 -5
  135. package/coverage/lcov-report/index.html +98 -83
  136. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  137. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  138. package/coverage/lcov-report/middleware/appInfo.js.html +2 -2
  139. package/coverage/lcov-report/middleware/authorized.js.html +11 -8
  140. package/coverage/lcov-report/middleware/builder.js.html +16 -7
  141. package/coverage/lcov-report/middleware/currentapp.js.html +6 -6
  142. package/coverage/lcov-report/middleware/index.html +23 -23
  143. package/coverage/lcov-report/middleware/joi-validator.js.html +2 -2
  144. package/coverage/lcov-report/middleware/resourceId.js.html +18 -18
  145. package/coverage/lcov-report/middleware/selfhost.js.html +2 -2
  146. package/coverage/lcov-report/middleware/usageQuota.js.html +4 -4
  147. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  148. package/coverage/lcov-report/{automations/thread.js.html → threads/automation.js.html} +83 -32
  149. package/coverage/lcov-report/threads/index.html +141 -0
  150. package/coverage/lcov-report/threads/index.js.html +260 -0
  151. package/coverage/lcov-report/threads/query.js.html +269 -0
  152. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  153. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  154. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  155. package/coverage/lcov-report/utilities/csvParser.js.html +144 -15
  156. package/coverage/lcov-report/utilities/global.js.html +26 -11
  157. package/coverage/lcov-report/utilities/index.html +49 -49
  158. package/coverage/lcov-report/utilities/index.js.html +65 -17
  159. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +30 -9
  160. package/coverage/lcov-report/utilities/queue/index.html +14 -14
  161. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  162. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  163. package/coverage/lcov-report/utilities/rowProcessor/index.html +4 -4
  164. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +2 -2
  165. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +3 -3
  166. package/coverage/lcov-report/utilities/scriptRunner.js.html +16 -7
  167. package/coverage/lcov-report/utilities/security.js.html +3 -3
  168. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  169. package/coverage/lcov-report/utilities/users.js.html +1 -1
  170. package/coverage/lcov-report/utilities/workerRequests.js.html +5 -5
  171. package/coverage/lcov.info +2301 -1599
  172. package/dist/api/controllers/application.js +16 -2
  173. package/dist/api/controllers/auth.js +3 -3
  174. package/dist/api/controllers/datasource.js +47 -16
  175. package/dist/api/controllers/deploy/index.js +2 -0
  176. package/dist/api/controllers/dev.js +4 -1
  177. package/dist/api/controllers/query.js +30 -58
  178. package/dist/api/controllers/row/ExternalRequest.js +37 -14
  179. package/dist/api/controllers/row/external.js +1 -0
  180. package/dist/api/controllers/row/utils.js +6 -12
  181. package/dist/api/controllers/static/index.js +1 -1
  182. package/dist/api/controllers/table/external.js +242 -0
  183. package/dist/api/controllers/table/index.js +49 -123
  184. package/dist/api/controllers/table/internal.js +150 -0
  185. package/dist/api/controllers/table/utils.js +62 -28
  186. package/dist/api/controllers/user.js +123 -11
  187. package/dist/api/routes/query.js +1 -1
  188. package/dist/api/routes/table.js +7 -1
  189. package/dist/api/routes/user.js +2 -1
  190. package/dist/app.js +2 -11
  191. package/dist/automations/bullboard.js +37 -10
  192. package/dist/automations/triggers.js +7 -3
  193. package/dist/automations/utils.js +11 -2
  194. package/dist/constants/index.js +13 -0
  195. package/dist/definitions/datasource.js +4 -0
  196. package/dist/integrations/base/IntegrationBase.js +2 -0
  197. package/dist/integrations/base/datasourcePlus.js +2 -0
  198. package/dist/integrations/base/sql.js +334 -180
  199. package/dist/integrations/base/sqlTable.js +143 -0
  200. package/dist/integrations/base/utils.js +30 -0
  201. package/dist/integrations/microsoftSqlServer.js +117 -9
  202. package/dist/integrations/mysql.js +14 -84
  203. package/dist/integrations/postgres.js +35 -28
  204. package/dist/integrations/utils.js +56 -9
  205. package/dist/middleware/authorized.js +2 -1
  206. package/dist/middleware/builder.js +4 -1
  207. package/dist/package.json +7 -6
  208. package/dist/tests/utilities/TestConfiguration.js +2 -1
  209. package/dist/{automations/thread.js → threads/automation.js} +23 -7
  210. package/dist/threads/index.js +58 -0
  211. package/dist/threads/query.js +61 -0
  212. package/dist/tsconfig.tsbuildinfo +1 -1
  213. package/dist/utilities/csvParser.js +46 -5
  214. package/dist/utilities/global.js +6 -2
  215. package/dist/utilities/index.js +18 -2
  216. package/dist/utilities/queue/inMemoryQueue.js +8 -0
  217. package/dist/utilities/scriptRunner.js +4 -1
  218. package/package.json +8 -7
  219. package/scripts/integrations/mssql/data/Dockerfile +9 -0
  220. package/scripts/integrations/mssql/data/entrypoint.sh +24 -0
  221. package/scripts/integrations/mssql/data/setup.sql +54 -0
  222. package/scripts/integrations/mssql/docker-compose.yaml +12 -0
  223. package/scripts/integrations/mssql/reset.sh +3 -0
  224. package/scripts/likeCypress.ts +33 -0
  225. package/src/api/controllers/application.js +19 -2
  226. package/src/api/controllers/auth.js +3 -0
  227. package/src/api/controllers/datasource.js +56 -16
  228. package/src/api/controllers/deploy/index.js +2 -0
  229. package/src/api/controllers/dev.js +4 -1
  230. package/src/api/controllers/query.js +29 -81
  231. package/src/api/controllers/row/ExternalRequest.ts +47 -16
  232. package/src/api/controllers/row/external.js +2 -0
  233. package/src/api/controllers/row/utils.js +8 -11
  234. package/src/api/controllers/static/index.js +1 -1
  235. package/src/api/controllers/table/external.js +300 -0
  236. package/src/api/controllers/table/index.js +47 -137
  237. package/src/api/controllers/table/internal.js +155 -0
  238. package/src/api/controllers/table/utils.js +74 -33
  239. package/src/api/controllers/user.js +132 -13
  240. package/src/api/routes/query.js +1 -0
  241. package/src/api/routes/table.js +11 -0
  242. package/src/api/routes/tests/datasource.spec.js +2 -2
  243. package/src/api/routes/tests/misc.spec.js +5 -1
  244. package/src/api/routes/tests/query.spec.js +0 -1
  245. package/src/api/routes/user.js +5 -0
  246. package/src/app.ts +2 -10
  247. package/src/automations/bullboard.js +28 -11
  248. package/src/automations/tests/automation.spec.js +3 -4
  249. package/src/automations/triggers.js +7 -3
  250. package/src/automations/utils.js +12 -2
  251. package/src/constants/index.js +15 -0
  252. package/src/definitions/common.ts +7 -1
  253. package/src/definitions/datasource.ts +9 -3
  254. package/src/integrations/airtable.ts +2 -1
  255. package/src/integrations/arangodb.ts +2 -1
  256. package/src/integrations/base/IntegrationBase.ts +6 -0
  257. package/src/integrations/base/datasourcePlus.ts +9 -0
  258. package/src/integrations/base/sql.ts +359 -222
  259. package/src/integrations/base/sqlTable.ts +180 -0
  260. package/src/integrations/base/utils.ts +22 -0
  261. package/src/integrations/couchdb.ts +2 -1
  262. package/src/integrations/dynamodb.ts +2 -1
  263. package/src/integrations/elasticsearch.ts +2 -1
  264. package/src/integrations/microsoftSqlServer.ts +145 -13
  265. package/src/integrations/mongodb.ts +2 -1
  266. package/src/integrations/mysql.ts +20 -94
  267. package/src/integrations/postgres.ts +45 -35
  268. package/src/integrations/rest.ts +2 -1
  269. package/src/integrations/s3.ts +2 -1
  270. package/src/integrations/tests/microsoftSqlServer.spec.js +22 -15
  271. package/src/integrations/tests/postgres.spec.js +4 -0
  272. package/src/integrations/utils.ts +60 -8
  273. package/src/middleware/authorized.js +2 -1
  274. package/src/middleware/builder.js +4 -1
  275. package/src/tests/utilities/TestConfiguration.js +2 -1
  276. package/src/{automations/thread.js → threads/automation.js} +24 -7
  277. package/src/threads/index.js +60 -0
  278. package/src/threads/query.js +63 -0
  279. package/src/utilities/csvParser.js +48 -5
  280. package/src/utilities/global.js +7 -2
  281. package/src/utilities/index.js +18 -2
  282. package/src/utilities/queue/inMemoryQueue.js +7 -0
  283. package/src/utilities/scriptRunner.js +4 -1
  284. package/src/utilities/tests/__snapshots__/csvParser.spec.js.snap +0 -6
  285. package/src/utilities/tests/csvParser.spec.js +3 -0
  286. package/tsconfig.json +1 -1
  287. package/builder/assets/index.34964222.js +0 -201
  288. package/builder/assets/index.7469e1e4.css +0 -29
  289. package/builder/assets/vendor.278a305c.js +0 -9
  290. package/builder/assets/vendor.d75d5eb7.css +0 -1
@@ -0,0 +1,180 @@
1
+ import { Knex, knex } from "knex"
2
+ import { Table } from "../../definitions/common"
3
+ import { Operation, QueryJson } from "../../definitions/datasource"
4
+ import { breakExternalTableId } from "../utils"
5
+ import SchemaBuilder = Knex.SchemaBuilder
6
+ import CreateTableBuilder = Knex.CreateTableBuilder
7
+ const { FieldTypes, RelationshipTypes } = require("../../constants")
8
+
9
+ function generateSchema(
10
+ schema: CreateTableBuilder,
11
+ table: Table,
12
+ tables: Record<string, Table>,
13
+ oldTable: null | Table = null
14
+ ) {
15
+ let primaryKey = table && table.primary ? table.primary[0] : null
16
+ const columns = Object.values(table.schema)
17
+ // all columns in a junction table will be meta
18
+ let metaCols = columns.filter(col => col.meta)
19
+ let isJunction = metaCols.length === columns.length
20
+ // can't change primary once its set for now
21
+ if (primaryKey && !oldTable && !isJunction) {
22
+ schema.increments(primaryKey).primary()
23
+ } else if (!oldTable && isJunction) {
24
+ schema.primary(metaCols.map(col => col.name))
25
+ }
26
+
27
+ // check if any columns need added
28
+ const foreignKeys = Object.values(table.schema).map(col => col.foreignKey)
29
+ for (let [key, column] of Object.entries(table.schema)) {
30
+ // skip things that are already correct
31
+ const oldColumn = oldTable ? oldTable.schema[key] : null
32
+ if ((oldColumn && oldColumn.type) || (primaryKey === key && !isJunction)) {
33
+ continue
34
+ }
35
+ switch (column.type) {
36
+ case FieldTypes.STRING:
37
+ case FieldTypes.OPTIONS:
38
+ case FieldTypes.LONGFORM:
39
+ schema.string(key)
40
+ break
41
+ case FieldTypes.NUMBER:
42
+ // if meta is specified then this is a junction table entry
43
+ if (column.meta && column.meta.toKey && column.meta.toTable) {
44
+ const { toKey, toTable } = column.meta
45
+ schema.integer(key).unsigned()
46
+ schema.foreign(key).references(`${toTable}.${toKey}`)
47
+ } else if (foreignKeys.indexOf(key) === -1) {
48
+ schema.float(key)
49
+ }
50
+ break
51
+ case FieldTypes.BOOLEAN:
52
+ schema.boolean(key)
53
+ break
54
+ case FieldTypes.DATETIME:
55
+ schema.datetime(key)
56
+ break
57
+ case FieldTypes.ARRAY:
58
+ schema.json(key)
59
+ break
60
+ case FieldTypes.LINK:
61
+ // this side of the relationship doesn't need any SQL work
62
+ if (
63
+ column.relationshipType !== RelationshipTypes.MANY_TO_ONE &&
64
+ column.relationshipType !== RelationshipTypes.MANY_TO_MANY
65
+ ) {
66
+ if (!column.foreignKey || !column.tableId) {
67
+ throw "Invalid relationship schema"
68
+ }
69
+ const { tableName } = breakExternalTableId(column.tableId)
70
+ // @ts-ignore
71
+ const relatedTable = tables[tableName]
72
+ if (!relatedTable) {
73
+ throw "Referenced table doesn't exist"
74
+ }
75
+ schema.integer(column.foreignKey).unsigned()
76
+ schema
77
+ .foreign(column.foreignKey)
78
+ .references(`${tableName}.${relatedTable.primary[0]}`)
79
+ }
80
+ break
81
+ }
82
+ }
83
+
84
+ // need to check if any columns have been deleted
85
+ if (oldTable) {
86
+ const deletedColumns = Object.entries(oldTable.schema)
87
+ .filter(
88
+ ([key, schema]) =>
89
+ schema.type !== FieldTypes.LINK && table.schema[key] == null
90
+ )
91
+ .map(([key]) => key)
92
+ deletedColumns.forEach(key => {
93
+ if (oldTable.constrained && oldTable.constrained.indexOf(key) !== -1) {
94
+ schema.dropForeign(key)
95
+ }
96
+ schema.dropColumn(key)
97
+ })
98
+ }
99
+
100
+ return schema
101
+ }
102
+
103
+ function buildCreateTable(
104
+ knex: Knex,
105
+ table: Table,
106
+ tables: Record<string, Table>
107
+ ): SchemaBuilder {
108
+ return knex.schema.createTable(table.name, schema => {
109
+ generateSchema(schema, table, tables)
110
+ })
111
+ }
112
+
113
+ function buildUpdateTable(
114
+ knex: Knex,
115
+ table: Table,
116
+ tables: Record<string, Table>,
117
+ oldTable: Table
118
+ ): SchemaBuilder {
119
+ return knex.schema.alterTable(table.name, schema => {
120
+ generateSchema(schema, table, tables, oldTable)
121
+ })
122
+ }
123
+
124
+ function buildDeleteTable(knex: Knex, table: Table): SchemaBuilder {
125
+ return knex.schema.dropTable(table.name)
126
+ }
127
+
128
+ class SqlTableQueryBuilder {
129
+ private readonly sqlClient: string
130
+
131
+ // pass through client to get flavour of SQL
132
+ constructor(client: string) {
133
+ this.sqlClient = client
134
+ }
135
+
136
+ getSqlClient(): string {
137
+ return this.sqlClient
138
+ }
139
+
140
+ /**
141
+ * @param json the input JSON structure from which an SQL query will be built.
142
+ * @return {string} the operation that was found in the JSON.
143
+ */
144
+ _operation(json: QueryJson): Operation {
145
+ return json.endpoint.operation
146
+ }
147
+
148
+ _tableQuery(json: QueryJson): any {
149
+ const client = knex({ client: this.sqlClient })
150
+ let query
151
+ if (!json.table || !json.meta || !json.meta.tables) {
152
+ throw "Cannot execute without table being specified"
153
+ }
154
+ switch (this._operation(json)) {
155
+ case Operation.CREATE_TABLE:
156
+ query = buildCreateTable(client, json.table, json.meta.tables)
157
+ break
158
+ case Operation.UPDATE_TABLE:
159
+ if (!json.meta || !json.meta.table) {
160
+ throw "Must specify old table for update"
161
+ }
162
+ query = buildUpdateTable(
163
+ client,
164
+ json.table,
165
+ json.meta.tables,
166
+ json.meta.table
167
+ )
168
+ break
169
+ case Operation.DELETE_TABLE:
170
+ query = buildDeleteTable(client, json.table)
171
+ break
172
+ default:
173
+ throw "Table operation is of unknown type"
174
+ }
175
+ return query.toSQL()
176
+ }
177
+ }
178
+
179
+ export default SqlTableQueryBuilder
180
+ module.exports = SqlTableQueryBuilder
@@ -0,0 +1,22 @@
1
+ import { QueryJson } from "../../definitions/datasource"
2
+ import { Datasource } from "../../definitions/common"
3
+
4
+ module DatasourceUtils {
5
+ const { integrations } = require("../index")
6
+
7
+ export async function makeExternalQuery(
8
+ datasource: Datasource,
9
+ json: QueryJson
10
+ ) {
11
+ const Integration = integrations[datasource.source]
12
+ // query is the opinionated function
13
+ if (Integration.prototype.query) {
14
+ const integration = new Integration(datasource.config)
15
+ return integration.query(json)
16
+ } else {
17
+ throw "Datasource does not support query."
18
+ }
19
+ }
20
+
21
+ module.exports.makeExternalQuery = makeExternalQuery
22
+ }
@@ -3,6 +3,7 @@ import {
3
3
  DatasourceFieldTypes,
4
4
  QueryTypes,
5
5
  } from "../definitions/datasource"
6
+ import { IntegrationBase } from "./base/IntegrationBase"
6
7
 
7
8
  module CouchDBModule {
8
9
  const PouchDB = require("pouchdb")
@@ -50,7 +51,7 @@ module CouchDBModule {
50
51
  },
51
52
  }
52
53
 
53
- class CouchDBIntegration {
54
+ class CouchDBIntegration implements IntegrationBase {
54
55
  private config: CouchDBConfig
55
56
  private client: any
56
57
 
@@ -3,6 +3,7 @@ import {
3
3
  DatasourceFieldTypes,
4
4
  QueryTypes,
5
5
  } from "../definitions/datasource"
6
+ import { IntegrationBase } from "./base/IntegrationBase"
6
7
 
7
8
  module DynamoModule {
8
9
  const AWS = require("aws-sdk")
@@ -113,7 +114,7 @@ module DynamoModule {
113
114
  },
114
115
  }
115
116
 
116
- class DynamoDBIntegration {
117
+ class DynamoDBIntegration implements IntegrationBase {
117
118
  private config: DynamoDBConfig
118
119
  private client: any
119
120
 
@@ -3,6 +3,7 @@ import {
3
3
  DatasourceFieldTypes,
4
4
  QueryTypes,
5
5
  } from "../definitions/datasource"
6
+ import { IntegrationBase } from "./base/IntegrationBase"
6
7
 
7
8
  module ElasticsearchModule {
8
9
  const { Client } = require("@elastic/elasticsearch")
@@ -74,7 +75,7 @@ module ElasticsearchModule {
74
75
  },
75
76
  }
76
77
 
77
- class ElasticSearchIntegration {
78
+ class ElasticSearchIntegration implements IntegrationBase {
78
79
  private config: ElasticsearchConfig
79
80
  private client: any
80
81
 
@@ -1,11 +1,20 @@
1
1
  import {
2
- Integration,
3
2
  DatasourceFieldTypes,
4
- QueryTypes,
3
+ Integration,
4
+ Operation,
5
5
  QueryJson,
6
+ QueryTypes,
6
7
  SqlQuery,
7
8
  } from "../definitions/datasource"
8
- import { getSqlQuery } from "./utils"
9
+ import {
10
+ getSqlQuery,
11
+ buildExternalTableId,
12
+ convertSqlType,
13
+ finaliseExternalTables,
14
+ SqlClients,
15
+ } from "./utils"
16
+ import { DatasourcePlus } from "./base/datasourcePlus"
17
+ import { Table, TableSchema } from "../definitions/common"
9
18
 
10
19
  module MSSQLModule {
11
20
  const sqlServer = require("mssql")
@@ -22,6 +31,7 @@ module MSSQLModule {
22
31
 
23
32
  const SCHEMA: Integration = {
24
33
  docs: "https://github.com/tediousjs/node-mssql",
34
+ plus: true,
25
35
  description:
26
36
  "Microsoft SQL Server is a relational database management system developed by Microsoft. ",
27
37
  friendlyName: "MS SQL Server",
@@ -69,26 +79,84 @@ module MSSQLModule {
69
79
  },
70
80
  }
71
81
 
72
- async function internalQuery(client: any, query: SqlQuery) {
82
+ async function internalQuery(
83
+ client: any,
84
+ query: SqlQuery,
85
+ operation: string | undefined = undefined
86
+ ) {
87
+ const request = client.request()
73
88
  try {
74
- return await client.query(query.sql, query.bindings || {})
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)
75
102
  } catch (err) {
76
103
  // @ts-ignore
77
104
  throw new Error(err)
78
105
  }
79
106
  }
80
107
 
81
- class SqlServerIntegration extends Sql {
108
+ class SqlServerIntegration extends Sql implements DatasourcePlus {
82
109
  private readonly config: MSSQLConfig
83
110
  static pool: any
111
+ public tables: Record<string, Table> = {}
112
+ public schemaErrors: Record<string, string> = {}
113
+
114
+ MASTER_TABLES = [
115
+ "spt_fallback_db",
116
+ "spt_fallback_dev",
117
+ "spt_fallback_usg",
118
+ "spt_monitor",
119
+ "MSreplication_options",
120
+ ]
121
+ TABLES_SQL =
122
+ "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'"
123
+
124
+ getDefinitionSQL(tableName: string) {
125
+ return `select *
126
+ from INFORMATION_SCHEMA.COLUMNS
127
+ where TABLE_NAME='${tableName}'`
128
+ }
129
+
130
+ getConstraintsSQL(tableName: string) {
131
+ return `SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
132
+ INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KU
133
+ ON TC.CONSTRAINT_TYPE = 'PRIMARY KEY'
134
+ AND TC.CONSTRAINT_NAME = KU.CONSTRAINT_NAME
135
+ AND KU.table_name='${tableName}'
136
+ ORDER BY
137
+ KU.TABLE_NAME,
138
+ KU.ORDINAL_POSITION;`
139
+ }
140
+
141
+ getAutoColumnsSQL(tableName: string) {
142
+ return `SELECT
143
+ COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA+'.'+TABLE_NAME),COLUMN_NAME,'IsComputed')
144
+ AS IS_COMPUTED,
145
+ COLUMNPROPERTY(object_id(TABLE_SCHEMA+'.'+TABLE_NAME), COLUMN_NAME, 'IsIdentity')
146
+ AS IS_IDENTITY,
147
+ *
148
+ FROM INFORMATION_SCHEMA.COLUMNS
149
+ WHERE TABLE_NAME='${tableName}'`
150
+ }
84
151
 
85
152
  constructor(config: MSSQLConfig) {
86
- super("mssql")
153
+ super(SqlClients.MS_SQL)
87
154
  this.config = config
88
155
  const clientCfg = {
89
156
  ...this.config,
90
157
  options: {
91
158
  encrypt: this.config.encrypt,
159
+ enableArithAbort: true,
92
160
  },
93
161
  }
94
162
  delete clientCfg.encrypt
@@ -99,14 +167,75 @@ module MSSQLModule {
99
167
 
100
168
  async connect() {
101
169
  try {
102
- const client = await this.pool.connect()
103
- this.client = client.request()
170
+ this.client = await this.pool.connect()
104
171
  } catch (err) {
105
172
  // @ts-ignore
106
173
  throw new Error(err)
107
174
  }
108
175
  }
109
176
 
177
+ async runSQL(sql: string) {
178
+ return (await internalQuery(this.client, getSqlQuery(sql))).recordset
179
+ }
180
+
181
+ /**
182
+ * Fetches the tables from the sql server database and assigns them to the datasource.
183
+ * @param {*} datasourceId - datasourceId to fetch
184
+ * @param entities - the tables that are to be built
185
+ */
186
+ async buildSchema(datasourceId: string, entities: Record<string, Table>) {
187
+ await this.connect()
188
+ let tableNames = await this.runSQL(this.TABLES_SQL)
189
+ if (tableNames == null || !Array.isArray(tableNames)) {
190
+ throw "Unable to get list of tables in database"
191
+ }
192
+ tableNames = tableNames
193
+ .map((record: any) => record.TABLE_NAME)
194
+ .filter((name: string) => this.MASTER_TABLES.indexOf(name) === -1)
195
+
196
+ const tables: Record<string, Table> = {}
197
+ for (let tableName of tableNames) {
198
+ // get the column definition (type)
199
+ const definition = await this.runSQL(this.getDefinitionSQL(tableName))
200
+ // find primary key constraints
201
+ const constraints = await this.runSQL(this.getConstraintsSQL(tableName))
202
+ // find the computed and identity columns (auto columns)
203
+ const columns = await this.runSQL(this.getAutoColumnsSQL(tableName))
204
+ const primaryKeys = constraints
205
+ .filter(
206
+ (constraint: any) => constraint.CONSTRAINT_TYPE === "PRIMARY KEY"
207
+ )
208
+ .map((constraint: any) => constraint.COLUMN_NAME)
209
+ const autoColumns = columns
210
+ .filter((col: any) => col.IS_COMPUTED || col.IS_IDENTITY)
211
+ .map((col: any) => col.COLUMN_NAME)
212
+
213
+ let schema: TableSchema = {}
214
+ for (let def of definition) {
215
+ const name = def.COLUMN_NAME
216
+ if (typeof name !== "string") {
217
+ continue
218
+ }
219
+ const type: string = convertSqlType(def.DATA_TYPE)
220
+
221
+ schema[name] = {
222
+ autocolumn: !!autoColumns.find((col: string) => col === name),
223
+ name: name,
224
+ type,
225
+ }
226
+ }
227
+ tables[tableName] = {
228
+ _id: buildExternalTableId(datasourceId, tableName),
229
+ primary: primaryKeys,
230
+ name: tableName,
231
+ schema,
232
+ }
233
+ }
234
+ const final = finaliseExternalTables(tables, entities)
235
+ this.tables = final.tables
236
+ this.schemaErrors = final.errors
237
+ }
238
+
110
239
  async read(query: SqlQuery | string) {
111
240
  await this.connect()
112
241
  const response = await internalQuery(this.client, getSqlQuery(query))
@@ -132,10 +261,13 @@ module MSSQLModule {
132
261
  }
133
262
 
134
263
  async query(json: QueryJson) {
135
- const operation = this._operation(json).toLowerCase()
136
- const input = this._query(json)
137
- const response = await internalQuery(this.client, input)
138
- return response.recordset ? response.recordset : [{ [operation]: true }]
264
+ await this.connect()
265
+ const operation = this._operation(json)
266
+ const queryFn = (query: any, op: string) =>
267
+ internalQuery(this.client, query, op)
268
+ const processFn = (result: any) =>
269
+ result.recordset ? result.recordset : [{ [operation]: true }]
270
+ return this.queryWithReturning(json, queryFn, processFn)
139
271
  }
140
272
  }
141
273
 
@@ -3,6 +3,7 @@ import {
3
3
  DatasourceFieldTypes,
4
4
  QueryTypes,
5
5
  } from "../definitions/datasource"
6
+ import { IntegrationBase } from "./base/IntegrationBase"
6
7
 
7
8
  module MongoDBModule {
8
9
  const { MongoClient } = require("mongodb")
@@ -62,7 +63,7 @@ module MongoDBModule {
62
63
  },
63
64
  }
64
65
 
65
- class MongoIntegration {
66
+ class MongoIntegration implements IntegrationBase {
66
67
  private config: MongoDBConfig
67
68
  private client: any
68
69
 
@@ -2,22 +2,22 @@ import {
2
2
  Integration,
3
3
  DatasourceFieldTypes,
4
4
  QueryTypes,
5
- Operation,
6
5
  QueryJson,
7
6
  SqlQuery,
8
7
  } from "../definitions/datasource"
9
8
  import { Table, TableSchema } from "../definitions/common"
10
- import { getSqlQuery } from "./utils"
9
+ import {
10
+ getSqlQuery,
11
+ SqlClients,
12
+ buildExternalTableId,
13
+ convertSqlType,
14
+ finaliseExternalTables,
15
+ } from "./utils"
16
+ import { DatasourcePlus } from "./base/datasourcePlus"
11
17
 
12
18
  module MySQLModule {
13
19
  const mysql = require("mysql2")
14
20
  const Sql = require("./base/sql")
15
- const {
16
- buildExternalTableId,
17
- convertType,
18
- copyExistingPropsOver,
19
- } = require("./utils")
20
- const { FieldTypes } = require("../constants")
21
21
 
22
22
  interface MySQLConfig {
23
23
  host: string
@@ -28,30 +28,6 @@ module MySQLModule {
28
28
  ssl?: object
29
29
  }
30
30
 
31
- const TYPE_MAP = {
32
- text: FieldTypes.LONGFORM,
33
- blob: FieldTypes.LONGFORM,
34
- enum: FieldTypes.STRING,
35
- varchar: FieldTypes.STRING,
36
- float: FieldTypes.NUMBER,
37
- int: FieldTypes.NUMBER,
38
- numeric: FieldTypes.NUMBER,
39
- bigint: FieldTypes.NUMBER,
40
- mediumint: FieldTypes.NUMBER,
41
- decimal: FieldTypes.NUMBER,
42
- dec: FieldTypes.NUMBER,
43
- double: FieldTypes.NUMBER,
44
- real: FieldTypes.NUMBER,
45
- fixed: FieldTypes.NUMBER,
46
- smallint: FieldTypes.NUMBER,
47
- timestamp: FieldTypes.DATETIME,
48
- date: FieldTypes.DATETIME,
49
- datetime: FieldTypes.DATETIME,
50
- time: FieldTypes.DATETIME,
51
- tinyint: FieldTypes.BOOLEAN,
52
- json: DatasourceFieldTypes.JSON,
53
- }
54
-
55
31
  const SCHEMA: Integration = {
56
32
  docs: "https://github.com/mysqljs/mysql",
57
33
  plus: true,
@@ -131,12 +107,14 @@ module MySQLModule {
131
107
  })
132
108
  }
133
109
 
134
- class MySQLIntegration extends Sql {
110
+ class MySQLIntegration extends Sql implements DatasourcePlus {
135
111
  private config: MySQLConfig
136
112
  private readonly client: any
113
+ public tables: Record<string, Table> = {}
114
+ public schemaErrors: Record<string, string> = {}
137
115
 
138
116
  constructor(config: MySQLConfig) {
139
- super("mysql")
117
+ super(SqlClients.MY_SQL)
140
118
  this.config = config
141
119
  if (config.ssl && Object.keys(config.ssl).length === 0) {
142
120
  delete config.ssl
@@ -181,14 +159,10 @@ module MySQLModule {
181
159
  schema[columnName] = {
182
160
  name: columnName,
183
161
  autocolumn: isAuto,
184
- type: convertType(column.Type, TYPE_MAP),
162
+ type: convertSqlType(column.Type),
185
163
  constraints,
186
164
  }
187
165
  }
188
- // for now just default to first column
189
- if (primaryKeys.length === 0) {
190
- primaryKeys.push(descResp[0].Field)
191
- }
192
166
  if (!tables[tableName]) {
193
167
  tables[tableName] = {
194
168
  _id: buildExternalTableId(datasourceId, tableName),
@@ -197,12 +171,12 @@ module MySQLModule {
197
171
  schema,
198
172
  }
199
173
  }
200
-
201
- copyExistingPropsOver(tableName, tables, entities)
202
174
  }
203
175
 
204
176
  this.client.end()
205
- this.tables = tables
177
+ const final = finaliseExternalTables(tables, entities)
178
+ this.tables = final.tables
179
+ this.schemaErrors = final.errors
206
180
  }
207
181
 
208
182
  async create(query: SqlQuery | string) {
@@ -210,7 +184,7 @@ module MySQLModule {
210
184
  return results.length ? results : [{ created: true }]
211
185
  }
212
186
 
213
- read(query: SqlQuery | string) {
187
+ async read(query: SqlQuery | string) {
214
188
  return internalQuery(this.client, getSqlQuery(query))
215
189
  }
216
190
 
@@ -224,60 +198,12 @@ module MySQLModule {
224
198
  return results.length ? results : [{ deleted: true }]
225
199
  }
226
200
 
227
- async getReturningRow(json: QueryJson) {
228
- if (!json.extra || !json.extra.idFilter) {
229
- return {}
230
- }
231
- const input = this._query({
232
- endpoint: {
233
- ...json.endpoint,
234
- operation: Operation.READ,
235
- },
236
- fields: [],
237
- filters: json.extra.idFilter,
238
- paginate: {
239
- limit: 1,
240
- },
241
- })
242
- return internalQuery(this.client, input, false)
243
- }
244
-
245
- // when creating if an ID has been inserted need to make sure
246
- // the id filter is enriched with it before trying to retrieve the row
247
- checkLookupKeys(results: any, json: QueryJson) {
248
- if (!results?.insertId || !json.meta?.table || !json.meta.table.primary) {
249
- return json
250
- }
251
- const primaryKey = json.meta.table.primary?.[0]
252
- json.extra = {
253
- idFilter: {
254
- equal: {
255
- [primaryKey]: results.insertId,
256
- },
257
- },
258
- }
259
- return json
260
- }
261
-
262
201
  async query(json: QueryJson) {
263
- const operation = this._operation(json)
264
202
  this.client.connect()
265
- const input = this._query(json, { disableReturning: true })
266
- let row
267
- // need to manage returning, a feature mySQL can't do
268
- if (operation === operation.DELETE) {
269
- row = this.getReturningRow(json)
270
- }
271
- const results = await internalQuery(this.client, input, false)
272
- // same as delete, manage returning
273
- if (operation === Operation.CREATE || operation === Operation.UPDATE) {
274
- row = this.getReturningRow(this.checkLookupKeys(results, json))
275
- }
203
+ const queryFn = (query: any) => internalQuery(this.client, query, false)
204
+ const output = await this.queryWithReturning(json, queryFn)
276
205
  this.client.end()
277
- if (operation !== Operation.READ) {
278
- return row
279
- }
280
- return results.length ? results : [{ [operation.toLowerCase()]: true }]
206
+ return output
281
207
  }
282
208
  }
283
209