@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
@@ -0,0 +1,183 @@
1
+ const CouchDB = require("../../../db")
2
+ const { FieldTypes, FormulaTypes } = require("../../../constants")
3
+ const { getAllInternalTables, clearColumns } = require("./utils")
4
+ const { doesContainStrings } = require("@budibase/string-templates")
5
+ const { cloneDeep } = require("lodash/fp")
6
+ const { isEqual, uniq } = require("lodash")
7
+ const { updateAllFormulasInTable } = require("../row/staticFormula")
8
+
9
+ function isStaticFormula(column) {
10
+ return (
11
+ column.type === FieldTypes.FORMULA &&
12
+ column.formulaType === FormulaTypes.STATIC
13
+ )
14
+ }
15
+
16
+ /**
17
+ * This retrieves the formula columns from a table schema that use a specified column name
18
+ * in the formula.
19
+ */
20
+ function getFormulaThatUseColumn(table, columnNames) {
21
+ let formula = []
22
+ columnNames = Array.isArray(columnNames) ? columnNames : [columnNames]
23
+ for (let column of Object.values(table.schema)) {
24
+ // not a static formula, or doesn't contain a relationship
25
+ if (!isStaticFormula(column)) {
26
+ continue
27
+ }
28
+ if (!doesContainStrings(column.formula, columnNames)) {
29
+ continue
30
+ }
31
+ formula.push(column.name)
32
+ }
33
+ return formula
34
+ }
35
+
36
+ /**
37
+ * This functions checks for when a related table, column or related column is deleted, if any
38
+ * tables need to have the formula column removed.
39
+ */
40
+ async function checkIfFormulaNeedsCleared(
41
+ appId,
42
+ table,
43
+ { oldTable, deletion }
44
+ ) {
45
+ const db = new CouchDB(appId)
46
+ // start by retrieving all tables, remove the current table from the list
47
+ const tables = (await getAllInternalTables(appId)).filter(
48
+ tbl => tbl._id !== table._id
49
+ )
50
+ const schemaToUse = oldTable ? oldTable.schema : table.schema
51
+ let removedColumns = Object.values(schemaToUse).filter(
52
+ column => deletion || !table.schema[column.name]
53
+ )
54
+ // remove any formula columns that used related columns
55
+ for (let removed of removedColumns) {
56
+ let tableToUse = table
57
+ // if relationship, get the related table
58
+ if (removed.type === FieldTypes.LINK) {
59
+ tableToUse = tables.find(table => table._id === removed.tableId)
60
+ }
61
+ const columnsToDelete = getFormulaThatUseColumn(tableToUse, removed.name)
62
+ if (columnsToDelete.length > 0) {
63
+ await clearColumns(db, table, columnsToDelete)
64
+ }
65
+ // need a special case, where a column has been removed from this table, but was used
66
+ // in a different, related tables formula
67
+ if (!table.relatedFormula) {
68
+ continue
69
+ }
70
+ for (let relatedTableId of table.relatedFormula) {
71
+ const relatedColumns = Object.values(table.schema).filter(
72
+ column => column.tableId === relatedTableId
73
+ )
74
+ const relatedTable = tables.find(table => table._id === relatedTableId)
75
+ // look to see if the column was used in a relationship formula,
76
+ // relationships won't be used for this
77
+ if (relatedTable && relatedColumns && removed.type !== FieldTypes.LINK) {
78
+ let relatedFormulaToRemove = []
79
+ for (let column of relatedColumns) {
80
+ relatedFormulaToRemove = relatedFormulaToRemove.concat(
81
+ getFormulaThatUseColumn(relatedTable, [
82
+ column.fieldName,
83
+ removed.name,
84
+ ])
85
+ )
86
+ }
87
+ if (relatedFormulaToRemove.length > 0) {
88
+ await clearColumns(db, relatedTable, uniq(relatedFormulaToRemove))
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
94
+
95
+ /**
96
+ * This function adds a note to related tables that they are
97
+ * used in a static formula - so that the link controller
98
+ * can manage hydrating related rows formula fields. This is
99
+ * specifically only for static formula.
100
+ */
101
+ async function updateRelatedFormulaLinksOnTables(
102
+ appId,
103
+ table,
104
+ { deletion } = { deletion: false }
105
+ ) {
106
+ const db = new CouchDB(appId)
107
+ // start by retrieving all tables, remove the current table from the list
108
+ const tables = (await getAllInternalTables(appId)).filter(
109
+ tbl => tbl._id !== table._id
110
+ )
111
+ // clone the tables, so we can compare at end
112
+ const initialTables = cloneDeep(tables)
113
+ // first find the related column names
114
+ const relatedColumns = Object.values(table.schema).filter(
115
+ col => col.type === FieldTypes.LINK
116
+ )
117
+ // we start by removing the formula field from all tables
118
+ for (let otherTable of tables) {
119
+ if (!otherTable.relatedFormula) {
120
+ continue
121
+ }
122
+ const index = otherTable.relatedFormula.indexOf(table._id)
123
+ if (index !== -1) {
124
+ otherTable.relatedFormula.splice(index, 1)
125
+ }
126
+ }
127
+ // if deleting, just remove the table IDs, don't try add
128
+ if (!deletion) {
129
+ for (let relatedCol of relatedColumns) {
130
+ let columns = getFormulaThatUseColumn(table, relatedCol.name)
131
+ if (!columns || columns.length === 0) {
132
+ continue
133
+ }
134
+ const relatedTable = tables.find(
135
+ related => related._id === relatedCol.tableId
136
+ )
137
+ // check if the table is already in the list of related formula, if it isn't, then add it
138
+ if (
139
+ relatedTable &&
140
+ (!relatedTable.relatedFormula ||
141
+ !relatedTable.relatedFormula.includes(table._id))
142
+ ) {
143
+ relatedTable.relatedFormula = relatedTable.relatedFormula
144
+ ? [...relatedTable.relatedFormula, table._id]
145
+ : [table._id]
146
+ }
147
+ }
148
+ }
149
+ // now we just need to compare all the tables and see if any need saved
150
+ for (let initial of initialTables) {
151
+ const found = tables.find(tbl => initial._id === tbl._id)
152
+ if (found && !isEqual(initial, found)) {
153
+ await db.put(found)
154
+ }
155
+ }
156
+ }
157
+
158
+ async function checkIfFormulaUpdated(appId, table, { oldTable }) {
159
+ // look to see if any formula values have changed
160
+ const shouldUpdate = Object.values(table.schema).find(
161
+ column =>
162
+ isStaticFormula(column) &&
163
+ (!oldTable ||
164
+ !oldTable.schema[column.name] ||
165
+ !isEqual(oldTable.schema[column.name], column))
166
+ )
167
+ // if a static formula column has updated, then need to run the update
168
+ if (shouldUpdate != null) {
169
+ await updateAllFormulasInTable(appId, table)
170
+ }
171
+ }
172
+
173
+ exports.runStaticFormulaChecks = async (
174
+ appId,
175
+ table,
176
+ { oldTable, deletion }
177
+ ) => {
178
+ await updateRelatedFormulaLinksOnTables(appId, table, { deletion })
179
+ await checkIfFormulaNeedsCleared(appId, table, { oldTable, deletion })
180
+ if (!deletion) {
181
+ await checkIfFormulaUpdated(appId, table, { oldTable })
182
+ }
183
+ }
@@ -3,12 +3,8 @@ const internal = require("./internal")
3
3
  const external = require("./external")
4
4
  const csvParser = require("../../../utilities/csvParser")
5
5
  const { isExternalTable, isSQL } = require("../../../integrations/utils")
6
- const {
7
- getTableParams,
8
- getDatasourceParams,
9
- BudibaseInternalDB,
10
- } = require("../../../db/utils")
11
- const { getTable } = require("./utils")
6
+ const { getDatasourceParams } = require("../../../db/utils")
7
+ const { getTable, getAllInternalTables } = require("./utils")
12
8
 
13
9
  function pickApi({ tableId, table }) {
14
10
  if (table && !tableId) {
@@ -26,17 +22,7 @@ function pickApi({ tableId, table }) {
26
22
  exports.fetch = async function (ctx) {
27
23
  const db = new CouchDB(ctx.appId)
28
24
 
29
- const internalTables = await db.allDocs(
30
- getTableParams(null, {
31
- include_docs: true,
32
- })
33
- )
34
-
35
- const internal = internalTables.rows.map(tableDoc => ({
36
- ...tableDoc.doc,
37
- type: "internal",
38
- sourceId: BudibaseInternalDB._id,
39
- }))
25
+ const internal = await getAllInternalTables(ctx.appId)
40
26
 
41
27
  const externalTables = await db.allDocs(
42
28
  getDatasourceParams("plus", {
@@ -9,6 +9,8 @@ const {
9
9
  handleDataImport,
10
10
  } = require("./utils")
11
11
  const usageQuota = require("../../../utilities/usageQuota")
12
+ const { cleanupAttachments } = require("../../../utilities/rowProcessor")
13
+ const { runStaticFormulaChecks } = require("./bulkFormula")
12
14
 
13
15
  exports.save = async function (ctx) {
14
16
  const appId = ctx.appId
@@ -104,7 +106,8 @@ exports.save = async function (ctx) {
104
106
  tableToSave._rev = result.rev
105
107
 
106
108
  tableToSave = await tableSaveFunctions.after(tableToSave)
107
-
109
+ // has to run after, make sure it has _id
110
+ await runStaticFormulaChecks(appId, tableToSave, { oldTable })
108
111
  return tableToSave
109
112
  }
110
113
 
@@ -141,6 +144,9 @@ exports.destroy = async function (ctx) {
141
144
  await db.deleteIndex(existingIndex)
142
145
  }
143
146
 
147
+ // has to run after, make sure it has _id
148
+ await runStaticFormulaChecks(appId, tableToDelete, { deletion: true })
149
+ await cleanupAttachments(appId, tableToDelete, { rows })
144
150
  return tableToDelete
145
151
  }
146
152
 
@@ -4,10 +4,15 @@ const {
4
4
  getRowParams,
5
5
  generateRowID,
6
6
  InternalTables,
7
+ getTableParams,
8
+ BudibaseInternalDB,
7
9
  } = require("../../../db/utils")
8
- const { isEqual } = require("lodash/fp")
10
+ const { isEqual } = require("lodash")
9
11
  const { AutoFieldSubTypes, FieldTypes } = require("../../../constants")
10
- const { inputProcessing } = require("../../../utilities/rowProcessor")
12
+ const {
13
+ inputProcessing,
14
+ cleanupAttachments,
15
+ } = require("../../../utilities/rowProcessor")
11
16
  const {
12
17
  USERS_TABLE_SCHEMA,
13
18
  SwitchableTypes,
@@ -21,8 +26,24 @@ const {
21
26
  const { getViews, saveView } = require("../view/utils")
22
27
  const viewTemplate = require("../view/viewBuilder")
23
28
  const usageQuota = require("../../../utilities/usageQuota")
29
+ const { cloneDeep } = require("lodash/fp")
30
+
31
+ exports.clearColumns = async (appId, table, columnNames) => {
32
+ const db = new CouchDB(appId)
33
+ const rows = await db.allDocs(
34
+ getRowParams(table._id, null, {
35
+ include_docs: true,
36
+ })
37
+ )
38
+ return db.bulkDocs(
39
+ rows.rows.map(({ doc }) => {
40
+ columnNames.forEach(colName => delete doc[colName])
41
+ return doc
42
+ })
43
+ )
44
+ }
24
45
 
25
- exports.checkForColumnUpdates = async (db, oldTable, updatedTable) => {
46
+ exports.checkForColumnUpdates = async (appId, db, oldTable, updatedTable) => {
26
47
  let updatedRows = []
27
48
  const rename = updatedTable._rename
28
49
  let deletedColumns = []
@@ -39,16 +60,20 @@ exports.checkForColumnUpdates = async (db, oldTable, updatedTable) => {
39
60
  include_docs: true,
40
61
  })
41
62
  )
42
- updatedRows = rows.rows.map(({ doc }) => {
63
+ const rawRows = rows.rows.map(({ doc }) => doc)
64
+ updatedRows = rawRows.map(row => {
65
+ row = cloneDeep(row)
43
66
  if (rename) {
44
- doc[rename.updated] = doc[rename.old]
45
- delete doc[rename.old]
67
+ row[rename.updated] = row[rename.old]
68
+ delete row[rename.old]
46
69
  } else if (deletedColumns.length !== 0) {
47
- deletedColumns.forEach(colName => delete doc[colName])
70
+ deletedColumns.forEach(colName => delete row[colName])
48
71
  }
49
- return doc
72
+ return row
50
73
  })
51
74
 
75
+ // cleanup any attachments from object storage for deleted attachment columns
76
+ await cleanupAttachments(appId, updatedTable, { oldTable, rows: rawRows })
52
77
  // Update views
53
78
  await exports.checkForViewUpdates(db, updatedTable, rename, deletedColumns)
54
79
  delete updatedTable._rename
@@ -209,6 +234,7 @@ class TableSaveFunctions {
209
234
  // when confirmed valid
210
235
  async mid(table) {
211
236
  let response = await exports.checkForColumnUpdates(
237
+ this.appId,
212
238
  this.db,
213
239
  this.oldTable,
214
240
  table
@@ -234,6 +260,20 @@ class TableSaveFunctions {
234
260
  }
235
261
  }
236
262
 
263
+ exports.getAllInternalTables = async appId => {
264
+ const db = new CouchDB(appId)
265
+ const internalTables = await db.allDocs(
266
+ getTableParams(null, {
267
+ include_docs: true,
268
+ })
269
+ )
270
+ return internalTables.rows.map(tableDoc => ({
271
+ ...tableDoc.doc,
272
+ type: "internal",
273
+ sourceId: BudibaseInternalDB._id,
274
+ }))
275
+ }
276
+
237
277
  exports.getAllExternalTables = async (appId, datasourceId) => {
238
278
  const db = new CouchDB(appId)
239
279
  const datasource = await db.get(datasourceId)
@@ -24,6 +24,7 @@ const backupRoutes = require("./backup")
24
24
  const metadataRoutes = require("./metadata")
25
25
  const devRoutes = require("./dev")
26
26
  const cloudRoutes = require("./cloud")
27
+ const migrationRoutes = require("./migrations")
27
28
 
28
29
  exports.mainRoutes = [
29
30
  authRoutes,
@@ -53,6 +54,7 @@ exports.mainRoutes = [
53
54
  // this could be breaking as koa may recognise other routes as this
54
55
  tableRoutes,
55
56
  rowRoutes,
57
+ migrationRoutes,
56
58
  ]
57
59
 
58
60
  exports.staticRoutes = staticRoutes
@@ -0,0 +1,14 @@
1
+ const Router = require("@koa/router")
2
+ const migrationsController = require("../controllers/migrations")
3
+ const router = Router()
4
+ const { internalApi } = require("@budibase/backend-core/auth")
5
+
6
+ router
7
+ .post("/api/migrations/run", internalApi, migrationsController.migrate)
8
+ .get(
9
+ "/api/migrations/definitions",
10
+ internalApi,
11
+ migrationsController.fetchDefinitions
12
+ )
13
+
14
+ module.exports = router
package/src/app.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  // need to load environment first
2
2
  import { ExtendableContext } from "koa"
3
3
 
4
- const env = require("./environment")
4
+ import * as env from "./environment"
5
5
  const CouchDB = require("./db")
6
6
  require("@budibase/backend-core").init(CouchDB)
7
7
  const Koa = require("koa")
@@ -16,6 +16,7 @@ const Sentry = require("@sentry/node")
16
16
  const fileSystem = require("./utilities/fileSystem")
17
17
  const bullboard = require("./automations/bullboard")
18
18
  const redis = require("./utilities/redis")
19
+ import * as migrations from "./migrations"
19
20
 
20
21
  const app = new Koa()
21
22
 
@@ -84,13 +85,25 @@ module.exports = server.listen(env.PORT || 0, async () => {
84
85
  await automations.init()
85
86
  })
86
87
 
87
- process.on("uncaughtException", err => {
88
- console.error(err)
88
+ const shutdown = () => {
89
89
  server.close()
90
90
  server.destroy()
91
+ }
92
+
93
+ process.on("uncaughtException", err => {
94
+ console.error(err)
95
+ shutdown()
91
96
  })
92
97
 
93
98
  process.on("SIGTERM", () => {
94
- server.close()
95
- server.destroy()
99
+ shutdown()
96
100
  })
101
+
102
+ // run migrations on startup if not done via http
103
+ // not recommended in a clustered environment
104
+ if (!env.HTTP_MIGRATIONS) {
105
+ migrations.migrate().catch(err => {
106
+ console.error("Error performing migrations. Exiting.\n", err)
107
+ shutdown()
108
+ })
109
+ }
@@ -61,6 +61,11 @@ exports.RelationshipTypes = {
61
61
  MANY_TO_MANY: "many-to-many",
62
62
  }
63
63
 
64
+ exports.FormulaTypes = {
65
+ STATIC: "static",
66
+ DYNAMIC: "dynamic",
67
+ }
68
+
64
69
  exports.AuthTypes = {
65
70
  APP: "app",
66
71
  BUILDER: "builder",
@@ -72,7 +72,7 @@ async function getLinksForRows(appId, rows) {
72
72
  )
73
73
  }
74
74
 
75
- async function getFullLinkedDocs(ctx, appId, links) {
75
+ async function getFullLinkedDocs(appId, links) {
76
76
  // create DBs
77
77
  const db = new CouchDB(appId)
78
78
  const linkedRowIds = links.map(link => link.id)
@@ -146,13 +146,12 @@ exports.updateLinks = async function (args) {
146
146
  /**
147
147
  * Given a table and a list of rows this will retrieve all of the attached docs and enrich them into the row.
148
148
  * This is required for formula fields, this may only be utilised internally (for now).
149
- * @param {object} ctx The request which is looking for rows.
149
+ * @param {string} appId The ID of the app which this request is in the context of.
150
150
  * @param {object} table The table from which the rows originated.
151
151
  * @param {array<object>} rows The rows which are to be enriched.
152
152
  * @return {Promise<*>} returns the rows with all of the enriched relationships on it.
153
153
  */
154
- exports.attachFullLinkedDocs = async (ctx, table, rows) => {
155
- const appId = ctx.appId
154
+ exports.attachFullLinkedDocs = async (appId, table, rows) => {
156
155
  const linkedTableIds = getLinkedTableIDs(table)
157
156
  if (linkedTableIds.length === 0) {
158
157
  return rows
@@ -166,7 +165,7 @@ exports.attachFullLinkedDocs = async (ctx, table, rows) => {
166
165
  // clear any existing links that could be dupe'd
167
166
  rows = clearRelationshipFields(table, rows)
168
167
  // now get the docs and combine into the rows
169
- let linked = await getFullLinkedDocs(ctx, appId, links)
168
+ let linked = await getFullLinkedDocs(appId, links)
170
169
  const linkedTables = []
171
170
  for (let row of rows) {
172
171
  for (let link of links.filter(link => link.thisId === row._id)) {
@@ -17,6 +17,8 @@ export interface FieldSchema {
17
17
  autocolumn?: boolean
18
18
  throughFrom?: string
19
19
  throughTo?: string
20
+ formula?: string
21
+ formulaType?: string
20
22
  main?: boolean
21
23
  meta?: {
22
24
  toTable: string
@@ -46,6 +48,7 @@ export interface Table extends Base {
46
48
  schema: TableSchema
47
49
  primaryDisplay?: string
48
50
  sourceId?: string
51
+ relatedFormula?: string[]
49
52
  constrained?: string[]
50
53
  }
51
54
 
@@ -43,6 +43,7 @@ module.exports = {
43
43
  REDIS_PASSWORD: process.env.REDIS_PASSWORD,
44
44
  INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
45
45
  MULTI_TENANCY: process.env.MULTI_TENANCY,
46
+ HTTP_MIGRATIONS: process.env.HTTP_MIGRATIONS,
46
47
  // environment
47
48
  NODE_ENV: process.env.NODE_ENV,
48
49
  JEST_WORKER_ID: process.env.JEST_WORKER_ID,
@@ -5,7 +5,6 @@ const {
5
5
  isExternalTable,
6
6
  isRowId: isExternalRowId,
7
7
  } = require("../integrations/utils")
8
- const migration = require("../migrations/usageQuotas")
9
8
 
10
9
  // currently only counting new writes and deletes
11
10
  const METHOD_MAP = {
@@ -74,7 +73,6 @@ module.exports = async (ctx, next) => {
74
73
  usage = files.map(file => file.size).reduce((total, size) => total + size)
75
74
  }
76
75
  try {
77
- await migration.run()
78
76
  await performRequest(ctx, next, property, usage)
79
77
  } catch (err) {
80
78
  ctx.throw(400, err)
@@ -0,0 +1,34 @@
1
+ const { DocumentTypes } = require("@budibase/backend-core/db")
2
+ import { getAppUrl } from "../../api/controllers/application"
3
+
4
+ /**
5
+ * Date:
6
+ * January 2022
7
+ *
8
+ * Description:
9
+ * Add the url to the app metadata if it doesn't exist
10
+ */
11
+ export const run = async (appDb: any) => {
12
+ let metadata
13
+ try {
14
+ metadata = await appDb.get(DocumentTypes.APP_METADATA)
15
+ } catch (e) {
16
+ // sometimes the metadata document doesn't exist
17
+ // exit early instead of failing the migration
18
+ console.error("Error retrieving app metadata. Skipping", e)
19
+ return
20
+ }
21
+
22
+ if (!metadata.url) {
23
+ const context = {
24
+ request: {
25
+ body: {
26
+ name: metadata.name,
27
+ },
28
+ },
29
+ }
30
+ metadata.url = getAppUrl(context)
31
+ console.log(`Adding url to app: ${metadata.url}`)
32
+ await appDb.put(metadata)
33
+ }
34
+ }
@@ -0,0 +1,20 @@
1
+ import { runQuotaMigration } from "./usageQuotas"
2
+ import * as syncApps from "./usageQuotas/syncApps"
3
+ import * as syncRows from "./usageQuotas/syncRows"
4
+
5
+ /**
6
+ * Date:
7
+ * January 2022
8
+ *
9
+ * Description:
10
+ * Synchronise the app and row quotas to the state of the db after it was
11
+ * discovered that the quota resets were still in place and the row quotas
12
+ * weren't being decremented correctly.
13
+ */
14
+
15
+ export const run = async () => {
16
+ await runQuotaMigration(async () => {
17
+ await syncApps.run()
18
+ await syncRows.run()
19
+ })
20
+ }
@@ -0,0 +1,29 @@
1
+ const { DocumentTypes } = require("@budibase/backend-core/db")
2
+ const env = require("../../../environment")
3
+ const TestConfig = require("../../../tests/utilities/TestConfiguration")
4
+
5
+ const migration = require("../appUrls")
6
+
7
+ describe("run", () => {
8
+ let config = new TestConfig(false)
9
+ const CouchDB = config.getCouch()
10
+
11
+ beforeEach(async () => {
12
+ await config.init()
13
+ })
14
+
15
+ afterAll(config.end)
16
+
17
+ it("runs successfully", async () => {
18
+ const app = await config.createApp("testApp")
19
+ const appDb = new CouchDB(app.appId)
20
+ let metadata = await appDb.get(DocumentTypes.APP_METADATA)
21
+ delete metadata.url
22
+ await appDb.put(metadata)
23
+
24
+ await migration.run(appDb)
25
+
26
+ metadata = await appDb.get(DocumentTypes.APP_METADATA)
27
+ expect(metadata.url).toEqual("/testapp")
28
+ })
29
+ })
@@ -4,10 +4,10 @@ const TestConfig = require("../../../tests/utilities/TestConfiguration")
4
4
  const syncApps = jest.fn()
5
5
  const syncRows = jest.fn()
6
6
 
7
- jest.mock("../../usageQuotas/syncApps", () => ({ run: syncApps }) )
8
- jest.mock("../../usageQuotas/syncRows", () => ({ run: syncRows }) )
7
+ jest.mock("../usageQuotas/syncApps", () => ({ run: syncApps }) )
8
+ jest.mock("../usageQuotas/syncRows", () => ({ run: syncRows }) )
9
9
 
10
- const migrations = require("../../usageQuotas")
10
+ const migration = require("../quotas1")
11
11
 
12
12
  describe("run", () => {
13
13
  let config = new TestConfig(false)
@@ -19,8 +19,8 @@ describe("run", () => {
19
19
 
20
20
  afterAll(config.end)
21
21
 
22
- it("runs the required migrations", async () => {
23
- await migrations.run()
22
+ it("runs ", async () => {
23
+ await migration.run()
24
24
  expect(syncApps).toHaveBeenCalledTimes(1)
25
25
  expect(syncRows).toHaveBeenCalledTimes(1)
26
26
  })
@@ -0,0 +1,25 @@
1
+ const TestConfig = require("../../../tests/utilities/TestConfiguration")
2
+ const { getGlobalDB } = require("@budibase/backend-core/tenancy")
3
+
4
+ // mock email view creation
5
+ const coreDb = require("@budibase/backend-core/db")
6
+ const createUserEmailView = jest.fn()
7
+ coreDb.createUserEmailView = createUserEmailView
8
+
9
+ const migration = require("../userEmailViewCasing")
10
+
11
+ describe("run", () => {
12
+ let config = new TestConfig(false)
13
+ const globalDb = getGlobalDB()
14
+
15
+ beforeEach(async () => {
16
+ await config.init()
17
+ })
18
+
19
+ afterAll(config.end)
20
+
21
+ it("runs successfully", async () => {
22
+ await migration.run(globalDb)
23
+ expect(createUserEmailView).toHaveBeenCalledTimes(1)
24
+ })
25
+ })
@@ -0,0 +1,8 @@
1
+ const { useQuotas } = require("../../../utilities/usageQuota")
2
+
3
+ export const runQuotaMigration = async (migration: Function) => {
4
+ if (!useQuotas()) {
5
+ return
6
+ }
7
+ await migration()
8
+ }
@@ -1,9 +1,9 @@
1
1
  const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
2
2
  const { getAllApps } = require("@budibase/backend-core/db")
3
- const CouchDB = require("../../db")
4
- const { getUsageQuotaDoc } = require("../../utilities/usageQuota")
3
+ import CouchDB from "../../../db"
4
+ import { getUsageQuotaDoc } from "../../../utilities/usageQuota"
5
5
 
6
- exports.run = async () => {
6
+ export const run = async () => {
7
7
  const db = getGlobalDB()
8
8
  // get app count
9
9
  const devApps = await getAllApps(CouchDB, { dev: true })