@budibase/server 0.9.189 → 0.9.190-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 (310) hide show
  1. package/Dockerfile +7 -1
  2. package/__mocks__/mssql.ts +1 -1
  3. package/__mocks__/oracledb.ts +31 -0
  4. package/__mocks__/pg.ts +3 -0
  5. package/builder/assets/bb-space-man.5be51a45.svg +290 -0
  6. package/builder/assets/index.4fb3f887.css +1 -0
  7. package/builder/assets/index.70dba837.js +1316 -0
  8. package/builder/assets/oracle.66592424.png +0 -0
  9. package/builder/assets/vendor.470042a4.js +152 -0
  10. package/builder/assets/vendor.5e1fe644.css +1 -0
  11. package/builder/index.html +4 -4
  12. package/coverage/clover.xml +1552 -1144
  13. package/coverage/coverage-final.json +96 -92
  14. package/coverage/lcov-report/api/controllers/analytics.js.html +2 -2
  15. package/coverage/lcov-report/api/controllers/apikeys.js.html +2 -2
  16. package/coverage/lcov-report/api/controllers/application.js.html +113 -80
  17. package/coverage/lcov-report/api/controllers/auth.js.html +19 -10
  18. package/coverage/lcov-report/api/controllers/automation.js.html +3 -3
  19. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/component.js.html +4 -4
  22. package/coverage/lcov-report/api/controllers/datasource.js.html +245 -41
  23. package/coverage/lcov-report/api/controllers/dev.js.html +16 -7
  24. package/coverage/lcov-report/api/controllers/hosting.js.html +10 -10
  25. package/coverage/lcov-report/api/controllers/index.html +98 -98
  26. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/layout.js.html +9 -9
  28. package/coverage/lcov-report/api/controllers/metadata.js.html +3 -3
  29. package/coverage/lcov-report/api/controllers/permission.js.html +3 -3
  30. package/coverage/lcov-report/api/controllers/query.js.html +79 -229
  31. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  32. package/coverage/lcov-report/api/controllers/routing.js.html +29 -29
  33. package/coverage/lcov-report/api/controllers/row/external.js.html +12 -6
  34. package/coverage/lcov-report/api/controllers/row/index.html +25 -25
  35. package/coverage/lcov-report/api/controllers/row/index.js.html +2 -2
  36. package/coverage/lcov-report/api/controllers/row/internal.js.html +9 -9
  37. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +2 -2
  38. package/coverage/lcov-report/api/controllers/row/utils.js.html +21 -30
  39. package/coverage/lcov-report/api/controllers/screen.js.html +3 -3
  40. package/coverage/lcov-report/api/controllers/script.js.html +3 -3
  41. package/coverage/lcov-report/api/controllers/table/external.js.html +1001 -0
  42. package/coverage/lcov-report/api/controllers/table/index.html +56 -26
  43. package/coverage/lcov-report/api/controllers/table/index.js.html +80 -350
  44. package/coverage/lcov-report/api/controllers/table/internal.js.html +533 -0
  45. package/coverage/lcov-report/api/controllers/table/utils.js.html +172 -49
  46. package/coverage/lcov-report/api/controllers/templates.js.html +4 -4
  47. package/coverage/lcov-report/api/controllers/user.js.html +390 -33
  48. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  49. package/coverage/lcov-report/api/controllers/view/index.html +11 -11
  50. package/coverage/lcov-report/api/controllers/view/index.js.html +6 -6
  51. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  52. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +3 -3
  53. package/coverage/lcov-report/api/controllers/webhook.js.html +4 -4
  54. package/coverage/lcov-report/api/index.html +1 -1
  55. package/coverage/lcov-report/api/index.js.html +5 -5
  56. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/dev.js.html +2 -2
  67. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/index.html +4 -4
  69. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/query.js.html +5 -2
  75. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/row.js.html +536 -2
  78. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/static.js.html +2 -2
  81. package/coverage/lcov-report/api/routes/table.js.html +464 -2
  82. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  83. package/coverage/lcov-report/api/routes/user.js.html +17 -2
  84. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  85. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  86. package/coverage/lcov-report/automations/actions.js.html +2 -2
  87. package/coverage/lcov-report/automations/automationUtils.js.html +4 -4
  88. package/coverage/lcov-report/automations/bullboard.js.html +71 -20
  89. package/coverage/lcov-report/automations/index.html +35 -50
  90. package/coverage/lcov-report/automations/index.js.html +2 -2
  91. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/discord.js.html +3 -3
  96. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/executeScript.js.html +3 -3
  98. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  99. package/coverage/lcov-report/automations/steps/index.html +14 -14
  100. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  101. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +3 -3
  102. package/coverage/lcov-report/automations/steps/queryRows.js.html +3 -3
  103. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  104. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  105. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  106. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  107. package/coverage/lcov-report/automations/steps/utils.js.html +7 -7
  108. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  109. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  110. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  111. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  112. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  113. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  114. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  115. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  116. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  117. package/coverage/lcov-report/automations/triggers.js.html +22 -10
  118. package/coverage/lcov-report/automations/utils.js.html +46 -16
  119. package/coverage/lcov-report/constants/index.html +5 -5
  120. package/coverage/lcov-report/constants/index.js.html +87 -21
  121. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  122. package/coverage/lcov-report/constants/screens.js.html +2 -2
  123. package/coverage/lcov-report/db/client.js.html +2 -2
  124. package/coverage/lcov-report/db/inMemoryView.js.html +2 -2
  125. package/coverage/lcov-report/db/index.html +2 -2
  126. package/coverage/lcov-report/db/index.js.html +1 -1
  127. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +6 -6
  128. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  129. package/coverage/lcov-report/db/linkedRows/index.js.html +5 -5
  130. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +6 -6
  131. package/coverage/lcov-report/db/newid.js.html +4 -4
  132. package/coverage/lcov-report/db/utils.js.html +75 -75
  133. package/coverage/lcov-report/events/AutomationEmitter.js.html +6 -6
  134. package/coverage/lcov-report/events/index.html +6 -6
  135. package/coverage/lcov-report/events/index.js.html +1 -1
  136. package/coverage/lcov-report/events/utils.js.html +5 -5
  137. package/coverage/lcov-report/index.html +102 -87
  138. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  139. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  140. package/coverage/lcov-report/middleware/appInfo.js.html +2 -2
  141. package/coverage/lcov-report/middleware/authorized.js.html +19 -16
  142. package/coverage/lcov-report/middleware/builder.js.html +22 -13
  143. package/coverage/lcov-report/middleware/currentapp.js.html +33 -33
  144. package/coverage/lcov-report/middleware/index.html +23 -23
  145. package/coverage/lcov-report/middleware/joi-validator.js.html +2 -2
  146. package/coverage/lcov-report/middleware/resourceId.js.html +18 -18
  147. package/coverage/lcov-report/middleware/selfhost.js.html +2 -2
  148. package/coverage/lcov-report/middleware/usageQuota.js.html +4 -4
  149. package/coverage/lcov-report/middleware/utils.js.html +2 -2
  150. package/coverage/lcov-report/{automations/thread.js.html → threads/automation.js.html} +90 -33
  151. package/coverage/lcov-report/threads/index.html +141 -0
  152. package/coverage/lcov-report/threads/index.js.html +278 -0
  153. package/coverage/lcov-report/threads/query.js.html +278 -0
  154. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  155. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  156. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  157. package/coverage/lcov-report/utilities/csvParser.js.html +144 -15
  158. package/coverage/lcov-report/utilities/global.js.html +38 -23
  159. package/coverage/lcov-report/utilities/index.html +48 -48
  160. package/coverage/lcov-report/utilities/index.js.html +49 -16
  161. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +31 -10
  162. package/coverage/lcov-report/utilities/queue/index.html +14 -14
  163. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  164. package/coverage/lcov-report/utilities/routing/index.js.html +2 -2
  165. package/coverage/lcov-report/utilities/rowProcessor/index.html +4 -4
  166. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +2 -2
  167. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +3 -3
  168. package/coverage/lcov-report/utilities/scriptRunner.js.html +16 -7
  169. package/coverage/lcov-report/utilities/security.js.html +3 -3
  170. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  171. package/coverage/lcov-report/utilities/users.js.html +1 -1
  172. package/coverage/lcov-report/utilities/workerRequests.js.html +5 -5
  173. package/coverage/lcov.info +2611 -1883
  174. package/dist/api/controllers/application.js +16 -6
  175. package/dist/api/controllers/auth.js +3 -3
  176. package/dist/api/controllers/datasource.js +67 -16
  177. package/dist/api/controllers/deploy/index.js +2 -0
  178. package/dist/api/controllers/dev.js +4 -1
  179. package/dist/api/controllers/query.js +32 -62
  180. package/dist/api/controllers/row/ExternalRequest.js +83 -36
  181. package/dist/api/controllers/row/external.js +1 -0
  182. package/dist/api/controllers/row/utils.js +6 -12
  183. package/dist/api/controllers/table/external.js +249 -0
  184. package/dist/api/controllers/table/index.js +49 -123
  185. package/dist/api/controllers/table/internal.js +146 -0
  186. package/dist/api/controllers/table/utils.js +62 -28
  187. package/dist/api/controllers/user.js +123 -11
  188. package/dist/api/routes/query.js +1 -1
  189. package/dist/api/routes/row.js +178 -0
  190. package/dist/api/routes/table.js +150 -1
  191. package/dist/api/routes/user.js +2 -1
  192. package/dist/app.js +2 -11
  193. package/dist/automations/bullboard.js +37 -10
  194. package/dist/automations/triggers.js +7 -3
  195. package/dist/automations/utils.js +11 -2
  196. package/dist/constants/index.js +19 -0
  197. package/dist/definitions/datasource.js +17 -1
  198. package/dist/environment.js +13 -0
  199. package/dist/integrations/base/IntegrationBase.js +2 -0
  200. package/dist/integrations/base/datasourcePlus.js +2 -0
  201. package/dist/integrations/base/sql.js +350 -177
  202. package/dist/integrations/base/sqlTable.js +143 -0
  203. package/dist/integrations/base/utils.js +30 -0
  204. package/dist/integrations/index.js +6 -0
  205. package/dist/integrations/microsoftSqlServer.js +117 -9
  206. package/dist/integrations/mysql.js +16 -85
  207. package/dist/integrations/oracle.js +367 -0
  208. package/dist/integrations/postgres.js +35 -28
  209. package/dist/integrations/utils.js +71 -9
  210. package/dist/middleware/authorized.js +2 -1
  211. package/dist/middleware/builder.js +4 -1
  212. package/dist/package.json +17 -8
  213. package/dist/tests/utilities/TestConfiguration.js +2 -1
  214. package/dist/{automations/thread.js → threads/automation.js} +26 -8
  215. package/dist/threads/index.js +64 -0
  216. package/dist/threads/query.js +64 -0
  217. package/dist/tsconfig.tsbuildinfo +1 -1
  218. package/dist/utilities/csvParser.js +46 -5
  219. package/dist/utilities/global.js +6 -2
  220. package/dist/utilities/index.js +11 -0
  221. package/dist/utilities/queue/inMemoryQueue.js +8 -0
  222. package/dist/utilities/scriptRunner.js +4 -1
  223. package/package.json +18 -9
  224. package/scripts/dev/manage.js +1 -0
  225. package/scripts/docs/README.md +31 -0
  226. package/scripts/docs/generate.js +74 -0
  227. package/scripts/docs/toSwagger.js +320 -0
  228. package/scripts/integrations/mssql/data/Dockerfile +9 -0
  229. package/scripts/integrations/mssql/data/entrypoint.sh +24 -0
  230. package/scripts/integrations/mssql/data/setup.sql +54 -0
  231. package/scripts/integrations/mssql/docker-compose.yaml +12 -0
  232. package/scripts/integrations/mssql/reset.sh +3 -0
  233. package/scripts/integrations/oracle/docker-compose.yml +19 -0
  234. package/scripts/integrations/oracle/instantclient/linux/x86-64/basiclite-21.4.zip +0 -0
  235. package/scripts/integrations/oracle/instantclient/linux/x86-64/install.sh +23 -0
  236. package/scripts/integrations/oracle/oracle.md +90 -0
  237. package/scripts/integrations/postgres/docker-compose.yml +3 -3
  238. package/scripts/likeCypress.ts +33 -0
  239. package/src/api/controllers/application.js +18 -7
  240. package/src/api/controllers/auth.js +3 -0
  241. package/src/api/controllers/datasource.js +84 -16
  242. package/src/api/controllers/deploy/index.js +2 -0
  243. package/src/api/controllers/dev.js +4 -1
  244. package/src/api/controllers/query.js +35 -85
  245. package/src/api/controllers/row/ExternalRequest.ts +99 -38
  246. package/src/api/controllers/row/external.js +2 -0
  247. package/src/api/controllers/row/utils.js +8 -11
  248. package/src/api/controllers/table/external.js +307 -0
  249. package/src/api/controllers/table/index.js +47 -137
  250. package/src/api/controllers/table/internal.js +151 -0
  251. package/src/api/controllers/table/utils.js +74 -33
  252. package/src/api/controllers/user.js +132 -13
  253. package/src/api/routes/query.js +1 -0
  254. package/src/api/routes/row.js +178 -0
  255. package/src/api/routes/table.js +154 -0
  256. package/src/api/routes/tests/application.spec.js +0 -2
  257. package/src/api/routes/tests/datasource.spec.js +2 -2
  258. package/src/api/routes/tests/misc.spec.js +5 -1
  259. package/src/api/routes/tests/query.spec.js +38 -9
  260. package/src/api/routes/tests/screen.spec.js +1 -1
  261. package/src/api/routes/user.js +5 -0
  262. package/src/app.ts +2 -10
  263. package/src/automations/bullboard.js +28 -11
  264. package/src/automations/tests/automation.spec.js +3 -4
  265. package/src/automations/triggers.js +7 -3
  266. package/src/automations/utils.js +12 -2
  267. package/src/constants/index.js +22 -0
  268. package/src/definitions/common.ts +7 -1
  269. package/src/definitions/datasource.ts +21 -3
  270. package/src/environment.js +15 -0
  271. package/src/integrations/airtable.ts +2 -1
  272. package/src/integrations/arangodb.ts +2 -1
  273. package/src/integrations/base/IntegrationBase.ts +6 -0
  274. package/src/integrations/base/datasourcePlus.ts +9 -0
  275. package/src/integrations/base/sql.ts +375 -218
  276. package/src/integrations/base/sqlTable.ts +180 -0
  277. package/src/integrations/base/utils.ts +22 -0
  278. package/src/integrations/couchdb.ts +2 -1
  279. package/src/integrations/dynamodb.ts +2 -1
  280. package/src/integrations/elasticsearch.ts +2 -1
  281. package/src/integrations/index.ts +7 -0
  282. package/src/integrations/microsoftSqlServer.ts +145 -13
  283. package/src/integrations/mongodb.ts +2 -1
  284. package/src/integrations/mysql.ts +23 -95
  285. package/src/integrations/oracle.ts +464 -0
  286. package/src/integrations/postgres.ts +45 -35
  287. package/src/integrations/rest.ts +2 -1
  288. package/src/integrations/s3.ts +2 -1
  289. package/src/integrations/tests/microsoftSqlServer.spec.js +22 -15
  290. package/src/integrations/tests/oracle.spec.js +94 -0
  291. package/src/integrations/tests/postgres.spec.js +4 -0
  292. package/src/integrations/utils.ts +78 -8
  293. package/src/middleware/authorized.js +2 -1
  294. package/src/middleware/builder.js +4 -1
  295. package/src/tests/utilities/TestConfiguration.js +2 -1
  296. package/src/{automations/thread.js → threads/automation.js} +27 -8
  297. package/src/threads/index.js +66 -0
  298. package/src/threads/query.js +66 -0
  299. package/src/utilities/csvParser.js +48 -5
  300. package/src/utilities/global.js +7 -2
  301. package/src/utilities/index.js +11 -0
  302. package/src/utilities/queue/inMemoryQueue.js +7 -0
  303. package/src/utilities/scriptRunner.js +4 -1
  304. package/src/utilities/tests/__snapshots__/csvParser.spec.js.snap +0 -6
  305. package/src/utilities/tests/csvParser.spec.js +3 -0
  306. package/tsconfig.json +1 -1
  307. package/builder/assets/index.5cc4c0d0.js +0 -201
  308. package/builder/assets/index.7469e1e4.css +0 -29
  309. package/builder/assets/vendor.278a305c.js +0 -9
  310. package/builder/assets/vendor.d75d5eb7.css +0 -1
@@ -27,13 +27,110 @@ function generateSaveValidator() {
27
27
  }
28
28
 
29
29
  router
30
+ /**
31
+ * @api {get} /api/tables Fetch all tables
32
+ * @apiName Fetch all tables
33
+ * @apiGroup tables
34
+ * @apiPermission table read access
35
+ * @apiDescription This endpoint retrieves all of the tables which have been created in
36
+ * an app. This includes all of the external and internal tables; to tell the difference
37
+ * between these look for the "type" property on each table, either being "internal" or "external".
38
+ *
39
+ * @apiSuccess {object[]} body The response body will be the list of tables that was found - as
40
+ * this does not take any parameters the only error scenario is no access.
41
+ */
30
42
  .get("/api/tables", authorized(BUILDER), tableController.fetch)
43
+ /**
44
+ * @api {get} /api/tables/:id Fetch a single table
45
+ * @apiName Fetch a single table
46
+ * @apiGroup tables
47
+ * @apiPermission table read access
48
+ * @apiDescription Retrieves a single table this could be be internal or external based on
49
+ * the provided table ID.
50
+ *
51
+ * @apiParam {string} id The ID of the table which is to be retrieved.
52
+ *
53
+ * @apiSuccess {object[]} body The response body will be the table that was found.
54
+ */
31
55
  .get(
32
56
  "/api/tables/:id",
33
57
  paramResource("id"),
34
58
  authorized(PermissionTypes.TABLE, PermissionLevels.READ),
35
59
  tableController.find
36
60
  )
61
+ /**
62
+ * @api {post} /api/tables Save a table
63
+ * @apiName Save a table
64
+ * @apiGroup tables
65
+ * @apiPermission builder
66
+ * @apiDescription Create or update a table with this endpoint, this will function for both internal
67
+ * external tables.
68
+ *
69
+ * @apiParam (Body) {string} [_id] If updating an existing table then the ID of the table must be specified.
70
+ * @apiParam (Body) {string} [_rev] If updating an existing internal table then the revision must also be specified.
71
+ * @apiParam (Body) {string} type] This should either be "internal" or "external" depending on the table type -
72
+ * this will default to internal.
73
+ * @apiParam (Body) {string} [sourceId] If creating an external table then this should be set to the data source ID. If
74
+ * building an internal table this does not need to be set, although it will be returned as "bb_internal".
75
+ * @apiParam (Body) {string} name The name of the table, this will be used in the UI. To rename the table simply
76
+ * supply the table structure to this endpoint with the name changed.
77
+ * @apiParam (Body) {object} schema A key value object which has all of the columns in the table as the keys in this
78
+ * object. For each column a "type" and "constraints" must be specified, with some types requiring further information.
79
+ * More information about the schema structure can be found in the Typescript definitions.
80
+ * @apiParam (Body) {string} [primaryDisplay] The name of the column which should be used when displaying rows
81
+ * from this table as relationships.
82
+ * @apiParam (Body) {object[]} [indexes] Specifies the search indexes - this is deprecated behaviour with the introduction
83
+ * of lucene indexes. This functionality is only available for internal tables.
84
+ * @apiParam (Body) {object} [_rename] If a column is to be renamed then the "old" column name should be set in this
85
+ * structure, and the "updated", new column name should also be supplied. The schema should also be updated, this field
86
+ * lets the server know that a field hasn't just been deleted, that the data has moved to a new name, this will fix
87
+ * the rows in the table. This functionality is only available for internal tables.
88
+ * @apiParam (Body) {object} [dataImport] When creating an internal table it can be built from a CSV, by using the
89
+ * CSV validation endpoint. Send the CSV data to the validation endpoint, then put the results of that call
90
+ * into this property, along with the CSV and a table/rows will be built from it. This is not supported when updating
91
+ * or for external tables.
92
+ *
93
+ * @apiParamExample {json} Example:
94
+ * {
95
+ * "_id": "ta_05541307fa0f4044abee071ca2a82119",
96
+ * "_rev": "10-0fbe4e78f69b255d79f1017e2eeef807",
97
+ * "type": "internal",
98
+ * "views": {},
99
+ * "name": "tableName",
100
+ * "schema": {
101
+ * "column": {
102
+ * "type": "string",
103
+ * "constraints": {
104
+ * "type": "string",
105
+ * "length": {
106
+ * "maximum": null
107
+ * },
108
+ * "presence": false
109
+ * },
110
+ * "name": "column"
111
+ * },
112
+ * },
113
+ * "primaryDisplay": "column",
114
+ * "indexes": [],
115
+ * "sourceId": "bb_internal",
116
+ * "_rename": {
117
+ * "old": "columnName",
118
+ * "updated": "newColumnName",
119
+ * },
120
+ * "dataImport": {
121
+ * "csvString": "column\nvalue",
122
+ * "primaryDisplay": "column",
123
+ * "schema": {
124
+ * "column": {
125
+ * "type": "string"
126
+ * }
127
+ * }
128
+ * }
129
+ * }
130
+ *
131
+ * @apiSuccess {object} table The response body will contain the table structure after being cleaned up and
132
+ * saved to the database.
133
+ */
37
134
  .post(
38
135
  "/api/tables",
39
136
  // allows control over updating a table
@@ -42,16 +139,73 @@ router
42
139
  generateSaveValidator(),
43
140
  tableController.save
44
141
  )
142
+ /**
143
+ * @api {post} /api/tables/csv/validate Validate a CSV for a table
144
+ * @apiName Validate a CSV for a table
145
+ * @apiGroup tables
146
+ * @apiPermission builder
147
+ * @apiDescription When creating a new table, or importing a CSV to an existing table the CSV must be validated and
148
+ * converted into a Budibase schema; this endpoint does this.
149
+ *
150
+ * @apiParam (Body) {string} csvString The CSV which is to be validated as a string.
151
+ * @apiParam (Body) {object} [schema] When a CSV has been validated it is possible to re-validate after changing the
152
+ * type of a field, by default everything will be strings as there is no way to infer types. The returned schema can
153
+ * be updated and then returned to the endpoint to re-validate and check if the type will work for the CSV, e.g.
154
+ * using a number instead of strings.
155
+ * @apiParam (Body) {string} [tableId] If importing data to an existing table this will pull the current table and
156
+ * remove any fields from the CSV schema which do not exist on the table/don't match the type of the table. When
157
+ * importing a CSV to an existing table only fields that are present on the table can be imported.
158
+ *
159
+ * @apiSuccess {object} schema The response body will contain a "schema" object that represents the schema found for
160
+ * the CSV - this will be in the same format used for table schema.s
161
+ */
45
162
  .post(
46
163
  "/api/tables/csv/validate",
47
164
  authorized(BUILDER),
48
165
  tableController.validateCSVSchema
49
166
  )
167
+ /**
168
+ * @api {post} /api/tables/:tableId/:revId Delete a table
169
+ * @apiName Delete a table
170
+ * @apiGroup tables
171
+ * @apiPermission builder
172
+ * @apiDescription This endpoint will delete a table and all of its associated data, for this reason it is
173
+ * quite dangerous - it will work for internal and external tables.
174
+ *
175
+ * @apiParam {string} tableId The ID of the table which is to be deleted.
176
+ * @apiParam {string} [revId] If deleting an internal table then the revision must also be supplied (_rev), for
177
+ * external tables this can simply be set to anything, e.g. "external".
178
+ *
179
+ * @apiSuccess {string} message A message stating that the table was deleted successfully.
180
+ */
50
181
  .delete(
51
182
  "/api/tables/:tableId/:revId",
52
183
  paramResource("tableId"),
53
184
  authorized(BUILDER),
54
185
  tableController.destroy
55
186
  )
187
+ /**
188
+ * @api {post} /api/tables/:tableId/:revId Import CSV to existing table
189
+ * @apiName Import CSV to existing table
190
+ * @apiGroup tables
191
+ * @apiPermission builder
192
+ * @apiDescription This endpoint will import data to existing tables, internal or external. It is used in combination
193
+ * with the CSV validation endpoint. Take the output of the CSV validation endpoint and pass it to this endpoint to
194
+ * import the data; please note this will only import fields that already exist on the table/match the type.
195
+ *
196
+ * @apiParam {string} tableId The ID of the table which the data should be imported to.
197
+ *
198
+ * @apiParam (Body) {object} dataImport This is the same as the structure used when creating an internal table with
199
+ * a CSV, it will have the "schema" returned from the CSV validation endpoint and the "csvString" which is to be
200
+ * turned into rows.
201
+ *
202
+ * @apiSuccess {string} message A message stating that the data was imported successfully.
203
+ */
204
+ .post(
205
+ "/api/tables/:tableId/import",
206
+ paramResource("tableId"),
207
+ authorized(BUILDER),
208
+ tableController.bulkImport
209
+ )
56
210
 
57
211
  module.exports = router
@@ -75,7 +75,6 @@ describe("/applications", () => {
75
75
  .expect("Content-Type", /json/)
76
76
  .expect(200)
77
77
  // should have empty packages
78
- expect(res.body.screens.length).toEqual(1)
79
78
  expect(res.body.layouts.length).toEqual(2)
80
79
  })
81
80
  })
@@ -88,7 +87,6 @@ describe("/applications", () => {
88
87
  .expect("Content-Type", /json/)
89
88
  .expect(200)
90
89
  expect(res.body.application).toBeDefined()
91
- expect(res.body.screens.length).toEqual(1)
92
90
  expect(res.body.layouts.length).toEqual(2)
93
91
  })
94
92
  })
@@ -26,8 +26,8 @@ describe("/datasources", () => {
26
26
  .expect('Content-Type', /json/)
27
27
  .expect(200)
28
28
 
29
- expect(res.res.statusMessage).toEqual("Datasource saved successfully.")
30
- expect(res.body.name).toEqual("Test")
29
+ expect(res.body.datasource.name).toEqual("Test")
30
+ expect(res.body.errors).toBeUndefined()
31
31
  })
32
32
  })
33
33
 
@@ -75,7 +75,11 @@ describe("run misc tests", () => {
75
75
  },
76
76
  })
77
77
  const dataImport = {
78
- csvString: "a,b,c,d\n1,2,3,4"
78
+ csvString: "a,b,c,d\n1,2,3,4",
79
+ schema: {},
80
+ }
81
+ for (let col of ["a", "b", "c", "d"]) {
82
+ dataImport.schema[col] = { type: "string" }
79
83
  }
80
84
  await tableUtils.handleDataImport(
81
85
  config.getAppId(),
@@ -1,7 +1,13 @@
1
- // mock out postgres for this
1
+ // Mock out postgres for this
2
2
  jest.mock("pg")
3
3
 
4
- const { findLastKey } = require("lodash/fp")
4
+ // Mock isProdAppID to we can later mock the implementation and pretend we are
5
+ // using prod app IDs
6
+ const authDb = require("@budibase/auth/db")
7
+ const { isProdAppID } = authDb
8
+ const mockIsProdAppID = jest.fn(isProdAppID)
9
+ authDb.isProdAppID = mockIsProdAppID
10
+
5
11
  const setup = require("./utilities")
6
12
  const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
7
13
  const { basicQuery, basicDatasource } = setup.structures
@@ -20,10 +26,12 @@ describe("/queries", () => {
20
26
  })
21
27
 
22
28
  async function createInvalidIntegration() {
23
- const datasource = await config.createDatasource({datasource: {
24
- ...basicDatasource().datasource,
25
- source: "INVALID_INTEGRATION",
26
- }})
29
+ const datasource = await config.createDatasource({
30
+ datasource: {
31
+ ...basicDatasource().datasource,
32
+ source: "INVALID_INTEGRATION",
33
+ },
34
+ })
27
35
  const query = await config.createQuery()
28
36
  return { datasource, query }
29
37
  }
@@ -97,11 +105,32 @@ describe("/queries", () => {
97
105
  .set(await config.defaultHeaders())
98
106
  .expect(200)
99
107
  .expect("Content-Type", /json/)
100
- expect(res.body.fields).toBeUndefined()
101
- expect(res.body.parameters).toBeUndefined()
102
- expect(res.body.schema).toBeUndefined()
108
+ expect(res.body.fields).toBeDefined()
109
+ expect(res.body.parameters).toBeDefined()
110
+ expect(res.body.schema).toBeDefined()
103
111
  })
104
112
  })
113
+
114
+ it("should remove sensitive info for prod apps", async () => {
115
+ // Mock isProdAppID to pretend we are using a prod app
116
+ mockIsProdAppID.mockClear()
117
+ mockIsProdAppID.mockImplementation(() => true)
118
+
119
+ const query = await config.createQuery()
120
+ const res = await request
121
+ .get(`/api/queries/${query._id}`)
122
+ .set(await config.defaultHeaders())
123
+ .expect("Content-Type", /json/)
124
+ .expect(200)
125
+ expect(res.body._id).toEqual(query._id)
126
+ expect(res.body.fields).toBeUndefined()
127
+ expect(res.body.parameters).toBeUndefined()
128
+ expect(res.body.schema).toBeDefined()
129
+
130
+ // Reset isProdAppID mock
131
+ expect(mockIsProdAppID).toHaveBeenCalledTimes(1)
132
+ mockIsProdAppID.mockImplementation(isProdAppID)
133
+ })
105
134
  })
106
135
 
107
136
  describe("destroy", () => {
@@ -21,7 +21,7 @@ describe("/screens", () => {
21
21
  .set(config.defaultHeaders())
22
22
  .expect("Content-Type", /json/)
23
23
  .expect(200)
24
- expect(res.body.length).toEqual(2)
24
+ expect(res.body.length).toEqual(1)
25
25
  expect(res.body.some(s => s._id === screen._id)).toEqual(true)
26
26
  })
27
27
 
@@ -34,5 +34,10 @@ router
34
34
  authorized(PermissionTypes.USER, PermissionLevels.WRITE),
35
35
  controller.destroyMetadata
36
36
  )
37
+ .post(
38
+ "/api/users/metadata/sync/:id",
39
+ authorized(PermissionTypes.USER, PermissionLevels.WRITE),
40
+ controller.syncUser
41
+ )
37
42
 
38
43
  module.exports = router
package/src/app.ts CHANGED
@@ -41,16 +41,8 @@ app.use(
41
41
  )
42
42
 
43
43
  if (!env.isTest()) {
44
- const bullApp = bullboard.init()
45
- app.use(async (ctx: ExtendableContext, next: () => any) => {
46
- if (ctx.path.startsWith(bullboard.pathPrefix)) {
47
- ctx.status = 200
48
- ctx.respond = false
49
- bullApp(ctx.req, ctx.res)
50
- } else {
51
- await next()
52
- }
53
- })
44
+ const plugin = bullboard.init()
45
+ app.use(plugin)
54
46
  }
55
47
 
56
48
  app.context.eventEmitter = eventEmitter
@@ -1,6 +1,6 @@
1
- const { createBullBoard } = require("bull-board")
2
- const { BullAdapter } = require("bull-board/bullAdapter")
3
- const express = require("express")
1
+ const { createBullBoard } = require("@bull-board/api")
2
+ const { BullAdapter } = require("@bull-board/api/bullAdapter")
3
+ const { KoaAdapter } = require("@bull-board/koa")
4
4
  const env = require("../environment")
5
5
  const Queue = env.isTest()
6
6
  ? require("../utilities/queue/inMemoryQueue")
@@ -9,23 +9,40 @@ const { JobQueues } = require("../constants")
9
9
  const { utils } = require("@budibase/auth/redis")
10
10
  const { opts, redisProtocolUrl } = utils.getRedisOptions()
11
11
 
12
- const redisConfig = redisProtocolUrl || { redis: opts }
13
- let automationQueue = new Queue(JobQueues.AUTOMATIONS, redisConfig)
12
+ const CLEANUP_PERIOD_MS = 60 * 1000
13
+ const queueConfig = redisProtocolUrl || { redis: opts }
14
+ let cleanupInternal = null
14
15
 
15
- exports.pathPrefix = "/bulladmin"
16
+ let automationQueue = new Queue(JobQueues.AUTOMATIONS, queueConfig)
17
+
18
+ async function cleanup() {
19
+ await automationQueue.clean(CLEANUP_PERIOD_MS, "completed")
20
+ }
21
+
22
+ const PATH_PREFIX = "/bulladmin"
16
23
 
17
24
  exports.init = () => {
18
- const expressApp = express()
25
+ // cleanup the events every 5 minutes
26
+ if (!cleanupInternal) {
27
+ cleanupInternal = setInterval(cleanup, CLEANUP_PERIOD_MS)
28
+ // fire off an initial cleanup
29
+ cleanup().catch(err => {
30
+ console.error(`Unable to cleanup automation queue initially - ${err}`)
31
+ })
32
+ }
19
33
  // Set up queues for bull board admin
20
34
  const queues = [automationQueue]
21
35
  const adapters = []
36
+ const serverAdapter = new KoaAdapter()
22
37
  for (let queue of queues) {
23
38
  adapters.push(new BullAdapter(queue))
24
39
  }
25
- const { router } = createBullBoard(adapters)
26
-
27
- expressApp.use(exports.pathPrefix, router)
28
- return expressApp
40
+ createBullBoard({
41
+ queues: adapters,
42
+ serverAdapter,
43
+ })
44
+ serverAdapter.setBasePath(PATH_PREFIX)
45
+ return serverAdapter.registerPlugin()
29
46
  }
30
47
 
31
48
  exports.queue = automationQueue
@@ -1,5 +1,5 @@
1
1
  jest.mock("../../utilities/usageQuota")
2
- jest.mock("../thread")
2
+ jest.mock("../../threads/automation")
3
3
  jest.mock("../../utilities/redis", () => ({
4
4
  init: jest.fn(),
5
5
  checkTestFlag: () => {
@@ -11,8 +11,7 @@ jest.spyOn(global.console, "error")
11
11
 
12
12
  require("../../environment")
13
13
  const automation = require("../index")
14
- const usageQuota = require("../../utilities/usageQuota")
15
- const thread = require("../thread")
14
+ const thread = require("../../threads/automation")
16
15
  const triggers = require("../triggers")
17
16
  const { basicAutomation } = require("../../tests/utilities/structures")
18
17
  const { wait } = require("../../utilities")
@@ -62,7 +61,7 @@ describe("Run through some parts of the automations system", () => {
62
61
  }
63
62
  }
64
63
  }
65
- }))
64
+ }), expect.any(Function))
66
65
  })
67
66
 
68
67
  it("should be able to clean inputs with the utilities", () => {
@@ -11,6 +11,10 @@ const utils = require("./utils")
11
11
  const env = require("../environment")
12
12
 
13
13
  const TRIGGER_DEFINITIONS = definitions
14
+ const JOB_OPTS = {
15
+ removeOnComplete: true,
16
+ removeOnFail: true,
17
+ }
14
18
 
15
19
  async function queueRelevantRowAutomations(event, eventType) {
16
20
  if (event.appId == null) {
@@ -47,7 +51,7 @@ async function queueRelevantRowAutomations(event, eventType) {
47
51
  automationTrigger.inputs &&
48
52
  automationTrigger.inputs.tableId === event.row.tableId
49
53
  ) {
50
- await queue.add({ automation, event })
54
+ await queue.add({ automation, event }, JOB_OPTS)
51
55
  }
52
56
  }
53
57
  }
@@ -86,7 +90,7 @@ exports.externalTrigger = async function (
86
90
  automation.definition.trigger != null &&
87
91
  automation.definition.trigger.stepId === definitions.APP.stepId &&
88
92
  automation.definition.trigger.stepId === "APP" &&
89
- !checkTestFlag(automation._id)
93
+ !(await checkTestFlag(automation._id))
90
94
  ) {
91
95
  // values are likely to be submitted as strings, so we shall convert to correct type
92
96
  const coercedFields = {}
@@ -100,7 +104,7 @@ exports.externalTrigger = async function (
100
104
  if (getResponses) {
101
105
  return utils.processEvent({ data })
102
106
  } else {
103
- return queue.add(data)
107
+ return queue.add(data, JOB_OPTS)
104
108
  }
105
109
  }
106
110
 
@@ -1,4 +1,4 @@
1
- const runner = require("./thread")
1
+ const { Thread, ThreadType } = require("../threads")
2
2
  const { definitions } = require("./triggerInfo")
3
3
  const webhooks = require("../api/controllers/webhook")
4
4
  const CouchDB = require("../db")
@@ -10,11 +10,12 @@ const { getDeployedAppID } = require("@budibase/auth/db")
10
10
 
11
11
  const WH_STEP_ID = definitions.WEBHOOK.stepId
12
12
  const CRON_STEP_ID = definitions.CRON.stepId
13
+ const Runner = new Thread(ThreadType.AUTOMATION)
13
14
 
14
15
  exports.processEvent = async job => {
15
16
  try {
16
17
  // need to actually await these so that an error can be captured properly
17
- return await runner(job)
18
+ return await Runner.run(job)
18
19
  } catch (err) {
19
20
  console.error(
20
21
  `${job.data.automation.appId} automation ${job.data.automation._id} was unable to run - ${err}`
@@ -162,3 +163,12 @@ exports.checkForWebhooks = async ({ appId, oldAuto, newAuto }) => {
162
163
  }
163
164
  return newAuto
164
165
  }
166
+
167
+ /**
168
+ * When removing an app/unpublishing it need to make sure automations are cleaned up (cron).
169
+ * @param appId {string} the app that is being removed.
170
+ * @return {Promise<void>} clean is complete if this succeeds.
171
+ */
172
+ exports.cleanupAutomations = async appId => {
173
+ await exports.disableAllCrons(appId)
174
+ }
@@ -45,6 +45,13 @@ exports.FieldTypes = {
45
45
  INTERNAL: "internal",
46
46
  }
47
47
 
48
+ exports.SwitchableTypes = [
49
+ exports.FieldTypes.STRING,
50
+ exports.FieldTypes.OPTIONS,
51
+ exports.FieldTypes.NUMBER,
52
+ exports.FieldTypes.BOOLEAN,
53
+ ]
54
+
48
55
  exports.RelationshipTypes = {
49
56
  ONE_TO_MANY: "one-to-many",
50
57
  MANY_TO_ONE: "many-to-one",
@@ -62,6 +69,10 @@ exports.DataSourceOperation = {
62
69
  READ: "READ",
63
70
  UPDATE: "UPDATE",
64
71
  DELETE: "DELETE",
72
+ BULK_CREATE: "BULK_CREATE",
73
+ CREATE_TABLE: "CREATE_TABLE",
74
+ UPDATE_TABLE: "UPDATE_TABLE",
75
+ DELETE_TABLE: "DELETE_TABLE",
65
76
  }
66
77
 
67
78
  exports.SortDirection = {
@@ -152,5 +163,16 @@ exports.MetadataTypes = {
152
163
  AUTOMATION_TEST_HISTORY: "automationTestHistory",
153
164
  }
154
165
 
166
+ exports.InvalidColumns = {
167
+ ID: "_id",
168
+ REV: "_rev",
169
+ TABLE_ID: "tableId",
170
+ }
171
+
172
+ exports.BuildSchemaErrors = {
173
+ NO_KEY: "no_key",
174
+ INVALID_COLUMN: "invalid_column",
175
+ }
176
+
155
177
  // pass through the list from the auth/core lib
156
178
  exports.ObjectStoreBuckets = ObjectStoreBuckets
@@ -17,6 +17,11 @@ export interface FieldSchema {
17
17
  autocolumn?: boolean
18
18
  throughFrom?: string
19
19
  throughTo?: string
20
+ main?: boolean
21
+ meta?: {
22
+ toTable: string
23
+ toKey: string
24
+ }
20
25
  constraints?: {
21
26
  type?: string
22
27
  email?: boolean
@@ -36,11 +41,12 @@ export interface TableSchema {
36
41
  export interface Table extends Base {
37
42
  type?: string
38
43
  views?: {}
39
- name?: string
44
+ name: string
40
45
  primary?: string[]
41
46
  schema: TableSchema
42
47
  primaryDisplay?: string
43
48
  sourceId?: string
49
+ constrained?: string[]
44
50
  }
45
51
 
46
52
  export interface Row extends Base {
@@ -1,10 +1,14 @@
1
- import { Table } from "./common"
1
+ import { Row, Table } from "./common"
2
2
 
3
3
  export enum Operation {
4
4
  CREATE = "CREATE",
5
5
  READ = "READ",
6
6
  UPDATE = "UPDATE",
7
7
  DELETE = "DELETE",
8
+ BULK_CREATE = "BULK_CREATE",
9
+ CREATE_TABLE = "CREATE_TABLE",
10
+ UPDATE_TABLE = "UPDATE_TABLE",
11
+ DELETE_TABLE = "DELETE_TABLE",
8
12
  }
9
13
 
10
14
  export enum SortDirection {
@@ -42,6 +46,7 @@ export enum SourceNames {
42
46
  MYSQL = "MYSQL",
43
47
  ARANGODB = "ARANGODB",
44
48
  REST = "REST",
49
+ ORACLE = "ORACLE",
45
50
  }
46
51
 
47
52
  export enum IncludeRelationships {
@@ -49,6 +54,17 @@ export enum IncludeRelationships {
49
54
  EXCLUDE = 0,
50
55
  }
51
56
 
57
+ export enum FilterTypes {
58
+ STRING = "string",
59
+ FUZZY = "fuzzy",
60
+ RANGE = "range",
61
+ EQUAL = "equal",
62
+ NOT_EQUAL = "notEqual",
63
+ EMPTY = "empty",
64
+ NOT_EMPTY = "notEmpty",
65
+ ONE_OF = "oneOf",
66
+ }
67
+
52
68
  export interface QueryDefinition {
53
69
  type: QueryTypes
54
70
  displayName?: string
@@ -116,7 +132,7 @@ export interface SortJson {
116
132
 
117
133
  export interface PaginationJson {
118
134
  limit: number
119
- page: string | number
135
+ page?: string | number
120
136
  }
121
137
 
122
138
  export interface RelationshipsJson {
@@ -141,9 +157,11 @@ export interface QueryJson {
141
157
  filters?: SearchFilters
142
158
  sort?: SortJson
143
159
  paginate?: PaginationJson
144
- body?: object
160
+ body?: Row | Row[]
161
+ table?: Table
145
162
  meta?: {
146
163
  table?: Table
164
+ tables?: Record<string, Table>
147
165
  }
148
166
  extra?: {
149
167
  idFilter?: SearchFilters
@@ -23,6 +23,8 @@ if (!LOADED && isDev() && !isTest()) {
23
23
  LOADED = true
24
24
  }
25
25
 
26
+ let inThread = false
27
+
26
28
  module.exports = {
27
29
  // important
28
30
  PORT: process.env.PORT,
@@ -62,6 +64,7 @@ module.exports = {
62
64
  USERID_API_KEY: process.env.USERID_API_KEY,
63
65
  DEPLOYMENT_CREDENTIALS_URL: process.env.DEPLOYMENT_CREDENTIALS_URL,
64
66
  ALLOW_DEV_AUTOMATIONS: process.env.ALLOW_DEV_AUTOMATIONS,
67
+ DISABLE_THREADING: process.env.DISABLE_THREADING,
65
68
  _set(key, value) {
66
69
  process.env[key] = value
67
70
  module.exports[key] = value
@@ -72,6 +75,18 @@ module.exports = {
72
75
  isProd: () => {
73
76
  return !isDev()
74
77
  },
78
+ // used to check if already in a thread, don't thread further
79
+ setInThread: () => {
80
+ inThread = true
81
+ },
82
+ isInThread: () => {
83
+ return inThread
84
+ },
85
+ }
86
+
87
+ // threading can cause memory issues with node-ts in development
88
+ if (isDev() && module.exports.DISABLE_THREADING == null) {
89
+ module.exports._set("DISABLE_THREADING", "1")
75
90
  }
76
91
 
77
92
  // clean up any environment variable edge cases
@@ -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 AirtableModule {
8
9
  const Airtable = require("airtable")
@@ -73,7 +74,7 @@ module AirtableModule {
73
74
  },
74
75
  }
75
76
 
76
- class AirtableIntegration {
77
+ class AirtableIntegration implements IntegrationBase {
77
78
  private config: AirtableConfig
78
79
  private client: any
79
80