@budibase/server 1.0.49-alpha.0 → 1.0.49-alpha.4

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 (239) hide show
  1. package/builder/assets/{index.dd9e386f.css → index.83c0944f.css} +1 -1
  2. package/builder/assets/{index.8184eed9.js → index.f763d886.js} +206 -205
  3. package/builder/assets/{vendor.3bd2f661.js → vendor.d526bfe8.js} +1 -1
  4. package/builder/index.html +3 -3
  5. package/coverage/clover.xml +1743 -1610
  6. package/coverage/coverage-final.json +90 -89
  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 +106 -106
  10. package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
  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 +1 -1
  15. package/coverage/lcov-report/api/controllers/datasource.js.html +16 -16
  16. package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/index.html +27 -12
  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/{migrations/usageQuotas/syncApps.js.html → api/controllers/migrations.js.html} +30 -45
  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 +40 -25
  30. package/coverage/lcov-report/api/controllers/row/index.js.html +13 -13
  31. package/coverage/lcov-report/api/controllers/row/internal.js.html +83 -146
  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 +13 -13
  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 +45 -30
  40. package/coverage/lcov-report/api/controllers/table/index.js.html +23 -65
  41. package/coverage/lcov-report/api/controllers/table/internal.js.html +33 -15
  42. package/coverage/lcov-report/api/controllers/table/utils.js.html +161 -41
  43. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  44. package/coverage/lcov-report/api/controllers/user.js.html +20 -20
  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 +3 -3
  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 +22 -7
  65. package/coverage/lcov-report/api/routes/index.js.html +10 -4
  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/{migrations/usageQuotas/index.js.html → api/routes/migrations.js.html} +25 -55
  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 +1 -1
  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 +7 -7
  90. package/coverage/lcov-report/automations/steps/delay.js.html +4 -4
  91. package/coverage/lcov-report/automations/steps/deleteRow.js.html +7 -7
  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 +1 -1
  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 +42 -27
  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 +1 -1
  123. package/coverage/lcov-report/db/index.js.html +3 -3
  124. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +7 -7
  125. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  126. package/coverage/lcov-report/db/linkedRows/index.js.html +26 -29
  127. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +13 -13
  128. package/coverage/lcov-report/db/newid.js.html +4 -4
  129. package/coverage/lcov-report/db/utils.js.html +71 -71
  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 +61 -76
  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 +1 -1
  139. package/coverage/lcov-report/middleware/builder.js.html +1 -1
  140. package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
  141. package/coverage/lcov-report/middleware/index.html +11 -11
  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 +6 -12
  146. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  147. package/coverage/lcov-report/threads/automation.js.html +1 -1
  148. package/coverage/lcov-report/threads/index.html +1 -1
  149. package/coverage/lcov-report/threads/index.js.html +10 -10
  150. package/coverage/lcov-report/threads/query.js.html +1 -1
  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 +14 -14
  157. package/coverage/lcov-report/utilities/index.html +1 -1
  158. package/coverage/lcov-report/utilities/index.js.html +24 -24
  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 +80 -44
  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 +1 -1
  170. package/coverage/lcov-report/utilities/usageQuota/index.js.html +10 -10
  171. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +1 -1
  172. package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +1 -1
  173. package/coverage/lcov-report/utilities/users.js.html +5 -5
  174. package/coverage/lcov-report/utilities/workerRequests.js.html +12 -12
  175. package/coverage/lcov.info +2545 -2257
  176. package/dist/api/controllers/application.js +18 -20
  177. package/dist/api/controllers/migrations.js +21 -0
  178. package/dist/api/controllers/row/internal.js +30 -53
  179. package/dist/api/controllers/row/internalSearch.js +12 -4
  180. package/dist/api/controllers/row/staticFormula.js +147 -0
  181. package/dist/api/controllers/table/bulkFormula.js +160 -0
  182. package/dist/api/controllers/table/index.js +3 -6
  183. package/dist/api/controllers/table/internal.js +7 -0
  184. package/dist/api/controllers/table/utils.js +32 -10
  185. package/dist/api/routes/index.js +2 -0
  186. package/dist/api/routes/migrations.js +9 -0
  187. package/dist/app.js +35 -5
  188. package/dist/constants/index.js +4 -0
  189. package/dist/db/linkedRows/index.js +4 -5
  190. package/dist/environment.js +1 -0
  191. package/dist/middleware/usageQuota.js +0 -2
  192. package/dist/migrations/functions/appUrls.js +46 -0
  193. package/dist/migrations/functions/quotas1.js +50 -0
  194. package/dist/migrations/{usageQuotas → functions/usageQuotas}/index.js +6 -10
  195. package/dist/migrations/{usageQuotas → functions/usageQuotas}/syncApps.js +11 -5
  196. package/dist/migrations/{usageQuotas → functions/usageQuotas}/syncRows.js +14 -8
  197. package/dist/migrations/functions/userEmailViewCasing.js +24 -0
  198. package/dist/migrations/index.js +62 -0
  199. package/dist/package.json +4 -4
  200. package/dist/tests/utilities/TestConfiguration.js +3 -0
  201. package/dist/tsconfig.tsbuildinfo +1 -1
  202. package/dist/utilities/rowProcessor/index.js +18 -11
  203. package/dist/utilities/rowProcessor/utils.js +14 -4
  204. package/package.json +5 -5
  205. package/src/api/controllers/application.js +3 -3
  206. package/src/api/controllers/migrations.js +13 -0
  207. package/src/api/controllers/row/internal.js +29 -50
  208. package/src/api/controllers/row/internalSearch.js +12 -4
  209. package/src/api/controllers/row/staticFormula.js +157 -0
  210. package/src/api/controllers/table/bulkFormula.js +183 -0
  211. package/src/api/controllers/table/index.js +3 -17
  212. package/src/api/controllers/table/internal.js +7 -1
  213. package/src/api/controllers/table/utils.js +48 -8
  214. package/src/api/routes/index.js +2 -0
  215. package/src/api/routes/migrations.js +14 -0
  216. package/src/app.ts +18 -5
  217. package/src/constants/index.js +5 -0
  218. package/src/db/linkedRows/index.js +4 -5
  219. package/src/definitions/common.ts +3 -0
  220. package/src/environment.js +1 -0
  221. package/src/middleware/usageQuota.js +0 -2
  222. package/src/migrations/functions/appUrls.ts +34 -0
  223. package/src/migrations/functions/quotas1.ts +20 -0
  224. package/src/migrations/functions/tests/appUrls.spec.js +29 -0
  225. package/src/migrations/{tests/usageQuotas/index.spec.js → functions/tests/quotas1.spec.js} +5 -5
  226. package/src/migrations/functions/tests/userEmailViewCasing.spec.js +25 -0
  227. package/src/migrations/functions/usageQuotas/index.ts +8 -0
  228. package/src/migrations/{usageQuotas/syncApps.js → functions/usageQuotas/syncApps.ts} +3 -3
  229. package/src/migrations/{usageQuotas/syncRows.js → functions/usageQuotas/syncRows.ts} +5 -5
  230. package/src/migrations/{tests/usageQuotas → functions/usageQuotas/tests}/syncApps.spec.js +4 -4
  231. package/src/migrations/{tests/usageQuotas → functions/usageQuotas/tests}/syncRows.spec.js +4 -4
  232. package/src/migrations/functions/userEmailViewCasing.ts +13 -0
  233. package/src/migrations/index.ts +56 -0
  234. package/src/tests/utilities/TestConfiguration.js +4 -0
  235. package/src/utilities/rowProcessor/index.js +24 -12
  236. package/src/utilities/rowProcessor/utils.js +23 -7
  237. package/coverage/lcov-report/migrations/usageQuotas/index.html +0 -141
  238. package/coverage/lcov-report/migrations/usageQuotas/syncRows.js.html +0 -143
  239. package/src/migrations/usageQuotas/index.js +0 -24
@@ -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
  };
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.49-alpha.0",
4
+ "version": "1.0.49-alpha.4",
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.49-alpha.0",
74
- "@budibase/client": "^1.0.49-alpha.0",
75
- "@budibase/string-templates": "^1.0.49-alpha.0",
73
+ "@budibase/backend-core": "^1.0.49-alpha.4",
74
+ "@budibase/client": "^1.0.49-alpha.4",
75
+ "@budibase/string-templates": "^1.0.49-alpha.4",
76
76
  "@bull-board/api": "^3.7.0",
77
77
  "@bull-board/koa": "^3.7.0",
78
78
  "@elastic/elasticsearch": "7.10.0",
@@ -168,5 +168,5 @@
168
168
  "optionalDependencies": {
169
169
  "oracledb": "^5.3.0"
170
170
  },
171
- "gitHead": "27d295ae310158a0a240fe62c45362ad03f82115"
171
+ "gitHead": "c991f284e3be5257091f234bdde5abaf1d53d2be"
172
172
  }
@@ -75,7 +75,7 @@ function getUserRoleId(ctx) {
75
75
  : ctx.user.role._id
76
76
  }
77
77
 
78
- async function getAppUrl(ctx) {
78
+ exports.getAppUrl = ctx => {
79
79
  // construct the url
80
80
  let url
81
81
  if (ctx.request.body.url) {
@@ -219,7 +219,7 @@ exports.create = async ctx => {
219
219
  const apps = await getAllApps(CouchDB, { dev: true })
220
220
  const name = ctx.request.body.name
221
221
  checkAppName(ctx, apps, name)
222
- const url = await getAppUrl(ctx)
222
+ const url = exports.getAppUrl(ctx)
223
223
  checkAppUrl(ctx, apps, url)
224
224
 
225
225
  const { useTemplate, templateKey, templateString } = ctx.request.body
@@ -286,7 +286,7 @@ exports.update = async ctx => {
286
286
  if (name) {
287
287
  checkAppName(ctx, apps, name, ctx.params.appId)
288
288
  }
289
- const url = await getAppUrl(ctx)
289
+ const url = await exports.getAppUrl(ctx)
290
290
  if (url) {
291
291
  checkAppUrl(ctx, apps, url, ctx.params.appId)
292
292
  ctx.request.body.url = url
@@ -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
+ }
@@ -1,8 +1,8 @@
1
1
  const CouchDB = require("../../../db")
2
2
  const linkRows = require("../../../db/linkedRows")
3
3
  const {
4
- getRowParams,
5
4
  generateRowID,
5
+ getRowParams,
6
6
  DocumentTypes,
7
7
  InternalTables,
8
8
  } = require("../../../db/utils")
@@ -10,11 +10,9 @@ const userController = require("../user")
10
10
  const {
11
11
  inputProcessing,
12
12
  outputProcessing,
13
- processAutoColumn,
14
13
  cleanupAttachments,
15
14
  } = require("../../../utilities/rowProcessor")
16
15
  const { FieldTypes } = require("../../../constants")
17
- const { isEqual } = require("lodash")
18
16
  const { validate, findRow } = require("./utils")
19
17
  const { fullSearch, paginatedSearch } = require("./internalSearch")
20
18
  const { getGlobalUsersFromMetadata } = require("../../../utilities/global")
@@ -27,6 +25,7 @@ const {
27
25
  getFromMemoryDoc,
28
26
  } = require("../view/utils")
29
27
  const { cloneDeep } = require("lodash/fp")
28
+ const { finaliseRow, updateRelatedFormula } = require("./staticFormula")
30
29
 
31
30
  const CALCULATION_TYPES = {
32
31
  SUM: "sum",
@@ -34,51 +33,6 @@ const CALCULATION_TYPES = {
34
33
  STATS: "stats",
35
34
  }
36
35
 
37
- async function storeResponse(ctx, db, row, oldTable, table) {
38
- row.type = "row"
39
- // don't worry about rev, tables handle rev/lastID updates
40
- // if another row has been written since processing this will
41
- // handle the auto ID clash
42
- if (!isEqual(oldTable, table)) {
43
- try {
44
- await db.put(table)
45
- } catch (err) {
46
- if (err.status === 409) {
47
- const updatedTable = await db.get(table._id)
48
- let response = processAutoColumn(null, updatedTable, row, {
49
- reprocessing: true,
50
- })
51
- await db.put(response.table)
52
- row = response.row
53
- } else {
54
- throw err
55
- }
56
- }
57
- }
58
- const response = await db.put(row)
59
- row._rev = response.rev
60
- // process the row before return, to include relationships
61
- row = await outputProcessing(ctx, table, row, { squash: false })
62
- return { row, table }
63
- }
64
-
65
- // doesn't do the outputProcessing
66
- async function getRawTableData(ctx, db, tableId) {
67
- let rows
68
- if (tableId === InternalTables.USER_METADATA) {
69
- await userController.fetchMetadata(ctx)
70
- rows = ctx.body
71
- } else {
72
- const response = await db.allDocs(
73
- getRowParams(tableId, null, {
74
- include_docs: true,
75
- })
76
- )
77
- rows = response.rows.map(row => row.doc)
78
- }
79
- return rows
80
- }
81
-
82
36
  async function getView(db, viewName) {
83
37
  let mainGetter = env.SELF_HOSTED ? getFromDesignDoc : getFromMemoryDoc
84
38
  let secondaryGetter = env.SELF_HOSTED ? getFromMemoryDoc : getFromDesignDoc
@@ -105,6 +59,22 @@ async function getView(db, viewName) {
105
59
  return viewInfo
106
60
  }
107
61
 
62
+ async function getRawTableData(ctx, db, tableId) {
63
+ let rows
64
+ if (tableId === InternalTables.USER_METADATA) {
65
+ await userController.fetchMetadata(ctx)
66
+ rows = ctx.body
67
+ } else {
68
+ const response = await db.allDocs(
69
+ getRowParams(tableId, null, {
70
+ include_docs: true,
71
+ })
72
+ )
73
+ rows = response.rows.map(row => row.doc)
74
+ }
75
+ return rows
76
+ }
77
+
108
78
  exports.patch = async ctx => {
109
79
  const appId = ctx.appId
110
80
  const db = new CouchDB(appId)
@@ -162,7 +132,10 @@ exports.patch = async ctx => {
162
132
  return { row: ctx.body, table }
163
133
  }
164
134
 
165
- return storeResponse(ctx, db, row, dbTable, table)
135
+ return finaliseRow(ctx.appId, table, row, {
136
+ oldTable: dbTable,
137
+ updateFormula: true,
138
+ })
166
139
  }
167
140
 
168
141
  exports.save = async function (ctx) {
@@ -196,7 +169,10 @@ exports.save = async function (ctx) {
196
169
  table,
197
170
  })
198
171
 
199
- return storeResponse(ctx, db, row, dbTable, table)
172
+ return finaliseRow(ctx.appId, table, row, {
173
+ oldTable: dbTable,
174
+ updateFormula: true,
175
+ })
200
176
  }
201
177
 
202
178
  exports.fetchView = async ctx => {
@@ -302,6 +278,8 @@ exports.destroy = async function (ctx) {
302
278
  })
303
279
  // remove any attachments that were on the row from object storage
304
280
  await cleanupAttachments(appId, table, { row })
281
+ // remove any static formula
282
+ await updateRelatedFormula(appId, table, row)
305
283
 
306
284
  let response
307
285
  if (ctx.params.tableId === InternalTables.USER_METADATA) {
@@ -350,6 +328,7 @@ exports.bulkDestroy = async ctx => {
350
328
  }
351
329
  // remove any attachments that were on the rows from object storage
352
330
  await cleanupAttachments(appId, table, { rows })
331
+ await updateRelatedFormula(appId, table, rows)
353
332
  await Promise.all(updates)
354
333
  return { response: { ok: true }, rows }
355
334
  }
@@ -37,22 +37,30 @@ class QueryBuilder {
37
37
  }
38
38
 
39
39
  setLimit(limit) {
40
- this.limit = limit
40
+ if (limit != null) {
41
+ this.limit = limit
42
+ }
41
43
  return this
42
44
  }
43
45
 
44
46
  setSort(sort) {
45
- this.sort = sort
47
+ if (sort != null) {
48
+ this.sort = sort
49
+ }
46
50
  return this
47
51
  }
48
52
 
49
53
  setSortOrder(sortOrder) {
50
- this.sortOrder = sortOrder
54
+ if (sortOrder != null) {
55
+ this.sortOrder = sortOrder
56
+ }
51
57
  return this
52
58
  }
53
59
 
54
60
  setSortType(sortType) {
55
- this.sortType = sortType
61
+ if (sortType != null) {
62
+ this.sortType = sortType
63
+ }
56
64
  return this
57
65
  }
58
66
 
@@ -0,0 +1,157 @@
1
+ const CouchDB = require("../../../db")
2
+ const { getRowParams } = require("../../../db/utils")
3
+ const {
4
+ outputProcessing,
5
+ processAutoColumn,
6
+ processFormulas,
7
+ } = require("../../../utilities/rowProcessor")
8
+ const { FieldTypes, FormulaTypes } = require("../../../constants")
9
+ const { isEqual } = require("lodash")
10
+ const { cloneDeep } = require("lodash/fp")
11
+
12
+ /**
13
+ * This function runs through a list of enriched rows, looks at the rows which
14
+ * are related and then checks if they need the state of their formulas
15
+ * updated.
16
+ * NOTE: this will only for affect static formulas.
17
+ */
18
+ exports.updateRelatedFormula = async (appId, table, enrichedRows) => {
19
+ const db = new CouchDB(appId)
20
+ // no formula to update, we're done
21
+ if (!table.relatedFormula) {
22
+ return
23
+ }
24
+ let promises = []
25
+ for (let enrichedRow of Array.isArray(enrichedRows)
26
+ ? enrichedRows
27
+ : [enrichedRows]) {
28
+ // the related rows by tableId
29
+ let relatedRows = {}
30
+ for (let [key, field] of Object.entries(enrichedRow)) {
31
+ const columnDefinition = table.schema[key]
32
+ if (columnDefinition && columnDefinition.type === FieldTypes.LINK) {
33
+ const relatedTableId = columnDefinition.tableId
34
+ if (!relatedRows[relatedTableId]) {
35
+ relatedRows[relatedTableId] = []
36
+ }
37
+ relatedRows[relatedTableId] = relatedRows[relatedTableId].concat(field)
38
+ }
39
+ }
40
+ for (let tableId of table.relatedFormula) {
41
+ let relatedTable
42
+ try {
43
+ // no rows to update, skip
44
+ if (!relatedRows[tableId] || relatedRows[tableId].length === 0) {
45
+ continue
46
+ }
47
+ relatedTable = await db.get(tableId)
48
+ } catch (err) {
49
+ // no error scenario, table doesn't seem to exist anymore, ignore
50
+ }
51
+ for (let column of Object.values(relatedTable.schema)) {
52
+ // needs updated in related rows
53
+ if (
54
+ column.type === FieldTypes.FORMULA &&
55
+ column.formulaType === FormulaTypes.STATIC
56
+ ) {
57
+ // re-enrich rows for all the related, don't update the related formula for them
58
+ promises = promises.concat(
59
+ relatedRows[tableId].map(related =>
60
+ exports.finaliseRow(appId, relatedTable, related, {
61
+ updateFormula: false,
62
+ })
63
+ )
64
+ )
65
+ }
66
+ }
67
+ }
68
+ }
69
+ await Promise.all(promises)
70
+ }
71
+
72
+ exports.updateAllFormulasInTable = async (appId, table) => {
73
+ const db = new CouchDB(appId)
74
+ // start by getting the raw rows (which will be written back to DB after update)
75
+ let rows = (
76
+ await db.allDocs(
77
+ getRowParams(table._id, null, {
78
+ include_docs: true,
79
+ })
80
+ )
81
+ ).rows.map(row => row.doc)
82
+ // now enrich the rows, note the clone so that we have the base state of the
83
+ // rows so that we don't write any of the enriched information back
84
+ let enrichedRows = await outputProcessing({ appId }, table, cloneDeep(rows), {
85
+ squash: false,
86
+ })
87
+ const updatedRows = []
88
+ for (let row of rows) {
89
+ // find the enriched row, if found process the formulas
90
+ const enrichedRow = enrichedRows.find(enriched => enriched._id === row._id)
91
+ if (enrichedRow) {
92
+ const processed = processFormulas(table, cloneDeep(row), {
93
+ dynamic: false,
94
+ contextRows: enrichedRow,
95
+ })
96
+ // values have changed, need to add to bulk docs to update
97
+ if (!isEqual(processed, row)) {
98
+ updatedRows.push(processed)
99
+ }
100
+ }
101
+ }
102
+ await db.bulkDocs(updatedRows)
103
+ }
104
+
105
+ /**
106
+ * This function runs at the end of the save/patch functions of the row controller, all this
107
+ * really does is enrich the row, handle any static formula processing, then return the enriched
108
+ * row. The reason we need to return the enriched row is that the automation row created trigger
109
+ * expects the row to be totally enriched/contain all relationships.
110
+ */
111
+ exports.finaliseRow = async (
112
+ appId,
113
+ table,
114
+ row,
115
+ { oldTable, updateFormula } = { updateFormula: true }
116
+ ) => {
117
+ const db = new CouchDB(appId)
118
+ row.type = "row"
119
+ // process the row before return, to include relationships
120
+ let enrichedRow = await outputProcessing({ appId }, table, cloneDeep(row), {
121
+ squash: false,
122
+ })
123
+ // use enriched row to generate formulas for saving, specifically only use as context
124
+ row = processFormulas(table, row, {
125
+ dynamic: false,
126
+ contextRows: enrichedRow,
127
+ })
128
+
129
+ // don't worry about rev, tables handle rev/lastID updates
130
+ // if another row has been written since processing this will
131
+ // handle the auto ID clash
132
+ if (oldTable && !isEqual(oldTable, table)) {
133
+ try {
134
+ await db.put(table)
135
+ } catch (err) {
136
+ if (err.status === 409) {
137
+ const updatedTable = await db.get(table._id)
138
+ let response = processAutoColumn(null, updatedTable, row, {
139
+ reprocessing: true,
140
+ })
141
+ await db.put(response.table)
142
+ row = response.row
143
+ } else {
144
+ throw err
145
+ }
146
+ }
147
+ }
148
+ const response = await db.put(row)
149
+ // for response, calculate the formulas for the enriched row
150
+ enrichedRow._rev = response.rev
151
+ enrichedRow = await processFormulas(table, enrichedRow, { dynamic: false })
152
+ // this updates the related formulas in other rows based on the relations to this row
153
+ if (updateFormula) {
154
+ await exports.updateRelatedFormula(appId, table, enrichedRow)
155
+ }
156
+ return { row: enrichedRow, table }
157
+ }