@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
@@ -6,17 +6,18 @@ import {
6
6
  SqlQuery,
7
7
  } from "../definitions/datasource"
8
8
  import { Table } from "../definitions/common"
9
- 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"
10
17
 
11
18
  module PostgresModule {
12
19
  const { Pool } = require("pg")
13
20
  const Sql = require("./base/sql")
14
- const { FieldTypes } = require("../constants")
15
- const {
16
- buildExternalTableId,
17
- convertType,
18
- copyExistingPropsOver,
19
- } = require("./utils")
20
21
  const { escapeDangerousCharacters } = require("../utilities")
21
22
 
22
23
  const JSON_REGEX = /'{.*}'::json/s
@@ -27,6 +28,7 @@ module PostgresModule {
27
28
  database: string
28
29
  user: string
29
30
  password: string
31
+ schema: string
30
32
  ssl?: boolean
31
33
  ca?: string
32
34
  rejectUnauthorized?: boolean
@@ -64,6 +66,11 @@ module PostgresModule {
64
66
  default: "root",
65
67
  required: true,
66
68
  },
69
+ schema: {
70
+ type: DatasourceFieldTypes.STRING,
71
+ default: "public",
72
+ required: true,
73
+ },
67
74
  ssl: {
68
75
  type: DatasourceFieldTypes.BOOLEAN,
69
76
  default: false,
@@ -96,22 +103,6 @@ module PostgresModule {
96
103
  },
97
104
  }
98
105
 
99
- const TYPE_MAP = {
100
- text: FieldTypes.LONGFORM,
101
- varchar: FieldTypes.STRING,
102
- integer: FieldTypes.NUMBER,
103
- bigint: FieldTypes.NUMBER,
104
- decimal: FieldTypes.NUMBER,
105
- smallint: FieldTypes.NUMBER,
106
- real: FieldTypes.NUMBER,
107
- "double precision": FieldTypes.NUMBER,
108
- timestamp: FieldTypes.DATETIME,
109
- time: FieldTypes.DATETIME,
110
- boolean: FieldTypes.BOOLEAN,
111
- json: FieldTypes.JSON,
112
- date: FieldTypes.DATETIME,
113
- }
114
-
115
106
  async function internalQuery(client: any, query: SqlQuery) {
116
107
  // need to handle a specific issue with json data types in postgres,
117
108
  // new lines inside the JSON data will break it
@@ -132,13 +123,14 @@ module PostgresModule {
132
123
  }
133
124
  }
134
125
 
135
- class PostgresIntegration extends Sql {
126
+ class PostgresIntegration extends Sql implements DatasourcePlus {
136
127
  static pool: any
137
128
  private readonly client: any
138
129
  private readonly config: PostgresConfig
130
+ public tables: Record<string, Table> = {}
131
+ public schemaErrors: Record<string, string> = {}
139
132
 
140
- COLUMNS_SQL =
141
- "select * from information_schema.columns where not table_schema = 'information_schema' and not table_schema = 'pg_catalog'"
133
+ COLUMNS_SQL!: string
142
134
 
143
135
  PRIMARY_KEYS_SQL = `
144
136
  select tc.table_schema, tc.table_name, kc.column_name as primary_key
@@ -151,7 +143,7 @@ module PostgresModule {
151
143
  `
152
144
 
153
145
  constructor(config: PostgresConfig) {
154
- super("pg")
146
+ super(SqlClients.POSTGRES)
155
147
  this.config = config
156
148
 
157
149
  let newConfig = {
@@ -168,6 +160,17 @@ module PostgresModule {
168
160
  }
169
161
 
170
162
  this.client = this.pool
163
+ this.setSchema()
164
+ }
165
+
166
+ setSchema() {
167
+ if (!this.config.schema) {
168
+ this.config.schema = "public"
169
+ }
170
+ this.client.on("connect", (client: any) => {
171
+ client.query(`SET search_path TO ${this.config.schema}`)
172
+ })
173
+ this.COLUMNS_SQL = `select * from information_schema.columns where table_schema = '${this.config.schema}'`
171
174
  }
172
175
 
173
176
  /**
@@ -207,13 +210,13 @@ module PostgresModule {
207
210
  if (!tables[tableName] || !tables[tableName].schema) {
208
211
  tables[tableName] = {
209
212
  _id: buildExternalTableId(datasourceId, tableName),
210
- primary: tableKeys[tableName] || ["id"],
213
+ primary: tableKeys[tableName] || [],
211
214
  name: tableName,
212
215
  schema: {},
213
216
  }
214
217
  }
215
218
 
216
- const type: string = convertType(column.data_type, TYPE_MAP)
219
+ const type: string = convertSqlType(column.data_type)
217
220
  const identity = !!(
218
221
  column.identity_generation ||
219
222
  column.identity_start ||
@@ -232,10 +235,9 @@ module PostgresModule {
232
235
  }
233
236
  }
234
237
 
235
- for (let tableName of Object.keys(tables)) {
236
- copyExistingPropsOver(tableName, tables, entities)
237
- }
238
- this.tables = tables
238
+ const final = finaliseExternalTables(tables, entities)
239
+ this.tables = final.tables
240
+ this.schemaErrors = final.errors
239
241
  }
240
242
 
241
243
  async create(query: SqlQuery | string) {
@@ -261,8 +263,16 @@ module PostgresModule {
261
263
  async query(json: QueryJson) {
262
264
  const operation = this._operation(json).toLowerCase()
263
265
  const input = this._query(json)
264
- const response = await internalQuery(this.client, input)
265
- return response.rows.length ? response.rows : [{ [operation]: true }]
266
+ if (Array.isArray(input)) {
267
+ const responses = []
268
+ for (let query of input) {
269
+ responses.push(await internalQuery(this.client, query))
270
+ }
271
+ return responses
272
+ } else {
273
+ const response = await internalQuery(this.client, input)
274
+ return response.rows.length ? response.rows : [{ [operation]: true }]
275
+ }
266
276
  }
267
277
  }
268
278
 
@@ -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 RestModule {
8
9
  const fetch = require("node-fetch")
@@ -131,7 +132,7 @@ module RestModule {
131
132
  },
132
133
  }
133
134
 
134
- class RestIntegration {
135
+ class RestIntegration implements IntegrationBase {
135
136
  private config: RestConfig
136
137
  private headers: {
137
138
  [key: string]: string
@@ -1,4 +1,5 @@
1
1
  import { Integration, QueryTypes } from "../definitions/datasource"
2
+ import { IntegrationBase } from "./base/IntegrationBase"
2
3
 
3
4
  module S3Module {
4
5
  const AWS = require("aws-sdk")
@@ -42,7 +43,7 @@ module S3Module {
42
43
  },
43
44
  }
44
45
 
45
- class S3Integration {
46
+ class S3Integration implements IntegrationBase {
46
47
  private readonly config: S3Config
47
48
  private client: any
48
49
  private connectionPromise: Promise<any>
@@ -9,32 +9,39 @@ class TestConfiguration {
9
9
  }
10
10
 
11
11
  describe("MS SQL Server Integration", () => {
12
- let config
12
+ let config
13
13
 
14
- beforeEach(() => {
14
+ beforeEach(async () => {
15
15
  config = new TestConfiguration()
16
16
  })
17
17
 
18
- it("calls the create method with the correct params", async () => {
19
- const sql = "insert into users (name, age) values ('Joe', 123);"
20
- const response = await config.integration.create({
21
- sql
18
+ describe("check sql used", () => {
19
+ beforeEach(async () => {
20
+ await config.integration.connect()
21
+ })
22
+
23
+ it("calls the create method with the correct params", async () => {
24
+ const sql = "insert into users (name, age) values ('Joe', 123);"
25
+ const response = await config.integration.create({
26
+ sql
27
+ })
28
+ expect(config.integration.client.request).toHaveBeenCalledWith()
29
+ expect(response[0]).toEqual(sql)
22
30
  })
23
- expect(config.integration.client.query).toHaveBeenCalledWith(sql, {})
24
- })
25
31
 
26
- it("calls the read method with the correct params", async () => {
27
- const sql = "select * from users;"
28
- const response = await config.integration.read({
29
- sql
32
+ it("calls the read method with the correct params", async () => {
33
+ const sql = "select * from users;"
34
+ const response = await config.integration.read({
35
+ sql
36
+ })
37
+ expect(config.integration.client.request).toHaveBeenCalledWith()
38
+ expect(response[0]).toEqual(sql)
30
39
  })
31
- expect(config.integration.client.query).toHaveBeenCalledWith(sql, {})
32
40
  })
33
41
 
34
42
  describe("no rows returned", () => {
35
43
  beforeEach(async () => {
36
44
  await config.integration.connect()
37
- config.integration.client.query.mockImplementation(() => ({ rows: [] }))
38
45
  })
39
46
 
40
47
  it("returns the correct response when the create response has no rows", async () => {
@@ -42,7 +49,7 @@ describe("MS SQL Server Integration", () => {
42
49
  const response = await config.integration.create({
43
50
  sql
44
51
  })
45
- expect(response).toEqual([{ created: true }])
52
+ expect(response[0]).toEqual(sql)
46
53
  })
47
54
  })
48
55
  })
@@ -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,45 @@
1
1
  import { SqlQuery } from "../definitions/datasource"
2
- import { Datasource } from "../definitions/common"
2
+ import { Datasource, Table } from "../definitions/common"
3
3
  import { SourceNames } from "../definitions/datasource"
4
4
  const { DocumentTypes, SEPARATOR } = require("../db/utils")
5
- const { FieldTypes } = require("../constants")
5
+ const { FieldTypes, BuildSchemaErrors } = require("../constants")
6
6
 
7
7
  const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}`
8
8
  const ROW_ID_REGEX = /^\[.*]$/g
9
9
 
10
+ const SQL_TYPE_MAP = {
11
+ text: FieldTypes.LONGFORM,
12
+ varchar: FieldTypes.STRING,
13
+ integer: FieldTypes.NUMBER,
14
+ bigint: FieldTypes.NUMBER,
15
+ decimal: FieldTypes.NUMBER,
16
+ smallint: FieldTypes.NUMBER,
17
+ real: FieldTypes.NUMBER,
18
+ "double precision": FieldTypes.NUMBER,
19
+ timestamp: FieldTypes.DATETIME,
20
+ time: FieldTypes.DATETIME,
21
+ boolean: FieldTypes.BOOLEAN,
22
+ json: FieldTypes.JSON,
23
+ date: FieldTypes.DATETIME,
24
+ blob: FieldTypes.LONGFORM,
25
+ enum: FieldTypes.STRING,
26
+ float: FieldTypes.NUMBER,
27
+ int: FieldTypes.NUMBER,
28
+ numeric: FieldTypes.NUMBER,
29
+ mediumint: FieldTypes.NUMBER,
30
+ dec: FieldTypes.NUMBER,
31
+ double: FieldTypes.NUMBER,
32
+ fixed: FieldTypes.NUMBER,
33
+ datetime: FieldTypes.DATETIME,
34
+ tinyint: FieldTypes.BOOLEAN,
35
+ }
36
+
37
+ export enum SqlClients {
38
+ MS_SQL = "mssql",
39
+ POSTGRES = "pg",
40
+ MY_SQL = "mysql",
41
+ }
42
+
10
43
  export function isExternalTable(tableId: string) {
11
44
  return tableId.includes(DocumentTypes.DATASOURCE)
12
45
  }
@@ -68,8 +101,8 @@ export function breakRowIdField(_id: string | { _id: string }): any[] {
68
101
  }
69
102
  }
70
103
 
71
- export function convertType(type: string, map: { [key: string]: any }) {
72
- for (let [external, internal] of Object.entries(map)) {
104
+ export function convertSqlType(type: string) {
105
+ for (let [external, internal] of Object.entries(SQL_TYPE_MAP)) {
73
106
  if (type.toLowerCase().includes(external)) {
74
107
  return internal
75
108
  }
@@ -102,14 +135,14 @@ export function isIsoDateString(str: string) {
102
135
  }
103
136
 
104
137
  // add the existing relationships from the entities if they exist, to prevent them from being overridden
105
- export function copyExistingPropsOver(
138
+ function copyExistingPropsOver(
106
139
  tableName: string,
107
- tables: { [key: string]: any },
140
+ table: Table,
108
141
  entities: { [key: string]: any }
109
142
  ) {
110
143
  if (entities && entities[tableName]) {
111
144
  if (entities[tableName].primaryDisplay) {
112
- tables[tableName].primaryDisplay = entities[tableName].primaryDisplay
145
+ table.primaryDisplay = entities[tableName].primaryDisplay
113
146
  }
114
147
  const existingTableSchema = entities[tableName].schema
115
148
  for (let key in existingTableSchema) {
@@ -117,8 +150,27 @@ export function copyExistingPropsOver(
117
150
  continue
118
151
  }
119
152
  if (existingTableSchema[key].type === "link") {
120
- tables[tableName].schema[key] = existingTableSchema[key]
153
+ table.schema[key] = existingTableSchema[key]
121
154
  }
122
155
  }
123
156
  }
157
+ return table
158
+ }
159
+
160
+ export function finaliseExternalTables(
161
+ tables: { [key: string]: any },
162
+ entities: { [key: string]: any }
163
+ ) {
164
+ const finalTables: { [key: string]: any } = {}
165
+ const errors: { [key: string]: string } = {}
166
+ for (let [name, table] of Object.entries(tables)) {
167
+ // make sure every table has a key
168
+ if (table.primary == null || table.primary.length === 0) {
169
+ errors[name] = BuildSchemaErrors.NO_KEY
170
+ continue
171
+ }
172
+ // make sure all previous props have been added back
173
+ finalTables[name] = copyExistingPropsOver(name, table, entities)
174
+ }
175
+ return { tables: finalTables, errors }
124
176
  }
@@ -15,7 +15,8 @@ module.exports =
15
15
  (permType, permLevel = null) =>
16
16
  async (ctx, next) => {
17
17
  // webhooks don't need authentication, each webhook unique
18
- if (isWebhookEndpoint(ctx)) {
18
+ // also internal requests (between services) don't need authorized
19
+ if (isWebhookEndpoint(ctx) || ctx.internal) {
19
20
  return next()
20
21
  }
21
22
 
@@ -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,5 +1,5 @@
1
- const actions = require("./actions")
2
- const automationUtils = require("./automationUtils")
1
+ const actions = require("../automations/actions")
2
+ const automationUtils = require("../automations/automationUtils")
3
3
  const AutomationEmitter = require("../events/AutomationEmitter")
4
4
  const { processObject } = require("@budibase/string-templates")
5
5
  const { DEFAULT_TENANT_ID } = require("@budibase/auth").constants
@@ -8,8 +8,10 @@ const { DocumentTypes, isDevAppID } = require("../db/utils")
8
8
  const { doInTenant } = require("@budibase/auth/tenancy")
9
9
  const env = require("../environment")
10
10
  const usage = require("../utilities/usageQuota")
11
+ const { definitions: triggerDefs } = require("../automations/triggerInfo")
11
12
 
12
13
  const FILTER_STEP_ID = actions.ACTION_DEFINITIONS.FILTER.stepId
14
+ const CRON_STEP_ID = triggerDefs.CRON.stepId
13
15
  const STOPPED_STATUS = { success: false, status: "STOPPED" }
14
16
 
15
17
  /**
@@ -23,6 +25,8 @@ class Orchestrator {
23
25
  this._chainCount = this._metadata ? this._metadata.automationChainCount : 0
24
26
  this._appId = triggerOutput.appId
25
27
  this._app = null
28
+ const triggerStepId = automation.definition.trigger.stepId
29
+ triggerOutput = this.cleanupTriggerOutputs(triggerStepId, triggerOutput)
26
30
  // remove from context
27
31
  delete triggerOutput.appId
28
32
  delete triggerOutput.metadata
@@ -34,11 +38,17 @@ class Orchestrator {
34
38
  this._emitter = new AutomationEmitter(this._chainCount + 1)
35
39
  this.executionOutput = { trigger: {}, steps: [] }
36
40
  // setup the execution output
37
- const triggerStepId = automation.definition.trigger.stepId
38
41
  const triggerId = automation.definition.trigger.id
39
42
  this.updateExecutionOutput(triggerId, triggerStepId, null, triggerOutput)
40
43
  }
41
44
 
45
+ cleanupTriggerOutputs(stepId, triggerOutput) {
46
+ if (stepId === CRON_STEP_ID) {
47
+ triggerOutput.timestamp = Date.now()
48
+ }
49
+ return triggerOutput
50
+ }
51
+
42
52
  async getStepFunctionality(stepId) {
43
53
  let step = await actions.getAction(stepId)
44
54
  if (step == null) {
@@ -119,10 +129,17 @@ class Orchestrator {
119
129
  }
120
130
  }
121
131
 
122
- module.exports = async job => {
132
+ module.exports = (input, callback) => {
123
133
  const automationOrchestrator = new Orchestrator(
124
- job.data.automation,
125
- job.data.event
134
+ input.data.automation,
135
+ input.data.event
126
136
  )
127
- return automationOrchestrator.execute()
137
+ automationOrchestrator
138
+ .execute()
139
+ .then(response => {
140
+ callback(null, response)
141
+ })
142
+ .catch(err => {
143
+ callback(err)
144
+ })
128
145
  }
@@ -0,0 +1,60 @@
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
+ if (!env.isTest()) {
28
+ const workerOpts = {
29
+ autoStart: true,
30
+ maxConcurrentWorkers: opts.count ? opts.count : 1,
31
+ }
32
+ if (opts.timeoutMs) {
33
+ workerOpts.maxCallTime = opts.timeoutMs
34
+ }
35
+ this.workers = workerFarm(workerOpts, typeToFile(type))
36
+ }
37
+ }
38
+
39
+ run(data) {
40
+ return new Promise((resolve, reject) => {
41
+ let fncToCall
42
+ // if in test then don't use threading
43
+ if (env.isTest()) {
44
+ fncToCall = require(typeToFile(this.type))
45
+ } else {
46
+ fncToCall = this.workers
47
+ }
48
+ fncToCall(data, (err, response) => {
49
+ if (err) {
50
+ reject(err)
51
+ } else {
52
+ resolve(response)
53
+ }
54
+ })
55
+ })
56
+ }
57
+ }
58
+
59
+ module.exports.Thread = Thread
60
+ module.exports.ThreadType = ThreadType
@@ -0,0 +1,63 @@
1
+ const ScriptRunner = require("../utilities/scriptRunner")
2
+ const { integrations } = require("../integrations")
3
+
4
+ function formatResponse(resp) {
5
+ if (typeof resp === "string") {
6
+ try {
7
+ resp = JSON.parse(resp)
8
+ } catch (err) {
9
+ resp = { response: resp }
10
+ }
11
+ }
12
+ return resp
13
+ }
14
+
15
+ async function runAndTransform(datasource, queryVerb, query, transformer) {
16
+ const Integration = integrations[datasource.source]
17
+ if (!Integration) {
18
+ throw "Integration type does not exist."
19
+ }
20
+ const integration = new Integration(datasource.config)
21
+
22
+ let rows = formatResponse(await integration[queryVerb](query))
23
+
24
+ // transform as required
25
+ if (transformer) {
26
+ const runner = new ScriptRunner(transformer, { data: rows })
27
+ rows = runner.execute()
28
+ }
29
+
30
+ // needs to an array for next step
31
+ if (!Array.isArray(rows)) {
32
+ rows = [rows]
33
+ }
34
+
35
+ // map into JSON if just raw primitive here
36
+ if (rows.find(row => typeof row !== "object")) {
37
+ rows = rows.map(value => ({ value }))
38
+ }
39
+
40
+ // get all the potential fields in the schema
41
+ let keys = rows.flatMap(Object.keys)
42
+
43
+ if (integration.end) {
44
+ integration.end()
45
+ }
46
+
47
+ return { rows, keys }
48
+ }
49
+
50
+ module.exports = (input, callback) => {
51
+ runAndTransform(
52
+ input.datasource,
53
+ input.queryVerb,
54
+ input.query,
55
+ input.transformer
56
+ )
57
+ .then(response => {
58
+ callback(null, response)
59
+ })
60
+ .catch(err => {
61
+ callback(err)
62
+ })
63
+ }
@@ -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
  }