@budibase/server 0.9.188 → 0.9.190-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/Dockerfile +7 -1
  2. package/__mocks__/mssql.ts +1 -1
  3. package/__mocks__/oracledb.ts +31 -0
  4. package/__mocks__/pg.ts +3 -0
  5. package/builder/assets/bb-space-man.5be51a45.svg +290 -0
  6. package/builder/assets/index.7d405e74.css +1 -0
  7. package/builder/assets/index.edea4fa5.js +1316 -0
  8. package/builder/assets/oracle.66592424.png +0 -0
  9. package/builder/assets/vendor.470042a4.js +152 -0
  10. package/builder/assets/vendor.5e1fe644.css +1 -0
  11. package/builder/index.html +4 -4
  12. package/coverage/clover.xml +1507 -1081
  13. package/coverage/coverage-final.json +93 -89
  14. package/coverage/lcov-report/api/controllers/analytics.js.html +2 -2
  15. package/coverage/lcov-report/api/controllers/apikeys.js.html +2 -2
  16. package/coverage/lcov-report/api/controllers/application.js.html +64 -13
  17. package/coverage/lcov-report/api/controllers/auth.js.html +19 -10
  18. package/coverage/lcov-report/api/controllers/automation.js.html +3 -3
  19. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/component.js.html +4 -4
  22. package/coverage/lcov-report/api/controllers/datasource.js.html +232 -28
  23. package/coverage/lcov-report/api/controllers/dev.js.html +16 -7
  24. package/coverage/lcov-report/api/controllers/hosting.js.html +10 -10
  25. package/coverage/lcov-report/api/controllers/index.html +91 -91
  26. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/layout.js.html +3 -3
  28. package/coverage/lcov-report/api/controllers/metadata.js.html +3 -3
  29. package/coverage/lcov-report/api/controllers/permission.js.html +3 -3
  30. package/coverage/lcov-report/api/controllers/query.js.html +53 -209
  31. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  32. package/coverage/lcov-report/api/controllers/routing.js.html +3 -3
  33. package/coverage/lcov-report/api/controllers/row/external.js.html +17 -8
  34. package/coverage/lcov-report/api/controllers/row/index.html +26 -26
  35. package/coverage/lcov-report/api/controllers/row/index.js.html +2 -2
  36. package/coverage/lcov-report/api/controllers/row/internal.js.html +9 -9
  37. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +2 -2
  38. package/coverage/lcov-report/api/controllers/row/utils.js.html +21 -30
  39. package/coverage/lcov-report/api/controllers/screen.js.html +2 -2
  40. package/coverage/lcov-report/api/controllers/script.js.html +3 -3
  41. package/coverage/lcov-report/api/controllers/table/external.js.html +1001 -0
  42. package/coverage/lcov-report/api/controllers/table/index.html +56 -26
  43. package/coverage/lcov-report/api/controllers/table/index.js.html +80 -350
  44. package/coverage/lcov-report/api/controllers/table/internal.js.html +545 -0
  45. package/coverage/lcov-report/api/controllers/table/utils.js.html +175 -52
  46. package/coverage/lcov-report/api/controllers/templates.js.html +4 -4
  47. package/coverage/lcov-report/api/controllers/user.js.html +390 -33
  48. package/coverage/lcov-report/api/controllers/view/exporters.js.html +19 -4
  49. package/coverage/lcov-report/api/controllers/view/index.html +22 -22
  50. package/coverage/lcov-report/api/controllers/view/index.js.html +52 -13
  51. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  52. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +3 -3
  53. package/coverage/lcov-report/api/controllers/webhook.js.html +4 -4
  54. package/coverage/lcov-report/api/index.html +1 -1
  55. package/coverage/lcov-report/api/index.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/dev.js.html +2 -2
  67. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/index.html +4 -4
  69. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/query.js.html +5 -2
  75. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/static.js.html +2 -2
  81. package/coverage/lcov-report/api/routes/table.js.html +35 -2
  82. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  83. package/coverage/lcov-report/api/routes/user.js.html +17 -2
  84. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  85. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  86. package/coverage/lcov-report/automations/actions.js.html +2 -2
  87. package/coverage/lcov-report/automations/automationUtils.js.html +4 -4
  88. package/coverage/lcov-report/automations/bullboard.js.html +71 -20
  89. package/coverage/lcov-report/automations/index.html +35 -50
  90. package/coverage/lcov-report/automations/index.js.html +2 -2
  91. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/discord.js.html +3 -3
  96. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/executeScript.js.html +3 -3
  98. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  99. package/coverage/lcov-report/automations/steps/index.html +14 -14
  100. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  101. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +3 -3
  102. package/coverage/lcov-report/automations/steps/queryRows.js.html +3 -3
  103. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  104. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  105. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  106. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  107. package/coverage/lcov-report/automations/steps/utils.js.html +7 -7
  108. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  109. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  110. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  111. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  112. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  113. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  114. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  115. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  116. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  117. package/coverage/lcov-report/automations/triggers.js.html +22 -10
  118. package/coverage/lcov-report/automations/utils.js.html +42 -12
  119. package/coverage/lcov-report/constants/index.html +5 -5
  120. package/coverage/lcov-report/constants/index.js.html +87 -21
  121. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  122. package/coverage/lcov-report/constants/screens.js.html +1 -1
  123. package/coverage/lcov-report/db/client.js.html +2 -2
  124. package/coverage/lcov-report/db/inMemoryView.js.html +2 -2
  125. package/coverage/lcov-report/db/index.html +2 -2
  126. package/coverage/lcov-report/db/index.js.html +1 -1
  127. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +6 -6
  128. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  129. package/coverage/lcov-report/db/linkedRows/index.js.html +5 -5
  130. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +6 -6
  131. package/coverage/lcov-report/db/newid.js.html +3 -3
  132. package/coverage/lcov-report/db/utils.js.html +57 -57
  133. package/coverage/lcov-report/events/AutomationEmitter.js.html +6 -6
  134. package/coverage/lcov-report/events/index.html +6 -6
  135. package/coverage/lcov-report/events/index.js.html +1 -1
  136. package/coverage/lcov-report/events/utils.js.html +5 -5
  137. package/coverage/lcov-report/index.html +106 -91
  138. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  139. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  140. package/coverage/lcov-report/middleware/appInfo.js.html +2 -2
  141. package/coverage/lcov-report/middleware/authorized.js.html +11 -8
  142. package/coverage/lcov-report/middleware/builder.js.html +16 -7
  143. package/coverage/lcov-report/middleware/currentapp.js.html +6 -6
  144. package/coverage/lcov-report/middleware/index.html +23 -23
  145. package/coverage/lcov-report/middleware/joi-validator.js.html +2 -2
  146. package/coverage/lcov-report/middleware/resourceId.js.html +18 -18
  147. package/coverage/lcov-report/middleware/selfhost.js.html +2 -2
  148. package/coverage/lcov-report/middleware/usageQuota.js.html +4 -4
  149. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  150. package/coverage/lcov-report/{automations/thread.js.html → threads/automation.js.html} +90 -33
  151. package/coverage/lcov-report/threads/index.html +141 -0
  152. package/coverage/lcov-report/threads/index.js.html +278 -0
  153. package/coverage/lcov-report/threads/query.js.html +278 -0
  154. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  155. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  156. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  157. package/coverage/lcov-report/utilities/csvParser.js.html +144 -15
  158. package/coverage/lcov-report/utilities/global.js.html +26 -11
  159. package/coverage/lcov-report/utilities/index.html +48 -48
  160. package/coverage/lcov-report/utilities/index.js.html +47 -14
  161. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +30 -9
  162. package/coverage/lcov-report/utilities/queue/index.html +14 -14
  163. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  164. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  165. package/coverage/lcov-report/utilities/rowProcessor/index.html +4 -4
  166. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +2 -2
  167. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +3 -3
  168. package/coverage/lcov-report/utilities/scriptRunner.js.html +16 -7
  169. package/coverage/lcov-report/utilities/security.js.html +3 -3
  170. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  171. package/coverage/lcov-report/utilities/users.js.html +1 -1
  172. package/coverage/lcov-report/utilities/workerRequests.js.html +5 -5
  173. package/coverage/lcov.info +2495 -1737
  174. package/dist/api/controllers/application.js +15 -1
  175. package/dist/api/controllers/auth.js +3 -3
  176. package/dist/api/controllers/datasource.js +67 -16
  177. package/dist/api/controllers/deploy/index.js +2 -0
  178. package/dist/api/controllers/dev.js +4 -1
  179. package/dist/api/controllers/query.js +30 -58
  180. package/dist/api/controllers/row/ExternalRequest.js +78 -34
  181. package/dist/api/controllers/row/external.js +3 -1
  182. package/dist/api/controllers/row/utils.js +6 -12
  183. package/dist/api/controllers/table/external.js +249 -0
  184. package/dist/api/controllers/table/index.js +49 -123
  185. package/dist/api/controllers/table/internal.js +150 -0
  186. package/dist/api/controllers/table/utils.js +62 -28
  187. package/dist/api/controllers/user.js +123 -11
  188. package/dist/api/controllers/view/exporters.js +4 -0
  189. package/dist/api/controllers/view/index.js +14 -2
  190. package/dist/api/routes/query.js +1 -1
  191. package/dist/api/routes/table.js +7 -1
  192. package/dist/api/routes/user.js +2 -1
  193. package/dist/app.js +2 -11
  194. package/dist/automations/bullboard.js +37 -10
  195. package/dist/automations/triggers.js +7 -3
  196. package/dist/automations/utils.js +11 -2
  197. package/dist/constants/index.js +19 -0
  198. package/dist/definitions/datasource.js +5 -0
  199. package/dist/environment.js +13 -0
  200. package/dist/integrations/base/IntegrationBase.js +2 -0
  201. package/dist/integrations/base/datasourcePlus.js +2 -0
  202. package/dist/integrations/base/sql.js +353 -177
  203. package/dist/integrations/base/sqlTable.js +143 -0
  204. package/dist/integrations/base/utils.js +30 -0
  205. package/dist/integrations/index.js +6 -0
  206. package/dist/integrations/microsoftSqlServer.js +117 -9
  207. package/dist/integrations/mysql.js +16 -85
  208. package/dist/integrations/oracle.js +347 -0
  209. package/dist/integrations/postgres.js +35 -28
  210. package/dist/integrations/utils.js +71 -9
  211. package/dist/middleware/authorized.js +2 -1
  212. package/dist/middleware/builder.js +4 -1
  213. package/dist/package.json +12 -7
  214. package/dist/tests/utilities/TestConfiguration.js +2 -1
  215. package/dist/{automations/thread.js → threads/automation.js} +26 -8
  216. package/dist/threads/index.js +64 -0
  217. package/dist/threads/query.js +64 -0
  218. package/dist/tsconfig.tsbuildinfo +1 -1
  219. package/dist/utilities/csvParser.js +46 -5
  220. package/dist/utilities/global.js +6 -2
  221. package/dist/utilities/index.js +11 -0
  222. package/dist/utilities/queue/inMemoryQueue.js +8 -0
  223. package/dist/utilities/scriptRunner.js +4 -1
  224. package/package.json +13 -8
  225. package/scripts/dev/manage.js +1 -0
  226. package/scripts/integrations/mssql/data/Dockerfile +9 -0
  227. package/scripts/integrations/mssql/data/entrypoint.sh +24 -0
  228. package/scripts/integrations/mssql/data/setup.sql +54 -0
  229. package/scripts/integrations/mssql/docker-compose.yaml +12 -0
  230. package/scripts/integrations/mssql/reset.sh +3 -0
  231. package/scripts/integrations/oracle/docker-compose.yml +19 -0
  232. package/scripts/integrations/oracle/instantclient/linux/x86-64/basiclite-21.4.zip +0 -0
  233. package/scripts/integrations/oracle/instantclient/linux/x86-64/install.sh +23 -0
  234. package/scripts/integrations/oracle/oracle.md +90 -0
  235. package/scripts/integrations/postgres/docker-compose.yml +3 -3
  236. package/scripts/likeCypress.ts +33 -0
  237. package/src/api/controllers/application.js +18 -1
  238. package/src/api/controllers/auth.js +3 -0
  239. package/src/api/controllers/datasource.js +84 -16
  240. package/src/api/controllers/deploy/index.js +2 -0
  241. package/src/api/controllers/dev.js +4 -1
  242. package/src/api/controllers/query.js +29 -81
  243. package/src/api/controllers/row/ExternalRequest.ts +91 -36
  244. package/src/api/controllers/row/external.js +4 -1
  245. package/src/api/controllers/row/utils.js +8 -11
  246. package/src/api/controllers/table/external.js +307 -0
  247. package/src/api/controllers/table/index.js +47 -137
  248. package/src/api/controllers/table/internal.js +155 -0
  249. package/src/api/controllers/table/utils.js +74 -33
  250. package/src/api/controllers/user.js +132 -13
  251. package/src/api/controllers/view/exporters.js +5 -0
  252. package/src/api/controllers/view/index.js +15 -2
  253. package/src/api/routes/query.js +1 -0
  254. package/src/api/routes/table.js +11 -0
  255. package/src/api/routes/tests/datasource.spec.js +2 -2
  256. package/src/api/routes/tests/misc.spec.js +5 -1
  257. package/src/api/routes/tests/query.spec.js +0 -1
  258. package/src/api/routes/user.js +5 -0
  259. package/src/app.ts +2 -10
  260. package/src/automations/bullboard.js +28 -11
  261. package/src/automations/tests/automation.spec.js +3 -4
  262. package/src/automations/triggers.js +7 -3
  263. package/src/automations/utils.js +12 -2
  264. package/src/constants/index.js +22 -0
  265. package/src/definitions/common.ts +7 -1
  266. package/src/definitions/datasource.ts +10 -3
  267. package/src/environment.js +15 -0
  268. package/src/integrations/airtable.ts +2 -1
  269. package/src/integrations/arangodb.ts +2 -1
  270. package/src/integrations/base/IntegrationBase.ts +6 -0
  271. package/src/integrations/base/datasourcePlus.ts +9 -0
  272. package/src/integrations/base/sql.ts +377 -218
  273. package/src/integrations/base/sqlTable.ts +180 -0
  274. package/src/integrations/base/utils.ts +22 -0
  275. package/src/integrations/couchdb.ts +2 -1
  276. package/src/integrations/dynamodb.ts +2 -1
  277. package/src/integrations/elasticsearch.ts +2 -1
  278. package/src/integrations/index.ts +7 -0
  279. package/src/integrations/microsoftSqlServer.ts +145 -13
  280. package/src/integrations/mongodb.ts +2 -1
  281. package/src/integrations/mysql.ts +23 -95
  282. package/src/integrations/oracle.ts +458 -0
  283. package/src/integrations/postgres.ts +45 -35
  284. package/src/integrations/rest.ts +2 -1
  285. package/src/integrations/s3.ts +2 -1
  286. package/src/integrations/tests/microsoftSqlServer.spec.js +22 -15
  287. package/src/integrations/tests/oracle.spec.js +94 -0
  288. package/src/integrations/tests/postgres.spec.js +4 -0
  289. package/src/integrations/utils.ts +78 -8
  290. package/src/middleware/authorized.js +2 -1
  291. package/src/middleware/builder.js +4 -1
  292. package/src/tests/utilities/TestConfiguration.js +2 -1
  293. package/src/{automations/thread.js → threads/automation.js} +27 -8
  294. package/src/threads/index.js +66 -0
  295. package/src/threads/query.js +66 -0
  296. package/src/utilities/csvParser.js +48 -5
  297. package/src/utilities/global.js +7 -2
  298. package/src/utilities/index.js +11 -0
  299. package/src/utilities/queue/inMemoryQueue.js +7 -0
  300. package/src/utilities/scriptRunner.js +4 -1
  301. package/src/utilities/tests/__snapshots__/csvParser.spec.js.snap +0 -6
  302. package/src/utilities/tests/csvParser.spec.js +3 -0
  303. package/tsconfig.json +1 -1
  304. package/builder/assets/index.550fdba7.js +0 -201
  305. package/builder/assets/index.7469e1e4.css +0 -29
  306. package/builder/assets/vendor.278a305c.js +0 -9
  307. package/builder/assets/vendor.d75d5eb7.css +0 -1
@@ -26,6 +26,9 @@ const { clientLibraryPath, stringToReadStream } = require("../../utilities");
26
26
  const { getAllLocks } = require("../../utilities/redis");
27
27
  const { updateClientLibrary, backupClientLibrary, revertClientLibrary, } = require("../../utilities/fileSystem/clientLibrary");
28
28
  const { getTenantId, isMultiTenant } = require("@budibase/auth/tenancy");
29
+ const { syncGlobalUsers } = require("./user");
30
+ const { app: appCache } = require("@budibase/auth/cache");
31
+ const { cleanupAutomations } = require("../../automations/utils");
29
32
  const URL_REGEX_SLASH = /\/|\\/g;
30
33
  // utility function, need to do away with this
31
34
  function getLayouts(db) {
@@ -213,6 +216,7 @@ exports.create = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
213
216
  if (!env.isTest()) {
214
217
  yield createApp(appId);
215
218
  }
219
+ yield appCache.invalidateAppMetadata(appId, newApplication);
216
220
  ctx.status = 200;
217
221
  ctx.body = newApplication;
218
222
  });
@@ -267,8 +271,12 @@ exports.delete = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
267
271
  if (!env.isTest() && !ctx.query.unpublish) {
268
272
  yield deleteApp(ctx.params.appId);
269
273
  }
274
+ if (ctx.query && ctx.query.unpublish) {
275
+ yield cleanupAutomations(ctx.params.appId);
276
+ }
270
277
  // make sure the app/role doesn't stick around after the app has been deleted
271
278
  yield removeAppFromUserRoles(ctx, ctx.params.appId);
279
+ yield appCache.invalidateAppMetadata(ctx.params.appId);
272
280
  ctx.status = 200;
273
281
  ctx.body = result;
274
282
  });
@@ -277,6 +285,7 @@ exports.sync = (ctx, next) => __awaiter(void 0, void 0, void 0, function* () {
277
285
  if (!isDevAppID(appId)) {
278
286
  ctx.throw(400, "This action cannot be performed for production apps");
279
287
  }
288
+ // replicate prod to dev
280
289
  const prodAppId = getDeployedAppID(appId);
281
290
  try {
282
291
  const prodDb = new CouchDB(prodAppId, { skip_setup: true });
@@ -307,6 +316,8 @@ exports.sync = (ctx, next) => __awaiter(void 0, void 0, void 0, function* () {
307
316
  catch (err) {
308
317
  error = err;
309
318
  }
319
+ // sync the users
320
+ yield syncGlobalUsers(appId);
310
321
  if (error) {
311
322
  ctx.throw(400, error);
312
323
  }
@@ -327,7 +338,10 @@ const updateAppPackage = (ctx, appPackage, appId) => __awaiter(void 0, void 0, v
327
338
  // the locked by property is attached by server but generated from
328
339
  // Redis, shouldn't ever store it
329
340
  delete newAppPackage.lockedBy;
330
- return yield db.put(newAppPackage);
341
+ const response = yield db.put(newAppPackage);
342
+ // remove any cached metadata, so that it will be updated
343
+ yield appCache.invalidateAppMetadata(appId);
344
+ return response;
331
345
  });
332
346
  const createEmptyAppPackage = (ctx, app) => __awaiter(void 0, void 0, void 0, function* () {
333
347
  const db = new CouchDB(app.appId);
@@ -22,6 +22,8 @@ exports.fetchSelf = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
22
22
  return;
23
23
  }
24
24
  const user = yield getFullUser(ctx, userId);
25
+ // this shouldn't be returned by the app self
26
+ delete user.roles;
25
27
  if (appId) {
26
28
  const db = new CouchDB(appId);
27
29
  // remove the full roles structure
@@ -40,9 +42,7 @@ exports.fetchSelf = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
40
42
  }
41
43
  // user has a role of some sort, return them
42
44
  else if (err.status === 404) {
43
- const metadata = {
44
- _id: userId,
45
- };
45
+ const metadata = Object.assign(Object.assign({}, user), { _id: userId });
46
46
  const dbResp = yield db.put(metadata);
47
47
  user._rev = dbResp.rev;
48
48
  response = user;
@@ -10,8 +10,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  const CouchDB = require("../../db");
12
12
  const { generateDatasourceID, getDatasourceParams, getQueryParams, DocumentTypes, BudibaseInternalDB, getTableParams, } = require("../../db/utils");
13
+ const { BuildSchemaErrors, InvalidColumns } = require("../../constants");
13
14
  const { integrations } = require("../../integrations");
14
- const { makeExternalQuery } = require("./row/utils");
15
+ const { getDatasourceAndQuery } = require("./row/utils");
15
16
  exports.fetch = function (ctx) {
16
17
  return __awaiter(this, void 0, void 0, function* () {
17
18
  const database = new CouchDB(ctx.appId);
@@ -33,11 +34,15 @@ exports.buildSchemaFromDb = function (ctx) {
33
34
  return __awaiter(this, void 0, void 0, function* () {
34
35
  const db = new CouchDB(ctx.appId);
35
36
  const datasource = yield db.get(ctx.params.datasourceId);
36
- const tables = yield buildSchemaHelper(datasource);
37
+ const { tables, error } = yield buildSchemaHelper(datasource);
37
38
  datasource.entities = tables;
38
- const response = yield db.put(datasource);
39
- datasource._rev = response.rev;
40
- ctx.body = datasource;
39
+ const dbResp = yield db.put(datasource);
40
+ datasource._rev = dbResp.rev;
41
+ const response = { datasource };
42
+ if (error) {
43
+ response.error = error;
44
+ }
45
+ ctx.body = response;
41
46
  });
42
47
  };
43
48
  exports.update = function (ctx) {
@@ -57,7 +62,7 @@ exports.update = function (ctx) {
57
62
  }
58
63
  ctx.status = 200;
59
64
  ctx.message = "Datasource saved successfully.";
60
- ctx.body = datasource;
65
+ ctx.body = { datasource };
61
66
  });
62
67
  };
63
68
  exports.save = function (ctx) {
@@ -66,12 +71,14 @@ exports.save = function (ctx) {
66
71
  const plus = ctx.request.body.datasource.plus;
67
72
  const fetchSchema = ctx.request.body.fetchSchema;
68
73
  const datasource = Object.assign({ _id: generateDatasourceID({ plus }), type: plus ? DocumentTypes.DATASOURCE_PLUS : DocumentTypes.DATASOURCE }, ctx.request.body.datasource);
74
+ let schemaError = null;
69
75
  if (fetchSchema) {
70
- let tables = yield buildSchemaHelper(datasource);
76
+ const { tables, error } = yield buildSchemaHelper(datasource);
77
+ schemaError = error;
71
78
  datasource.entities = tables;
72
79
  }
73
- const response = yield db.put(datasource);
74
- datasource._rev = response.rev;
80
+ const dbResp = yield db.put(datasource);
81
+ datasource._rev = dbResp.rev;
75
82
  // Drain connection pools when configuration is changed
76
83
  if (datasource.source) {
77
84
  const source = integrations[datasource.source];
@@ -79,17 +86,23 @@ exports.save = function (ctx) {
79
86
  yield source.pool.end();
80
87
  }
81
88
  }
82
- ctx.status = 200;
83
- ctx.message = "Datasource saved successfully.";
84
- ctx.body = datasource;
89
+ const response = { datasource };
90
+ if (schemaError) {
91
+ response.error = schemaError;
92
+ }
93
+ ctx.body = response;
85
94
  });
86
95
  };
87
96
  exports.destroy = function (ctx) {
88
97
  return __awaiter(this, void 0, void 0, function* () {
89
98
  const db = new CouchDB(ctx.appId);
90
99
  // Delete all queries for the datasource
91
- const rows = yield db.allDocs(getQueryParams(ctx.params.datasourceId, null));
92
- yield db.bulkDocs(rows.rows.map(row => (Object.assign(Object.assign({}, row.doc), { _deleted: true }))));
100
+ const queries = yield db.allDocs(getQueryParams(ctx.params.datasourceId, null));
101
+ yield db.bulkDocs(queries.rows.map(row => ({
102
+ _id: row.id,
103
+ _rev: row.value.rev,
104
+ _deleted: true,
105
+ })));
93
106
  // delete the datasource
94
107
  yield db.remove(ctx.params.datasourceId, ctx.params.revId);
95
108
  ctx.message = `Datasource deleted.`;
@@ -107,18 +120,56 @@ exports.query = function (ctx) {
107
120
  return __awaiter(this, void 0, void 0, function* () {
108
121
  const queryJson = ctx.request.body;
109
122
  try {
110
- ctx.body = yield makeExternalQuery(ctx.appId, queryJson);
123
+ ctx.body = yield getDatasourceAndQuery(ctx.appId, queryJson);
111
124
  }
112
125
  catch (err) {
113
126
  ctx.throw(400, err);
114
127
  }
115
128
  });
116
129
  };
130
+ function getErrorTables(errors, errorType) {
131
+ return Object.entries(errors)
132
+ .filter(entry => entry[1] === errorType)
133
+ .map(([name]) => name);
134
+ }
135
+ function updateError(error, newError, tables) {
136
+ if (!error) {
137
+ error = "";
138
+ }
139
+ if (error.length > 0) {
140
+ error += "\n";
141
+ }
142
+ error += `${newError} ${tables.join(", ")}`;
143
+ return error;
144
+ }
117
145
  const buildSchemaHelper = (datasource) => __awaiter(void 0, void 0, void 0, function* () {
118
146
  const Connector = integrations[datasource.source];
119
147
  // Connect to the DB and build the schema
120
148
  const connector = new Connector(datasource.config);
121
149
  yield connector.buildSchema(datasource._id, datasource.entities);
122
150
  datasource.entities = connector.tables;
123
- return connector.tables;
151
+ // make sure they all have a display name selected
152
+ for (let entity of Object.values(datasource.entities)) {
153
+ if (entity.primaryDisplay) {
154
+ continue;
155
+ }
156
+ const notAutoColumn = Object.values(entity.schema).find(schema => !schema.autocolumn);
157
+ if (notAutoColumn) {
158
+ entity.primaryDisplay = notAutoColumn.name;
159
+ }
160
+ }
161
+ const errors = connector.schemaErrors;
162
+ let error = null;
163
+ if (errors && Object.keys(errors).length > 0) {
164
+ const noKey = getErrorTables(errors, BuildSchemaErrors.NO_KEY);
165
+ const invalidCol = getErrorTables(errors, BuildSchemaErrors.INVALID_COLUMN);
166
+ if (noKey.length) {
167
+ error = updateError(error, "No primary key constraint found for the following:", noKey);
168
+ }
169
+ if (invalidCol.length) {
170
+ const invalidCols = Object.values(InvalidColumns).join(", ");
171
+ error = updateError(error, `Cannot use columns ${invalidCols} found in following:`, invalidCol);
172
+ }
173
+ }
174
+ return { tables: connector.tables, error };
124
175
  });
@@ -13,6 +13,7 @@ const Deployment = require("./Deployment");
13
13
  const { Replication, getDeployedAppID } = require("@budibase/auth/db");
14
14
  const { DocumentTypes, getAutomationParams } = require("../../../db/utils");
15
15
  const { disableAllCrons, enableCronTrigger, } = require("../../../automations/utils");
16
+ const { app: appCache } = require("@budibase/auth/cache");
16
17
  // the max time we can wait for an invalidation to complete before considering it failed
17
18
  const MAX_PENDING_TIME_MS = 30 * 60000;
18
19
  const DeploymentStatus = {
@@ -95,6 +96,7 @@ function deployApp(deployment) {
95
96
  appDoc.appId = productionAppId;
96
97
  appDoc.instance._id = productionAppId;
97
98
  yield db.put(appDoc);
99
+ yield appCache.invalidateAppMetadata(productionAppId);
98
100
  console.log("New app doc written successfully.");
99
101
  yield initDeployedApp(productionAppId);
100
102
  console.log("Deployed app initialised, setting deployment to successful");
@@ -16,6 +16,7 @@ const { request } = require("../../utilities/workerRequests");
16
16
  const { clearLock } = require("../../utilities/redis");
17
17
  const { Replication } = require("@budibase/auth").db;
18
18
  const { DocumentTypes } = require("../../db/utils");
19
+ const { app: appCache } = require("@budibase/auth/cache");
19
20
  function redirect(ctx, method, path = "global") {
20
21
  return __awaiter(this, void 0, void 0, function* () {
21
22
  const { devPath } = ctx.params;
@@ -25,7 +26,8 @@ function redirect(ctx, method, path = "global") {
25
26
  body: ctx.request.body,
26
27
  }, true));
27
28
  if (response.status !== 200) {
28
- ctx.throw(response.status, response.statusText);
29
+ const err = yield response.text();
30
+ ctx.throw(400, err);
29
31
  }
30
32
  const cookie = response.headers.get("set-cookie");
31
33
  if (cookie) {
@@ -102,6 +104,7 @@ exports.revert = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
102
104
  appDoc.appId = appId;
103
105
  appDoc.instance._id = appId;
104
106
  yield db.put(appDoc);
107
+ yield appCache.invalidateAppMetadata(appId);
105
108
  ctx.body = {
106
109
  message: "Reverted changes successfully.",
107
110
  };
@@ -11,10 +11,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  const { processString } = require("@budibase/string-templates");
12
12
  const CouchDB = require("../../db");
13
13
  const { generateQueryID, getQueryParams } = require("../../db/utils");
14
- const { integrations } = require("../../integrations");
15
14
  const { BaseQueryVerbs } = require("../../constants");
16
15
  const env = require("../../environment");
17
- const ScriptRunner = require("../../utilities/scriptRunner");
16
+ const { Thread, ThreadType } = require("../../threads");
17
+ const Runner = new Thread(ThreadType.QUERY, { timeoutMs: 10000 });
18
18
  // simple function to append "readable" to all read queries
19
19
  function enrichQueries(input) {
20
20
  const wasArray = Array.isArray(input);
@@ -26,38 +26,6 @@ function enrichQueries(input) {
26
26
  }
27
27
  return wasArray ? queries : queries[0];
28
28
  }
29
- function formatResponse(resp) {
30
- if (typeof resp === "string") {
31
- try {
32
- resp = JSON.parse(resp);
33
- }
34
- catch (err) {
35
- resp = { response: resp };
36
- }
37
- }
38
- return resp;
39
- }
40
- function runAndTransform(integration, queryVerb, enrichedQuery, transformer) {
41
- return __awaiter(this, void 0, void 0, function* () {
42
- let rows = formatResponse(yield integration[queryVerb](enrichedQuery));
43
- // transform as required
44
- if (transformer) {
45
- const runner = new ScriptRunner(transformer, { data: rows });
46
- rows = runner.execute();
47
- }
48
- // needs to an array for next step
49
- if (!Array.isArray(rows)) {
50
- rows = [rows];
51
- }
52
- // map into JSON if just raw primitive here
53
- if (rows.find(row => typeof row !== "object")) {
54
- rows = rows.map(value => ({ value }));
55
- }
56
- // get all the potential fields in the schema
57
- let keys = rows.flatMap(Object.keys);
58
- return { rows, keys };
59
- });
60
- }
61
29
  exports.fetch = function (ctx) {
62
30
  return __awaiter(this, void 0, void 0, function* () {
63
31
  const db = new CouchDB(ctx.appId);
@@ -94,7 +62,9 @@ function enrichQueryFields(fields, parameters = {}) {
94
62
  }
95
63
  else if (typeof fields[key] === "string") {
96
64
  // enrich string value as normal
97
- enrichedQuery[key] = yield processString(fields[key], parameters);
65
+ enrichedQuery[key] = yield processString(fields[key], parameters, {
66
+ noHelpers: true,
67
+ });
98
68
  }
99
69
  else {
100
70
  enrichedQuery[key] = fields[key];
@@ -133,21 +103,22 @@ exports.preview = function (ctx) {
133
103
  return __awaiter(this, void 0, void 0, function* () {
134
104
  const db = new CouchDB(ctx.appId);
135
105
  const datasource = yield db.get(ctx.request.body.datasourceId);
136
- const Integration = integrations[datasource.source];
137
- if (!Integration) {
138
- ctx.throw(400, "Integration type does not exist.");
139
- }
140
106
  const { fields, parameters, queryVerb, transformer } = ctx.request.body;
141
107
  const enrichedQuery = yield enrichQueryFields(fields, parameters);
142
- const integration = new Integration(datasource.config);
143
- const { rows, keys } = yield runAndTransform(integration, queryVerb, enrichedQuery, transformer);
144
- ctx.body = {
145
- rows,
146
- schemaFields: [...new Set(keys)],
147
- };
148
- // cleanup
149
- if (integration.end) {
150
- integration.end();
108
+ try {
109
+ const { rows, keys } = yield Runner.run({
110
+ datasource,
111
+ queryVerb,
112
+ query: enrichedQuery,
113
+ transformer,
114
+ });
115
+ ctx.body = {
116
+ rows,
117
+ schemaFields: [...new Set(keys)],
118
+ };
119
+ }
120
+ catch (err) {
121
+ ctx.throw(400, err);
151
122
  }
152
123
  });
153
124
  };
@@ -156,18 +127,19 @@ exports.execute = function (ctx) {
156
127
  const db = new CouchDB(ctx.appId);
157
128
  const query = yield db.get(ctx.params.queryId);
158
129
  const datasource = yield db.get(query.datasourceId);
159
- const Integration = integrations[datasource.source];
160
- if (!Integration) {
161
- ctx.throw(400, "Integration type does not exist.");
162
- }
163
130
  const enrichedQuery = yield enrichQueryFields(query.fields, ctx.request.body.parameters);
164
- const integration = new Integration(datasource.config);
165
131
  // call the relevant CRUD method on the integration class
166
- const { rows } = yield runAndTransform(integration, query.queryVerb, enrichedQuery, query.transformer);
167
- ctx.body = rows;
168
- // cleanup
169
- if (integration.end) {
170
- integration.end();
132
+ try {
133
+ const { rows } = yield Runner.run({
134
+ datasource,
135
+ queryVerb: query.queryVerb,
136
+ query: enrichedQuery,
137
+ transformer: query.transformer,
138
+ });
139
+ ctx.body = rows;
140
+ }
141
+ catch (err) {
142
+ ctx.throw(400, err);
171
143
  }
172
144
  });
173
145
  };
@@ -24,12 +24,13 @@ const datasource_1 = require("../../../definitions/datasource");
24
24
  const utils_1 = require("../../../integrations/utils");
25
25
  var External;
26
26
  (function (External) {
27
- const { makeExternalQuery } = require("./utils");
27
+ const { getDatasourceAndQuery } = require("./utils");
28
28
  const { DataSourceOperation, FieldTypes, RelationshipTypes, } = require("../../../constants");
29
29
  const { breakExternalTableId, isSQL } = require("../../../integrations/utils");
30
30
  const { processObjectSync } = require("@budibase/string-templates");
31
31
  const { cloneDeep } = require("lodash/fp");
32
32
  const CouchDB = require("../../../db");
33
+ const { processFormulas } = require("../../../utilities/rowProcessor/utils");
33
34
  function buildFilters(id, filters, table) {
34
35
  const primary = table.primary;
35
36
  // if passed in array need to copy for shifting etc
@@ -151,8 +152,23 @@ var External;
151
152
  thisRow._rev = "rev";
152
153
  return thisRow;
153
154
  }
154
- function isMany(field) {
155
- return (field.relationshipType && field.relationshipType.split("-")[0] === "many");
155
+ function fixArrayTypes(row, table) {
156
+ for (let [fieldName, schema] of Object.entries(table.schema)) {
157
+ if (schema.type === FieldTypes.ARRAY &&
158
+ typeof row[fieldName] === "string") {
159
+ try {
160
+ row[fieldName] = JSON.parse(row[fieldName]);
161
+ }
162
+ catch (err) {
163
+ // couldn't convert back to array, ignore
164
+ delete row[fieldName];
165
+ }
166
+ }
167
+ }
168
+ return row;
169
+ }
170
+ function isOneSide(field) {
171
+ return (field.relationshipType && field.relationshipType.split("-")[0] === "one");
156
172
  }
157
173
  class ExternalRequest {
158
174
  constructor(appId, operation, tableId, datasource) {
@@ -182,7 +198,10 @@ var External;
182
198
  let newRow = {}, manyRelationships = [];
183
199
  for (let [key, field] of Object.entries(table.schema)) {
184
200
  // if set already, or not set just skip it
185
- if ((!row[key] && row[key] !== "") || newRow[key] || field.autocolumn) {
201
+ if (row[key] == null ||
202
+ newRow[key] ||
203
+ field.autocolumn ||
204
+ field.type === FieldTypes.FORMULA) {
186
205
  continue;
187
206
  }
188
207
  // if its an empty string then it means return the column to null (if possible)
@@ -207,24 +226,35 @@ var External;
207
226
  const linkTable = this.tables[linkTableName];
208
227
  // @ts-ignore
209
228
  const linkTablePrimary = linkTable.primary[0];
210
- if (!isMany(field)) {
229
+ // one to many
230
+ if (isOneSide(field)) {
211
231
  newRow[field.foreignKey || linkTablePrimary] = utils_1.breakRowIdField(row[key][0])[0];
212
232
  }
213
- else {
233
+ // many to many
234
+ else if (field.through) {
214
235
  // we're not inserting a doc, will be a bunch of update calls
215
- const isUpdate = !field.through;
216
- const thisKey = isUpdate
217
- ? "id"
218
- : field.throughTo || linkTablePrimary;
219
- // @ts-ignore
220
- const otherKey = isUpdate
221
- ? field.fieldName
222
- : field.throughFrom || tablePrimary;
236
+ const otherKey = field.throughFrom || linkTablePrimary;
237
+ const thisKey = field.throughTo || tablePrimary;
223
238
  row[key].map((relationship) => {
224
- // we don't really support composite keys for relationships, this is why [0] is used
225
239
  manyRelationships.push({
226
240
  tableId: field.through || field.tableId,
227
- isUpdate,
241
+ isUpdate: false,
242
+ key: otherKey,
243
+ [otherKey]: utils_1.breakRowIdField(relationship)[0],
244
+ // leave the ID for enrichment later
245
+ [thisKey]: `{{ literal ${tablePrimary} }}`,
246
+ });
247
+ });
248
+ }
249
+ // many to one
250
+ else {
251
+ const thisKey = "id";
252
+ // @ts-ignore
253
+ const otherKey = field.fieldName;
254
+ row[key].map((relationship) => {
255
+ manyRelationships.push({
256
+ tableId: field.tableId,
257
+ isUpdate: true,
228
258
  key: otherKey,
229
259
  [thisKey]: utils_1.breakRowIdField(relationship)[0],
230
260
  // leave the ID for enrichment later
@@ -278,7 +308,7 @@ var External;
278
308
  return rows;
279
309
  }
280
310
  outputProcessing(rows = [], table, relationships) {
281
- if (rows[0].read === true) {
311
+ if (!rows || rows.length === 0 || rows[0].read === true) {
282
312
  return [];
283
313
  }
284
314
  let finalRows = {};
@@ -290,12 +320,15 @@ var External;
290
320
  finalRows = this.updateRelationshipColumns(row, finalRows, relationships);
291
321
  continue;
292
322
  }
293
- const thisRow = basicProcessing(row, table);
323
+ const thisRow = fixArrayTypes(basicProcessing(row, table), table);
324
+ if (thisRow._id == null) {
325
+ throw "Unable to generate row ID for SQL rows";
326
+ }
294
327
  finalRows[thisRow._id] = thisRow;
295
328
  // do this at end once its been added to the final rows
296
329
  finalRows = this.updateRelationshipColumns(row, finalRows, relationships);
297
330
  }
298
- return Object.values(finalRows);
331
+ return processFormulas(table, Object.values(finalRows));
299
332
  }
300
333
  /**
301
334
  * Gets the list of relationship JSON structures based on the columns in the table,
@@ -329,8 +362,8 @@ var External;
329
362
  const { tableName: throughTableName } = breakExternalTableId(field.through);
330
363
  definition.through = throughTableName;
331
364
  // don't support composite keys for relationships
332
- definition.from = field.throughFrom || table.primary[0];
333
- definition.to = field.throughTo || linkTable.primary[0];
365
+ definition.from = field.throughTo || table.primary[0];
366
+ definition.to = field.throughFrom || linkTable.primary[0];
334
367
  definition.fromPrimary = table.primary[0];
335
368
  definition.toPrimary = linkTable.primary[0];
336
369
  }
@@ -352,24 +385,34 @@ var External;
352
385
  // make a new request to get the row with all its relationships
353
386
  // we need this to work out if any relationships need removed
354
387
  for (let field of Object.values(table.schema)) {
355
- if (field.type !== FieldTypes.LINK || !field.fieldName) {
388
+ if (field.type !== FieldTypes.LINK ||
389
+ !field.fieldName ||
390
+ isOneSide(field)) {
356
391
  continue;
357
392
  }
358
393
  const isMany = field.relationshipType === RelationshipTypes.MANY_TO_MANY;
359
394
  const tableId = isMany ? field.through : field.tableId;
360
- const manyKey = field.throughFrom || primaryKey;
395
+ const { tableName: relatedTableName } = breakExternalTableId(tableId);
396
+ // @ts-ignore
397
+ const linkPrimaryKey = this.tables[relatedTableName].primary[0];
398
+ const manyKey = field.throughTo || primaryKey;
399
+ const lookupField = isMany ? primaryKey : field.foreignKey;
361
400
  const fieldName = isMany ? manyKey : field.fieldName;
362
- const response = yield makeExternalQuery(this.appId, {
401
+ if (!lookupField || !row[lookupField]) {
402
+ continue;
403
+ }
404
+ const response = yield getDatasourceAndQuery(this.appId, {
363
405
  endpoint: getEndpoint(tableId, DataSourceOperation.READ),
364
406
  filters: {
365
407
  equal: {
366
- [fieldName]: row[primaryKey],
408
+ [fieldName]: row[lookupField],
367
409
  },
368
410
  },
369
411
  });
370
412
  // this is the response from knex if no rows found
371
413
  const rows = !response[0].read ? response : [];
372
- related[fieldName] = { rows, isMany, tableId };
414
+ const storeTo = isMany ? field.throughFrom || linkPrimaryKey : manyKey;
415
+ related[storeTo] = { rows, isMany, tableId };
373
416
  }
374
417
  return related;
375
418
  });
@@ -403,7 +446,7 @@ var External;
403
446
  ? DataSourceOperation.UPDATE
404
447
  : DataSourceOperation.CREATE;
405
448
  if (!found) {
406
- promises.push(makeExternalQuery(appId, {
449
+ promises.push(getDatasourceAndQuery(appId, {
407
450
  endpoint: getEndpoint(tableId, operation),
408
451
  // if we're doing many relationships then we're writing, only one response
409
452
  body,
@@ -430,7 +473,7 @@ var External;
430
473
  ? DataSourceOperation.DELETE
431
474
  : DataSourceOperation.UPDATE;
432
475
  const body = isMany ? null : { [colName]: null };
433
- promises.push(makeExternalQuery(this.appId, {
476
+ promises.push(getDatasourceAndQuery(this.appId, {
434
477
  endpoint: getEndpoint(tableId, op),
435
478
  body,
436
479
  filters,
@@ -449,13 +492,14 @@ var External;
449
492
  * is more performant and has the added benefit of protecting against this scenario.
450
493
  */
451
494
  buildFields(table, includeRelations = datasource_1.IncludeRelationships.INCLUDE) {
452
- function extractNonLinkFieldNames(table, existing = []) {
495
+ function extractRealFields(table, existing = []) {
453
496
  return Object.entries(table.schema)
454
497
  .filter(column => column[1].type !== FieldTypes.LINK &&
498
+ column[1].type !== FieldTypes.FORMULA &&
455
499
  !existing.find((field) => field === column[0]))
456
500
  .map(column => `${table.name}.${column[0]}`);
457
501
  }
458
- let fields = extractNonLinkFieldNames(table);
502
+ let fields = extractRealFields(table);
459
503
  for (let field of Object.values(table.schema)) {
460
504
  if (field.type !== FieldTypes.LINK || !includeRelations) {
461
505
  continue;
@@ -463,7 +507,7 @@ var External;
463
507
  const { tableName: linkTableName } = breakExternalTableId(field.tableId);
464
508
  const linkTable = this.tables[linkTableName];
465
509
  if (linkTable) {
466
- const linkedFields = extractNonLinkFieldNames(linkTable, fields);
510
+ const linkedFields = extractRealFields(linkTable, fields);
467
511
  fields = fields.concat(linkedFields);
468
512
  }
469
513
  }
@@ -487,7 +531,7 @@ var External;
487
531
  throw `Unable to process query, table "${tableName}" not defined.`;
488
532
  }
489
533
  // look for specific components of config which may not be considered acceptable
490
- let { id, row, filters, sort, paginate } = cleanupConfig(config, table);
534
+ let { id, row, filters, sort, paginate, rows } = cleanupConfig(config, table);
491
535
  filters = buildFilters(id, filters || {}, table);
492
536
  const relationships = this.buildRelationships(table);
493
537
  // clean up row on ingress using schema
@@ -511,7 +555,7 @@ var External;
511
555
  sort,
512
556
  paginate,
513
557
  relationships,
514
- body: row,
558
+ body: row || rows,
515
559
  // pass an id filter into extra, purely for mysql/returning
516
560
  extra: {
517
561
  idFilter: buildFilters(id || generateIdForRow(row, table), {}, table),
@@ -521,7 +565,7 @@ var External;
521
565
  },
522
566
  };
523
567
  // can't really use response right now
524
- const response = yield makeExternalQuery(appId, json);
568
+ const response = yield getDatasourceAndQuery(appId, json);
525
569
  // handle many to many relationships now if we know the ID (could be auto increment)
526
570
  if (operation !== DataSourceOperation.READ &&
527
571
  processed.manyRelationships) {
@@ -41,6 +41,7 @@ function handleRequest(appId, operation, tableId, opts = {}) {
41
41
  return new ExternalRequest(appId, operation, tableId, opts.datasource).run(opts);
42
42
  });
43
43
  }
44
+ exports.handleRequest = handleRequest;
44
45
  exports.patch = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
45
46
  const appId = ctx.appId;
46
47
  const inputs = ctx.request.body;
@@ -64,7 +65,8 @@ exports.save = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
64
65
  exports.fetchView = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
65
66
  // there are no views in external data sources, shouldn't ever be called
66
67
  // for now just fetch
67
- ctx.params.tableId = ctx.params.viewName.split("all_")[1];
68
+ const split = ctx.params.viewName.split("all_");
69
+ ctx.params.tableId = split[1] ? split[1] : split[0];
68
70
  return exports.fetch(ctx);
69
71
  });
70
72
  exports.fetch = (ctx) => __awaiter(void 0, void 0, void 0, function* () {