@budibase/server 1.0.48 → 1.0.49-alpha.3

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 (289) hide show
  1. package/builder/assets/{index.e083dcaf.js → index.21e57eb4.js} +234 -231
  2. package/builder/assets/index.83c0944f.css +1 -0
  3. package/builder/assets/{vendor.f9faf6ab.js → vendor.d526bfe8.js} +12 -12
  4. package/builder/index.html +3 -3
  5. package/coverage/clover.xml +2338 -2028
  6. package/coverage/coverage-final.json +100 -95
  7. package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
  8. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  9. package/coverage/lcov-report/api/controllers/application.js.html +210 -126
  10. package/coverage/lcov-report/api/controllers/auth.js.html +18 -6
  11. package/coverage/lcov-report/api/controllers/automation.js.html +21 -21
  12. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/component.js.html +4 -4
  15. package/coverage/lcov-report/api/controllers/datasource.js.html +72 -36
  16. package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/index.html +53 -53
  18. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/layout.js.html +6 -6
  20. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/migrations.js.html +119 -0
  22. package/coverage/lcov-report/api/controllers/permission.js.html +15 -15
  23. package/coverage/lcov-report/api/controllers/query/index.html +1 -1
  24. package/coverage/lcov-report/api/controllers/query/index.js.html +19 -19
  25. package/coverage/lcov-report/api/controllers/query/validation.js.html +6 -6
  26. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  27. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/external.js.html +16 -16
  29. package/coverage/lcov-report/api/controllers/row/index.html +48 -33
  30. package/coverage/lcov-report/api/controllers/row/index.js.html +26 -26
  31. package/coverage/lcov-report/api/controllers/row/internal.js.html +95 -158
  32. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +41 -17
  33. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +551 -0
  34. package/coverage/lcov-report/api/controllers/row/utils.js.html +39 -57
  35. package/coverage/lcov-report/api/controllers/screen.js.html +7 -7
  36. package/coverage/lcov-report/api/controllers/script.js.html +4 -4
  37. package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +629 -0
  38. package/coverage/lcov-report/api/controllers/table/external.js.html +12 -12
  39. package/coverage/lcov-report/api/controllers/table/index.html +46 -31
  40. package/coverage/lcov-report/api/controllers/table/index.js.html +53 -77
  41. package/coverage/lcov-report/api/controllers/table/internal.js.html +59 -35
  42. package/coverage/lcov-report/api/controllers/table/utils.js.html +217 -73
  43. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  44. package/coverage/lcov-report/api/controllers/user.js.html +30 -24
  45. package/coverage/lcov-report/api/controllers/view/exporters.js.html +4 -4
  46. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  47. package/coverage/lcov-report/api/controllers/view/index.js.html +14 -14
  48. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  49. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +7 -7
  50. package/coverage/lcov-report/api/controllers/webhook.js.html +15 -15
  51. package/coverage/lcov-report/api/index.html +1 -1
  52. package/coverage/lcov-report/api/index.js.html +8 -8
  53. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/index.html +18 -18
  65. package/coverage/lcov-report/api/routes/index.js.html +3 -3
  66. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/{hosting.js.html → migrations.js.html} +17 -14
  70. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/static.js.html +17 -2
  78. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  81. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  82. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  83. package/coverage/lcov-report/automations/actions.js.html +24 -24
  84. package/coverage/lcov-report/automations/automationUtils.js.html +5 -5
  85. package/coverage/lcov-report/automations/bullboard.js.html +16 -16
  86. package/coverage/lcov-report/automations/index.html +1 -1
  87. package/coverage/lcov-report/automations/index.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/bash.js.html +6 -6
  89. package/coverage/lcov-report/automations/steps/createRow.js.html +13 -19
  90. package/coverage/lcov-report/automations/steps/delay.js.html +4 -4
  91. package/coverage/lcov-report/automations/steps/deleteRow.js.html +11 -20
  92. package/coverage/lcov-report/automations/steps/discord.js.html +7 -7
  93. package/coverage/lcov-report/automations/steps/executeQuery.js.html +6 -6
  94. package/coverage/lcov-report/automations/steps/executeScript.js.html +6 -6
  95. package/coverage/lcov-report/automations/steps/filter.js.html +7 -7
  96. package/coverage/lcov-report/automations/steps/index.html +13 -13
  97. package/coverage/lcov-report/automations/steps/integromat.js.html +5 -5
  98. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +8 -8
  99. package/coverage/lcov-report/automations/steps/queryRows.js.html +10 -10
  100. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +5 -5
  101. package/coverage/lcov-report/automations/steps/serverLog.js.html +3 -3
  102. package/coverage/lcov-report/automations/steps/slack.js.html +5 -5
  103. package/coverage/lcov-report/automations/steps/updateRow.js.html +6 -6
  104. package/coverage/lcov-report/automations/steps/utils.js.html +3 -3
  105. package/coverage/lcov-report/automations/steps/zapier.js.html +5 -5
  106. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  107. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  108. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  109. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  110. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  111. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  112. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  113. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  114. package/coverage/lcov-report/automations/triggers.js.html +18 -18
  115. package/coverage/lcov-report/automations/utils.js.html +32 -32
  116. package/coverage/lcov-report/constants/index.html +5 -5
  117. package/coverage/lcov-report/constants/index.js.html +53 -26
  118. package/coverage/lcov-report/constants/layouts.js.html +5 -5
  119. package/coverage/lcov-report/constants/screens.js.html +4 -4
  120. package/coverage/lcov-report/db/client.js.html +18 -18
  121. package/coverage/lcov-report/db/inMemoryView.js.html +7 -7
  122. package/coverage/lcov-report/db/index.html +15 -15
  123. package/coverage/lcov-report/db/index.js.html +3 -3
  124. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +18 -18
  125. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  126. package/coverage/lcov-report/db/linkedRows/index.js.html +38 -41
  127. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +15 -15
  128. package/coverage/lcov-report/db/newid.js.html +4 -4
  129. package/coverage/lcov-report/db/utils.js.html +86 -86
  130. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  131. package/coverage/lcov-report/events/index.html +1 -1
  132. package/coverage/lcov-report/events/index.js.html +5 -5
  133. package/coverage/lcov-report/events/utils.js.html +3 -3
  134. package/coverage/lcov-report/index.html +114 -99
  135. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  136. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  137. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  138. package/coverage/lcov-report/middleware/authorized.js.html +166 -64
  139. package/coverage/lcov-report/middleware/builder.js.html +8 -8
  140. package/coverage/lcov-report/middleware/currentapp.js.html +29 -29
  141. package/coverage/lcov-report/middleware/index.html +30 -30
  142. package/coverage/lcov-report/middleware/joi-validator.js.html +3 -3
  143. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  144. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  145. package/coverage/lcov-report/middleware/usageQuota.js.html +270 -42
  146. package/coverage/lcov-report/middleware/utils.js.html +2 -2
  147. package/coverage/lcov-report/threads/automation.js.html +1 -1
  148. package/coverage/lcov-report/threads/index.html +15 -15
  149. package/coverage/lcov-report/threads/index.js.html +10 -10
  150. package/coverage/lcov-report/threads/query.js.html +23 -8
  151. package/coverage/lcov-report/threads/utils.js.html +14 -14
  152. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  153. package/coverage/lcov-report/utilities/budibaseDir.js.html +7 -7
  154. package/coverage/lcov-report/utilities/centralPath.js.html +4 -4
  155. package/coverage/lcov-report/utilities/csvParser.js.html +6 -6
  156. package/coverage/lcov-report/utilities/global.js.html +26 -26
  157. package/coverage/lcov-report/utilities/index.html +19 -19
  158. package/coverage/lcov-report/utilities/index.js.html +25 -25
  159. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +8 -8
  160. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  161. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  162. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  163. package/coverage/lcov-report/utilities/rowProcessor/index.html +27 -27
  164. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +115 -79
  165. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +75 -27
  166. package/coverage/lcov-report/utilities/scriptRunner.js.html +5 -5
  167. package/coverage/lcov-report/utilities/security.js.html +8 -8
  168. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  169. package/coverage/lcov-report/utilities/usageQuota/index.html +141 -0
  170. package/coverage/lcov-report/utilities/usageQuota/index.js.html +356 -0
  171. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +302 -0
  172. package/coverage/lcov-report/{api/controllers/hosting.js.html → utilities/usageQuota/usageQuoteReset.js.html} +36 -48
  173. package/coverage/lcov-report/utilities/users.js.html +5 -5
  174. package/coverage/lcov-report/utilities/workerRequests.js.html +39 -108
  175. package/coverage/lcov.info +3656 -3057
  176. package/dist/api/controllers/application.js +61 -34
  177. package/dist/api/controllers/auth.js +4 -0
  178. package/dist/api/controllers/datasource.js +11 -0
  179. package/dist/api/controllers/migrations.js +21 -0
  180. package/dist/api/controllers/row/internal.js +30 -53
  181. package/dist/api/controllers/row/internalSearch.js +12 -4
  182. package/dist/api/controllers/row/staticFormula.js +147 -0
  183. package/dist/api/controllers/row/utils.js +6 -8
  184. package/dist/api/controllers/static/index.js +59 -10
  185. package/dist/api/controllers/table/bulkFormula.js +160 -0
  186. package/dist/api/controllers/table/index.js +12 -9
  187. package/dist/api/controllers/table/internal.js +9 -0
  188. package/dist/api/controllers/table/utils.js +41 -12
  189. package/dist/api/controllers/user.js +2 -0
  190. package/dist/api/routes/index.js +2 -2
  191. package/dist/api/routes/migrations.js +9 -0
  192. package/dist/api/routes/static.js +2 -1
  193. package/dist/app.js +35 -5
  194. package/dist/automations/steps/createRow.js +2 -4
  195. package/dist/automations/steps/deleteRow.js +1 -4
  196. package/dist/constants/index.js +7 -0
  197. package/dist/db/linkedRows/index.js +4 -5
  198. package/dist/definitions/datasource.js +1 -0
  199. package/dist/environment.js +2 -0
  200. package/dist/integrations/base/sql.js +13 -6
  201. package/dist/integrations/googlesheets.js +340 -0
  202. package/dist/integrations/index.js +3 -0
  203. package/dist/integrations/utils.js +57 -9
  204. package/dist/middleware/authorized.js +55 -30
  205. package/dist/middleware/usageQuota.js +80 -13
  206. package/dist/migrations/functions/appUrls.js +46 -0
  207. package/dist/migrations/functions/quotas1.js +50 -0
  208. package/dist/migrations/functions/usageQuotas/index.js +20 -0
  209. package/dist/migrations/functions/usageQuotas/syncApps.js +32 -0
  210. package/dist/migrations/functions/usageQuotas/syncRows.js +35 -0
  211. package/dist/migrations/functions/userEmailViewCasing.js +24 -0
  212. package/dist/migrations/index.js +62 -0
  213. package/dist/package.json +8 -5
  214. package/dist/tests/utilities/TestConfiguration.js +13 -6
  215. package/dist/threads/query.js +4 -0
  216. package/dist/tsconfig.tsbuildinfo +1 -1
  217. package/dist/utilities/rowProcessor/index.js +18 -11
  218. package/dist/utilities/rowProcessor/utils.js +14 -4
  219. package/dist/utilities/{usageQuota.js → usageQuota/index.js} +44 -31
  220. package/dist/utilities/usageQuota/rows.js +77 -0
  221. package/dist/utilities/usageQuota/usageQuoteReset.js +16 -0
  222. package/dist/utilities/workerRequests.js +0 -20
  223. package/nodemon.json +1 -1
  224. package/package.json +9 -6
  225. package/src/api/controllers/application.js +52 -24
  226. package/src/api/controllers/auth.js +4 -0
  227. package/src/api/controllers/datasource.js +12 -0
  228. package/src/api/controllers/migrations.js +13 -0
  229. package/src/api/controllers/row/internal.js +29 -50
  230. package/src/api/controllers/row/internalSearch.js +12 -4
  231. package/src/api/controllers/row/staticFormula.js +157 -0
  232. package/src/api/controllers/row/utils.js +6 -12
  233. package/src/api/controllers/static/index.js +63 -10
  234. package/src/api/controllers/table/bulkFormula.js +183 -0
  235. package/src/api/controllers/table/index.js +16 -24
  236. package/src/api/controllers/table/internal.js +9 -1
  237. package/src/api/controllers/table/utils.js +57 -9
  238. package/src/api/controllers/user.js +2 -0
  239. package/src/api/routes/index.js +2 -2
  240. package/src/api/routes/migrations.js +14 -0
  241. package/src/api/routes/static.js +5 -0
  242. package/src/api/routes/tests/application.spec.js +2 -2
  243. package/src/api/routes/tests/auth.spec.js +1 -2
  244. package/src/api/routes/tests/static.spec.js +98 -0
  245. package/src/app.ts +18 -5
  246. package/src/automations/steps/createRow.js +2 -4
  247. package/src/automations/steps/deleteRow.js +1 -4
  248. package/src/constants/index.js +9 -0
  249. package/src/db/linkedRows/index.js +4 -5
  250. package/src/definitions/common.ts +3 -0
  251. package/src/definitions/datasource.ts +3 -0
  252. package/src/environment.js +2 -0
  253. package/src/integrations/base/sql.ts +14 -7
  254. package/src/integrations/googlesheets.ts +353 -0
  255. package/src/integrations/index.ts +3 -0
  256. package/src/integrations/tests/sql.spec.js +16 -0
  257. package/src/integrations/utils.ts +71 -11
  258. package/src/middleware/authorized.js +64 -30
  259. package/src/middleware/tests/authorized.spec.js +6 -4
  260. package/src/middleware/tests/usageQuota.spec.js +18 -24
  261. package/src/middleware/usageQuota.js +91 -15
  262. package/src/migrations/functions/appUrls.ts +34 -0
  263. package/src/migrations/functions/quotas1.ts +20 -0
  264. package/src/migrations/functions/tests/appUrls.spec.js +29 -0
  265. package/src/migrations/functions/tests/quotas1.spec.js +27 -0
  266. package/src/migrations/functions/tests/userEmailViewCasing.spec.js +25 -0
  267. package/src/migrations/functions/usageQuotas/index.ts +8 -0
  268. package/src/migrations/functions/usageQuotas/syncApps.ts +18 -0
  269. package/src/migrations/functions/usageQuotas/syncRows.ts +21 -0
  270. package/src/migrations/functions/usageQuotas/tests/syncApps.spec.js +37 -0
  271. package/src/migrations/functions/usageQuotas/tests/syncRows.spec.js +43 -0
  272. package/src/migrations/functions/userEmailViewCasing.ts +13 -0
  273. package/src/migrations/index.ts +56 -0
  274. package/src/tests/utilities/TestConfiguration.js +14 -6
  275. package/src/threads/query.js +5 -0
  276. package/src/utilities/rowProcessor/index.js +24 -12
  277. package/src/utilities/rowProcessor/utils.js +23 -7
  278. package/src/utilities/tests/usageQuota/usageQuota.spec.js +72 -0
  279. package/src/utilities/usageQuota/index.js +92 -0
  280. package/src/utilities/usageQuota/rows.js +74 -0
  281. package/src/utilities/usageQuota/usageQuoteReset.js +18 -0
  282. package/src/utilities/workerRequests.js +0 -23
  283. package/builder/assets/index.39e01193.css +0 -1
  284. package/dist/api/controllers/hosting.js +0 -30
  285. package/dist/api/routes/hosting.js +0 -11
  286. package/src/api/controllers/hosting.js +0 -22
  287. package/src/api/routes/hosting.js +0 -13
  288. package/src/api/routes/tests/hosting.spec.js +0 -36
  289. package/src/utilities/usageQuota.js +0 -73
@@ -175,6 +175,7 @@ function processAutoColumn(user, table, row, opts = { reprocessing: false, noAut
175
175
  return { table, row };
176
176
  }
177
177
  exports.processAutoColumn = processAutoColumn;
178
+ exports.processFormulas = processFormulas;
178
179
  /**
179
180
  * This will coerce a value to the correct types based on the type transform map
180
181
  * @param {object} row The value to coerce
@@ -218,11 +219,11 @@ exports.inputProcessing = (user = {}, table, row, opts = { noAutoRelationships:
218
219
  }
219
220
  continue;
220
221
  }
221
- // specific case to delete formula values if they get saved
222
- // type coercion cannot completely remove the field, so have to do it here
222
+ // remove any formula values, they are to be generated
223
223
  if (field.type === FieldTypes.FORMULA) {
224
224
  delete clonedRow[key];
225
225
  }
226
+ // otherwise coerce what is there to correct types
226
227
  else {
227
228
  clonedRow[key] = exports.coerce(value, field.type);
228
229
  }
@@ -237,24 +238,23 @@ exports.inputProcessing = (user = {}, table, row, opts = { noAutoRelationships:
237
238
  /**
238
239
  * This function enriches the input rows with anything they are supposed to contain, for example
239
240
  * link records or attachment links.
240
- * @param {object} ctx the request which is looking for enriched rows.
241
+ * @param {string} appId the app in which the request is looking for enriched rows.
241
242
  * @param {object} table the table from which these rows came from originally, this is used to determine
242
243
  * the schema of the rows and then enrich.
243
244
  * @param {object[]|object} rows the rows which are to be enriched.
244
245
  * @param {object} opts used to set some options for the output, such as disabling relationship squashing.
245
246
  * @returns {object[]|object} the enriched rows will be returned.
246
247
  */
247
- exports.outputProcessing = (ctx, table, rows, opts = { squash: true }) => __awaiter(void 0, void 0, void 0, function* () {
248
- const appId = ctx.appId;
248
+ exports.outputProcessing = ({ appId }, table, rows, opts = { squash: true }) => __awaiter(void 0, void 0, void 0, function* () {
249
249
  let wasArray = true;
250
250
  if (!(rows instanceof Array)) {
251
251
  rows = [rows];
252
252
  wasArray = false;
253
253
  }
254
254
  // attach any linked row information
255
- let enriched = yield linkRows.attachFullLinkedDocs(ctx, table, rows);
255
+ let enriched = yield linkRows.attachFullLinkedDocs(appId, table, rows);
256
256
  // process formulas
257
- enriched = processFormulas(table, enriched);
257
+ enriched = processFormulas(table, enriched, { dynamic: true });
258
258
  // update the attachments URL depending on hosting
259
259
  for (let [property, column] of Object.entries(table.schema)) {
260
260
  if (column.type === FieldTypes.ATTACHMENT) {
@@ -280,9 +280,11 @@ exports.outputProcessing = (ctx, table, rows, opts = { squash: true }) => __awai
280
280
  * @param {any} row optional - the row being removed.
281
281
  * @param {any} rows optional - if multiple rows being deleted can do this in bulk.
282
282
  * @param {any} oldRow optional - if updating a row this will determine the difference.
283
+ * @param {any} oldTable optional - if updating a table, can supply the old table to look for
284
+ * deleted attachment columns.
283
285
  * @return {Promise<void>} When all attachments have been removed this will return.
284
286
  */
285
- exports.cleanupAttachments = (appId, table, { row, rows, oldRow }) => __awaiter(void 0, void 0, void 0, function* () {
287
+ exports.cleanupAttachments = (appId, table, { row, rows, oldRow, oldTable }) => __awaiter(void 0, void 0, void 0, function* () {
286
288
  if (!isProdAppID(appId)) {
287
289
  const prodAppId = getDeployedAppID(appId);
288
290
  // if prod exists, then don't allow deleting
@@ -297,12 +299,17 @@ exports.cleanupAttachments = (appId, table, { row, rows, oldRow }) => __awaiter(
297
299
  files = files.concat(row[key].map(attachment => attachment.key));
298
300
  }
299
301
  }
300
- for (let [key, schema] of Object.entries(table.schema)) {
302
+ const schemaToUse = oldTable ? oldTable.schema : table.schema;
303
+ for (let [key, schema] of Object.entries(schemaToUse)) {
301
304
  if (schema.type !== FieldTypes.ATTACHMENT) {
302
305
  continue;
303
306
  }
304
- // if updating, need to manage the differences
305
- if (oldRow && row) {
307
+ // old table had this column, new table doesn't - delete it
308
+ if (oldTable && !table.schema[key]) {
309
+ rows.forEach(row => addFiles(row, key));
310
+ }
311
+ else if (oldRow && row) {
312
+ // if updating, need to manage the differences
306
313
  files = files.concat(getRemovedAttachmentKeys(oldRow, row, key));
307
314
  }
308
315
  else if (row) {
@@ -1,20 +1,30 @@
1
1
  "use strict";
2
- const { FieldTypes } = require("../../constants");
2
+ const { FieldTypes, FormulaTypes } = require("../../constants");
3
3
  const { processStringSync } = require("@budibase/string-templates");
4
4
  /**
5
5
  * Looks through the rows provided and finds formulas - which it then processes.
6
6
  */
7
- exports.processFormulas = (table, rows) => {
7
+ exports.processFormulas = (table, rows, { dynamic, contextRows } = { dynamic: true }) => {
8
8
  const single = !Array.isArray(rows);
9
9
  if (single) {
10
10
  rows = [rows];
11
+ contextRows = contextRows ? [contextRows] : contextRows;
11
12
  }
12
13
  for (let [column, schema] of Object.entries(table.schema)) {
13
- if (schema.type !== FieldTypes.FORMULA) {
14
+ const isStatic = schema.formulaType === FormulaTypes.STATIC;
15
+ if (schema.type !== FieldTypes.FORMULA ||
16
+ (dynamic && isStatic) ||
17
+ (!dynamic && !isStatic)) {
14
18
  continue;
15
19
  }
16
20
  // iterate through rows and process formula
17
- rows = rows.map(row => (Object.assign(Object.assign({}, row), { [column]: processStringSync(schema.formula, row) })));
21
+ for (let i = 0; i < rows.length; i++) {
22
+ if (schema.formula) {
23
+ let row = rows[i];
24
+ let context = contextRows ? contextRows[i] : row;
25
+ rows[i] = Object.assign(Object.assign({}, row), { [column]: processStringSync(schema.formula, context) });
26
+ }
27
+ }
18
28
  }
19
29
  return single ? rows[0] : rows;
20
30
  };
@@ -8,12 +8,24 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- const env = require("../environment");
12
- const { getGlobalDB } = require("@budibase/backend-core/tenancy");
11
+ const env = require("../../environment");
12
+ const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy");
13
13
  const { StaticDatabases, generateNewUsageQuotaDoc, } = require("@budibase/backend-core/db");
14
- function getNewQuotaReset() {
15
- return Date.now() + 2592000000;
16
- }
14
+ exports.useQuotas = () => {
15
+ // check if quotas are enabled
16
+ if (env.USE_QUOTAS) {
17
+ // check if there are any tenants without limits
18
+ if (env.EXCLUDE_QUOTAS_TENANTS) {
19
+ const excludedTenants = env.EXCLUDE_QUOTAS_TENANTS.replace(/\s/g, "").split(",");
20
+ const tenantId = getTenantId();
21
+ if (excludedTenants.includes(tenantId)) {
22
+ return false;
23
+ }
24
+ }
25
+ return true;
26
+ }
27
+ return false;
28
+ };
17
29
  exports.Properties = {
18
30
  ROW: "rows",
19
31
  UPLOAD: "storage",
@@ -21,21 +33,21 @@ exports.Properties = {
21
33
  USER: "users",
22
34
  AUTOMATION: "automationRuns",
23
35
  APPS: "apps",
24
- EMAILS: "emails",
36
+ EMAILS: "emails", // doesn't work yet
25
37
  };
26
- function getUsageQuotaDoc(db) {
27
- return __awaiter(this, void 0, void 0, function* () {
28
- let quota;
29
- try {
30
- quota = yield db.get(StaticDatabases.PLATFORM_INFO.docs.usageQuota);
31
- }
32
- catch (err) {
33
- // doc doesn't exist. Create it
34
- quota = yield db.post(generateNewUsageQuotaDoc());
35
- }
36
- return quota;
37
- });
38
- }
38
+ exports.getUsageQuotaDoc = (db) => __awaiter(void 0, void 0, void 0, function* () {
39
+ let quota;
40
+ try {
41
+ quota = yield db.get(StaticDatabases.GLOBAL.docs.usageQuota);
42
+ }
43
+ catch (err) {
44
+ // doc doesn't exist. Create it
45
+ quota = generateNewUsageQuotaDoc();
46
+ const response = yield db.put(quota);
47
+ quota._rev = response.rev;
48
+ }
49
+ return quota;
50
+ });
39
51
  /**
40
52
  * Given a specified tenantId this will add to the usage object for the specified property.
41
53
  * @param {string} property The property which is to be added to (within the nested usageQuota object).
@@ -43,27 +55,28 @@ function getUsageQuotaDoc(db) {
43
55
  * @returns {Promise<void>} When this completes the API key will now be up to date - the quota period may have
44
56
  * also been reset after this call.
45
57
  */
46
- exports.update = (property, usage) => __awaiter(void 0, void 0, void 0, function* () {
47
- if (!env.USE_QUOTAS) {
58
+ exports.update = (property, usage, opts = { dryRun: false }) => __awaiter(void 0, void 0, void 0, function* () {
59
+ if (!exports.useQuotas()) {
48
60
  return;
49
61
  }
50
62
  try {
51
63
  const db = getGlobalDB();
52
- const quota = yield getUsageQuotaDoc(db);
53
- // Check if the quota needs reset
54
- if (Date.now() >= quota.quotaReset) {
55
- quota.quotaReset = getNewQuotaReset();
56
- for (let prop of Object.keys(quota.usageQuota)) {
57
- quota.usageQuota[prop] = 0;
58
- }
59
- }
64
+ const quota = yield exports.getUsageQuotaDoc(db);
60
65
  // increment the quota
61
66
  quota.usageQuota[property] += usage;
62
- if (quota.usageQuota[property] > quota.usageLimits[property]) {
67
+ if (quota.usageQuota[property] > quota.usageLimits[property] &&
68
+ usage > 0 // allow for decrementing usage when the quota is already exceeded
69
+ ) {
63
70
  throw new Error(`You have exceeded your usage quota of ${quota.usageLimits[property]} ${property}.`);
64
71
  }
72
+ if (quota.usageQuota[property] < 0) {
73
+ // never go negative if the quota has previously been exceeded
74
+ quota.usageQuota[property] = 0;
75
+ }
65
76
  // update the usage quotas
66
- yield db.put(quota);
77
+ if (!opts.dryRun) {
78
+ yield db.put(quota);
79
+ }
67
80
  }
68
81
  catch (err) {
69
82
  console.error(`Error updating usage quotas for ${property}`, err);
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ const { getRowParams, USER_METDATA_PREFIX } = require("../../db/utils");
12
+ const CouchDB = require("../../db");
13
+ const { isDevAppID, getDevelopmentAppID } = require("@budibase/backend-core/db");
14
+ const ROW_EXCLUSIONS = [USER_METDATA_PREFIX];
15
+ const getAppPairs = appIds => {
16
+ // collect the app ids into dev / prod pairs
17
+ // keyed by the dev app id
18
+ const pairs = {};
19
+ for (let appId of appIds) {
20
+ const devId = getDevelopmentAppID(appId);
21
+ if (!pairs[devId]) {
22
+ pairs[devId] = {};
23
+ }
24
+ if (isDevAppID(appId)) {
25
+ pairs[devId].devId = appId;
26
+ }
27
+ else {
28
+ pairs[devId].prodId = appId;
29
+ }
30
+ }
31
+ return pairs;
32
+ };
33
+ const getAppRows = (appId) => __awaiter(void 0, void 0, void 0, function* () {
34
+ const appDb = new CouchDB(appId);
35
+ const response = yield appDb.allDocs(getRowParams(null, null, {
36
+ include_docs: false,
37
+ }));
38
+ return response.rows
39
+ .map(r => r.id)
40
+ .filter(id => {
41
+ for (let exclusion of ROW_EXCLUSIONS) {
42
+ if (id.startsWith(exclusion)) {
43
+ return false;
44
+ }
45
+ }
46
+ return true;
47
+ });
48
+ });
49
+ /**
50
+ * Return a set of all rows in the given app ids.
51
+ * The returned rows will be unique on a per dev/prod app basis.
52
+ * Rows duplicates may exist across apps due to data import so they are not filtered out.
53
+ */
54
+ exports.getUniqueRows = (appIds) => __awaiter(void 0, void 0, void 0, function* () {
55
+ let uniqueRows = [];
56
+ const pairs = getAppPairs(appIds);
57
+ for (let pair of Object.values(pairs)) {
58
+ let appRows = [];
59
+ for (let appId of [pair.devId, pair.prodId]) {
60
+ if (!appId) {
61
+ continue;
62
+ }
63
+ try {
64
+ appRows.push(yield getAppRows(appId));
65
+ }
66
+ catch (e) {
67
+ console.error(e);
68
+ // don't error out if we can't count the app rows, just continue
69
+ }
70
+ }
71
+ // ensure uniqueness on a per app pair basis
72
+ // this can't be done on all rows because app import results in
73
+ // duplicate row ids across apps
74
+ uniqueRows = uniqueRows.concat(...new Set(appRows));
75
+ }
76
+ return uniqueRows;
77
+ });
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ // UNUSED CODE
3
+ // Preserved for future use
4
+ /* eslint-disable no-unused-vars */
5
+ function getNewQuotaReset() {
6
+ return Date.now() + 2592000000;
7
+ }
8
+ function resetQuotasIfRequired(quota) {
9
+ // Check if the quota needs reset
10
+ if (Date.now() >= quota.quotaReset) {
11
+ quota.quotaReset = getNewQuotaReset();
12
+ for (let prop of Object.keys(quota.usageQuota)) {
13
+ quota.usageQuota[prop] = 0;
14
+ }
15
+ }
16
+ }
@@ -61,26 +61,6 @@ exports.sendSmtpEmail = (to, from, subject, contents, automation) => __awaiter(v
61
61
  }
62
62
  return response.json();
63
63
  });
64
- exports.getDeployedApps = () => __awaiter(void 0, void 0, void 0, function* () {
65
- try {
66
- const response = yield fetch(checkSlashesInUrl(env.WORKER_URL + `/api/apps`), request(null, {
67
- method: "GET",
68
- }));
69
- const json = yield response.json();
70
- const apps = {};
71
- for (let [key, value] of Object.entries(json)) {
72
- if (value.url) {
73
- value.url = value.url.toLowerCase();
74
- apps[key.toLowerCase()] = value;
75
- }
76
- }
77
- return apps;
78
- }
79
- catch (err) {
80
- // error, cannot determine deployed apps, don't stop app creation - sort this later
81
- return {};
82
- }
83
- });
84
64
  exports.getGlobalSelf = (ctx, appId = null) => __awaiter(void 0, void 0, void 0, function* () {
85
65
  const endpoint = `/api/global/users/self`;
86
66
  const response = yield fetch(checkSlashesInUrl(env.WORKER_URL + endpoint),
package/nodemon.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "watch": ["src", "../auth"],
2
+ "watch": ["src", "../backend-core"],
3
3
  "ext": "js,ts,json",
4
4
  "ignore": ["src/**/*.spec.ts", "src/**/*.spec.js"],
5
5
  "exec": "ts-node src/index.ts"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/server",
3
3
  "email": "hi@budibase.com",
4
- "version": "1.0.48",
4
+ "version": "1.0.49-alpha.3",
5
5
  "description": "Budibase Web Server",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -70,9 +70,9 @@
70
70
  "license": "GPL-3.0",
71
71
  "dependencies": {
72
72
  "@apidevtools/swagger-parser": "^10.0.3",
73
- "@budibase/backend-core": "^1.0.48",
74
- "@budibase/client": "^1.0.48",
75
- "@budibase/string-templates": "^1.0.48",
73
+ "@budibase/backend-core": "^1.0.49-alpha.3",
74
+ "@budibase/client": "^1.0.49-alpha.3",
75
+ "@budibase/string-templates": "^1.0.49-alpha.3",
76
76
  "@bull-board/api": "^3.7.0",
77
77
  "@bull-board/koa": "^3.7.0",
78
78
  "@elastic/elasticsearch": "7.10.0",
@@ -92,6 +92,8 @@
92
92
  "fix-path": "3.0.0",
93
93
  "form-data": "^4.0.0",
94
94
  "fs-extra": "8.1.0",
95
+ "google-auth-library": "^7.11.0",
96
+ "google-spreadsheet": "^3.2.0",
95
97
  "jimp": "0.16.1",
96
98
  "joi": "17.2.1",
97
99
  "js-yaml": "^4.1.0",
@@ -110,7 +112,7 @@
110
112
  "mongodb": "3.6.3",
111
113
  "mssql": "6.2.3",
112
114
  "mysql2": "^2.3.1",
113
- "node-fetch": "2.6.0",
115
+ "node-fetch": "2.6.7",
114
116
  "open": "^8.4.0",
115
117
  "pg": "8.5.1",
116
118
  "pino-pretty": "4.0.0",
@@ -139,6 +141,7 @@
139
141
  "@jest/test-sequencer": "^24.8.0",
140
142
  "@types/apidoc": "^0.50.0",
141
143
  "@types/bull": "^3.15.1",
144
+ "@types/google-spreadsheet": "^3.1.5",
142
145
  "@types/jest": "^26.0.23",
143
146
  "@types/koa": "^2.13.3",
144
147
  "@types/koa-router": "^7.4.2",
@@ -165,5 +168,5 @@
165
168
  "optionalDependencies": {
166
169
  "oracledb": "^5.3.0"
167
170
  },
168
- "gitHead": "a5f51d83ba37e2361daf4ff579bb039a2247dd5e"
171
+ "gitHead": "9a271e74c17536fc00aabf0d4672012a28aa52b0"
169
172
  }
@@ -33,10 +33,7 @@ const {
33
33
  Replication,
34
34
  } = require("@budibase/backend-core/db")
35
35
  const { USERS_TABLE_SCHEMA } = require("../../constants")
36
- const {
37
- getDeployedApps,
38
- removeAppFromUserRoles,
39
- } = require("../../utilities/workerRequests")
36
+ const { removeAppFromUserRoles } = require("../../utilities/workerRequests")
40
37
  const { clientLibraryPath, stringToReadStream } = require("../../utilities")
41
38
  const { getAllLocks } = require("../../utilities/redis")
42
39
  const {
@@ -78,29 +75,42 @@ function getUserRoleId(ctx) {
78
75
  : ctx.user.role._id
79
76
  }
80
77
 
81
- async function getAppUrlIfNotInUse(ctx) {
78
+ exports.getAppUrl = ctx => {
79
+ // construct the url
82
80
  let url
83
81
  if (ctx.request.body.url) {
82
+ // if the url is provided, use that
84
83
  url = encodeURI(ctx.request.body.url)
85
84
  } else if (ctx.request.body.name) {
85
+ // otherwise use the name
86
86
  url = encodeURI(`${ctx.request.body.name}`)
87
87
  }
88
88
  if (url) {
89
89
  url = `/${url.replace(URL_REGEX_SLASH, "")}`.toLowerCase()
90
90
  }
91
- if (!env.SELF_HOSTED) {
92
- return url
91
+ return url
92
+ }
93
+
94
+ const checkAppUrl = (ctx, apps, url, currentAppId) => {
95
+ if (currentAppId) {
96
+ apps = apps.filter(app => app.appId !== currentAppId)
93
97
  }
94
- const deployedApps = await getDeployedApps()
95
- if (
96
- url &&
97
- deployedApps[url] != null &&
98
- ctx.params != null &&
99
- deployedApps[url].appId !== ctx.params.appId
100
- ) {
101
- ctx.throw(400, "App name/URL is already in use.")
98
+ if (apps.some(app => app.url === url)) {
99
+ ctx.throw(400, "App URL is already in use.")
100
+ }
101
+ }
102
+
103
+ const checkAppName = (ctx, apps, name, currentAppId) => {
104
+ // TODO: Replace with Joi
105
+ if (!name) {
106
+ ctx.throw(400, "Name is required")
107
+ }
108
+ if (currentAppId) {
109
+ apps = apps.filter(app => app.appId !== currentAppId)
110
+ }
111
+ if (apps.some(app => app.name === name)) {
112
+ ctx.throw(400, "App name is already in use.")
102
113
  }
103
- return url
104
114
  }
105
115
 
106
116
  async function createInstance(template) {
@@ -206,6 +216,12 @@ exports.fetchAppPackage = async ctx => {
206
216
  }
207
217
 
208
218
  exports.create = async ctx => {
219
+ const apps = await getAllApps(CouchDB, { dev: true })
220
+ const name = ctx.request.body.name
221
+ checkAppName(ctx, apps, name)
222
+ const url = exports.getAppUrl(ctx)
223
+ checkAppUrl(ctx, apps, url)
224
+
209
225
  const { useTemplate, templateKey, templateString } = ctx.request.body
210
226
  const instanceConfig = {
211
227
  useTemplate,
@@ -218,7 +234,6 @@ exports.create = async ctx => {
218
234
  const instance = await createInstance(instanceConfig)
219
235
  const appId = instance._id
220
236
 
221
- const url = await getAppUrlIfNotInUse(ctx)
222
237
  const db = new CouchDB(appId)
223
238
  let _rev
224
239
  try {
@@ -235,7 +250,7 @@ exports.create = async ctx => {
235
250
  type: "app",
236
251
  version: packageJson.version,
237
252
  componentLibraries: ["@budibase/standard-components"],
238
- name: ctx.request.body.name,
253
+ name: name,
239
254
  url: url,
240
255
  template: ctx.request.body.template,
241
256
  instance: instance,
@@ -262,8 +277,22 @@ exports.create = async ctx => {
262
277
  ctx.body = newApplication
263
278
  }
264
279
 
280
+ // This endpoint currently operates as a PATCH rather than a PUT
281
+ // Thus name and url fields are handled only if present
265
282
  exports.update = async ctx => {
266
- const data = await updateAppPackage(ctx, ctx.request.body, ctx.params.appId)
283
+ const apps = await getAllApps(CouchDB, { dev: true })
284
+ // validation
285
+ const name = ctx.request.body.name
286
+ if (name) {
287
+ checkAppName(ctx, apps, name, ctx.params.appId)
288
+ }
289
+ const url = await exports.getAppUrl(ctx)
290
+ if (url) {
291
+ checkAppUrl(ctx, apps, url, ctx.params.appId)
292
+ ctx.request.body.url = url
293
+ }
294
+
295
+ const data = await updateAppPackage(ctx.request.body, ctx.params.appId)
267
296
  ctx.status = 200
268
297
  ctx.body = data
269
298
  }
@@ -285,7 +314,7 @@ exports.updateClient = async ctx => {
285
314
  version: packageJson.version,
286
315
  revertableVersion: currentVersion,
287
316
  }
288
- const data = await updateAppPackage(ctx, appPackageUpdates, ctx.params.appId)
317
+ const data = await updateAppPackage(appPackageUpdates, ctx.params.appId)
289
318
  ctx.status = 200
290
319
  ctx.body = data
291
320
  }
@@ -308,7 +337,7 @@ exports.revertClient = async ctx => {
308
337
  version: application.revertableVersion,
309
338
  revertableVersion: null,
310
339
  }
311
- const data = await updateAppPackage(ctx, appPackageUpdates, ctx.params.appId)
340
+ const data = await updateAppPackage(appPackageUpdates, ctx.params.appId)
312
341
  ctx.status = 200
313
342
  ctx.body = data
314
343
  }
@@ -381,12 +410,11 @@ exports.sync = async (ctx, next) => {
381
410
  }
382
411
  }
383
412
 
384
- const updateAppPackage = async (ctx, appPackage, appId) => {
385
- const url = await getAppUrlIfNotInUse(ctx)
413
+ const updateAppPackage = async (appPackage, appId) => {
386
414
  const db = new CouchDB(appId)
387
415
  const application = await db.get(DocumentTypes.APP_METADATA)
388
416
 
389
- const newAppPackage = { ...application, ...appPackage, url }
417
+ const newAppPackage = { ...application, ...appPackage }
390
418
  if (appPackage._rev !== application._rev) {
391
419
  newAppPackage._rev = application._rev
392
420
  }
@@ -16,6 +16,8 @@ exports.fetchSelf = async ctx => {
16
16
  const user = await getFullUser(ctx, userId)
17
17
  // this shouldn't be returned by the app self
18
18
  delete user.roles
19
+ // forward the csrf token from the session
20
+ user.csrfToken = ctx.user.csrfToken
19
21
 
20
22
  if (appId) {
21
23
  const db = new CouchDB(appId)
@@ -24,6 +26,8 @@ exports.fetchSelf = async ctx => {
24
26
  try {
25
27
  const userTable = await db.get(InternalTables.USER_METADATA)
26
28
  const metadata = await db.get(userId)
29
+ // make sure there is never a stale csrf token
30
+ delete metadata.csrfToken
27
31
  // specifically needs to make sure is enriched
28
32
  ctx.body = await outputProcessing(ctx, userTable, {
29
33
  ...user,
@@ -38,6 +38,13 @@ exports.fetch = async function (ctx) {
38
38
  )
39
39
  ).rows.map(row => row.doc)
40
40
 
41
+ for (let datasource of datasources) {
42
+ if (datasource.config && datasource.config.auth) {
43
+ // strip secrets from response so they don't show in the network request
44
+ delete datasource.config.auth
45
+ }
46
+ }
47
+
41
48
  ctx.body = [bbInternalDb, ...datasources]
42
49
  }
43
50
 
@@ -94,8 +101,13 @@ exports.update = async function (ctx) {
94
101
  const db = new CouchDB(ctx.appId)
95
102
  const datasourceId = ctx.params.datasourceId
96
103
  let datasource = await db.get(datasourceId)
104
+ const auth = datasource.config.auth
97
105
  await invalidateVariables(datasource, ctx.request.body)
98
106
  datasource = { ...datasource, ...ctx.request.body }
107
+ if (auth && !ctx.request.body.auth) {
108
+ // don't strip auth config from DB
109
+ datasource.config.auth = auth
110
+ }
99
111
 
100
112
  const response = await db.put(datasource)
101
113
  datasource._rev = response.rev
@@ -0,0 +1,13 @@
1
+ const { migrate, MIGRATIONS } = require("../../migrations")
2
+
3
+ exports.migrate = async ctx => {
4
+ const options = ctx.request.body
5
+ // don't await as can take a while, just return
6
+ migrate(options)
7
+ ctx.status = 200
8
+ }
9
+
10
+ exports.fetchDefinitions = async ctx => {
11
+ ctx.body = MIGRATIONS
12
+ ctx.status = 200
13
+ }