@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.
- package/builder/assets/{index.e083dcaf.js → index.21e57eb4.js} +234 -231
- package/builder/assets/index.83c0944f.css +1 -0
- package/builder/assets/{vendor.f9faf6ab.js → vendor.d526bfe8.js} +12 -12
- package/builder/index.html +3 -3
- package/coverage/clover.xml +2338 -2028
- package/coverage/coverage-final.json +100 -95
- package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
- package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/controllers/application.js.html +210 -126
- package/coverage/lcov-report/api/controllers/auth.js.html +18 -6
- package/coverage/lcov-report/api/controllers/automation.js.html +21 -21
- package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
- package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
- package/coverage/lcov-report/api/controllers/component.js.html +4 -4
- package/coverage/lcov-report/api/controllers/datasource.js.html +72 -36
- package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
- package/coverage/lcov-report/api/controllers/index.html +53 -53
- package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
- package/coverage/lcov-report/api/controllers/layout.js.html +6 -6
- package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
- package/coverage/lcov-report/api/controllers/migrations.js.html +119 -0
- package/coverage/lcov-report/api/controllers/permission.js.html +15 -15
- package/coverage/lcov-report/api/controllers/query/index.html +1 -1
- package/coverage/lcov-report/api/controllers/query/index.js.html +19 -19
- package/coverage/lcov-report/api/controllers/query/validation.js.html +6 -6
- package/coverage/lcov-report/api/controllers/role.js.html +9 -9
- package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/external.js.html +16 -16
- package/coverage/lcov-report/api/controllers/row/index.html +48 -33
- package/coverage/lcov-report/api/controllers/row/index.js.html +26 -26
- package/coverage/lcov-report/api/controllers/row/internal.js.html +95 -158
- package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +41 -17
- package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +551 -0
- package/coverage/lcov-report/api/controllers/row/utils.js.html +39 -57
- package/coverage/lcov-report/api/controllers/screen.js.html +7 -7
- package/coverage/lcov-report/api/controllers/script.js.html +4 -4
- package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +629 -0
- package/coverage/lcov-report/api/controllers/table/external.js.html +12 -12
- package/coverage/lcov-report/api/controllers/table/index.html +46 -31
- package/coverage/lcov-report/api/controllers/table/index.js.html +53 -77
- package/coverage/lcov-report/api/controllers/table/internal.js.html +59 -35
- package/coverage/lcov-report/api/controllers/table/utils.js.html +217 -73
- package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
- package/coverage/lcov-report/api/controllers/user.js.html +30 -24
- package/coverage/lcov-report/api/controllers/view/exporters.js.html +4 -4
- package/coverage/lcov-report/api/controllers/view/index.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.js.html +14 -14
- package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
- package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +7 -7
- package/coverage/lcov-report/api/controllers/webhook.js.html +15 -15
- package/coverage/lcov-report/api/index.html +1 -1
- package/coverage/lcov-report/api/index.js.html +8 -8
- package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
- package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/routes/application.js.html +1 -1
- package/coverage/lcov-report/api/routes/auth.js.html +1 -1
- package/coverage/lcov-report/api/routes/automation.js.html +1 -1
- package/coverage/lcov-report/api/routes/backup.js.html +1 -1
- package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
- package/coverage/lcov-report/api/routes/component.js.html +1 -1
- package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
- package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
- package/coverage/lcov-report/api/routes/dev.js.html +1 -1
- package/coverage/lcov-report/api/routes/index.html +18 -18
- package/coverage/lcov-report/api/routes/index.js.html +3 -3
- package/coverage/lcov-report/api/routes/integration.js.html +1 -1
- package/coverage/lcov-report/api/routes/layout.js.html +1 -1
- package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
- package/coverage/lcov-report/api/routes/{hosting.js.html → migrations.js.html} +17 -14
- package/coverage/lcov-report/api/routes/permission.js.html +1 -1
- package/coverage/lcov-report/api/routes/query.js.html +1 -1
- package/coverage/lcov-report/api/routes/role.js.html +1 -1
- package/coverage/lcov-report/api/routes/routing.js.html +1 -1
- package/coverage/lcov-report/api/routes/row.js.html +1 -1
- package/coverage/lcov-report/api/routes/screen.js.html +1 -1
- package/coverage/lcov-report/api/routes/script.js.html +1 -1
- package/coverage/lcov-report/api/routes/static.js.html +17 -2
- package/coverage/lcov-report/api/routes/table.js.html +1 -1
- package/coverage/lcov-report/api/routes/templates.js.html +1 -1
- package/coverage/lcov-report/api/routes/user.js.html +1 -1
- package/coverage/lcov-report/api/routes/view.js.html +1 -1
- package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/actions.js.html +24 -24
- package/coverage/lcov-report/automations/automationUtils.js.html +5 -5
- package/coverage/lcov-report/automations/bullboard.js.html +16 -16
- package/coverage/lcov-report/automations/index.html +1 -1
- package/coverage/lcov-report/automations/index.js.html +1 -1
- package/coverage/lcov-report/automations/steps/bash.js.html +6 -6
- package/coverage/lcov-report/automations/steps/createRow.js.html +13 -19
- package/coverage/lcov-report/automations/steps/delay.js.html +4 -4
- package/coverage/lcov-report/automations/steps/deleteRow.js.html +11 -20
- package/coverage/lcov-report/automations/steps/discord.js.html +7 -7
- package/coverage/lcov-report/automations/steps/executeQuery.js.html +6 -6
- package/coverage/lcov-report/automations/steps/executeScript.js.html +6 -6
- package/coverage/lcov-report/automations/steps/filter.js.html +7 -7
- package/coverage/lcov-report/automations/steps/index.html +13 -13
- package/coverage/lcov-report/automations/steps/integromat.js.html +5 -5
- package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +8 -8
- package/coverage/lcov-report/automations/steps/queryRows.js.html +10 -10
- package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +5 -5
- package/coverage/lcov-report/automations/steps/serverLog.js.html +3 -3
- package/coverage/lcov-report/automations/steps/slack.js.html +5 -5
- package/coverage/lcov-report/automations/steps/updateRow.js.html +6 -6
- package/coverage/lcov-report/automations/steps/utils.js.html +3 -3
- package/coverage/lcov-report/automations/steps/zapier.js.html +5 -5
- package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
- package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
- package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
- package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
- package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
- package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
- package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
- package/coverage/lcov-report/automations/triggers.js.html +18 -18
- package/coverage/lcov-report/automations/utils.js.html +32 -32
- package/coverage/lcov-report/constants/index.html +5 -5
- package/coverage/lcov-report/constants/index.js.html +53 -26
- package/coverage/lcov-report/constants/layouts.js.html +5 -5
- package/coverage/lcov-report/constants/screens.js.html +4 -4
- package/coverage/lcov-report/db/client.js.html +18 -18
- package/coverage/lcov-report/db/inMemoryView.js.html +7 -7
- package/coverage/lcov-report/db/index.html +15 -15
- package/coverage/lcov-report/db/index.js.html +3 -3
- package/coverage/lcov-report/db/linkedRows/LinkController.js.html +18 -18
- package/coverage/lcov-report/db/linkedRows/index.html +10 -10
- package/coverage/lcov-report/db/linkedRows/index.js.html +38 -41
- package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +15 -15
- package/coverage/lcov-report/db/newid.js.html +4 -4
- package/coverage/lcov-report/db/utils.js.html +86 -86
- package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
- package/coverage/lcov-report/events/index.html +1 -1
- package/coverage/lcov-report/events/index.js.html +5 -5
- package/coverage/lcov-report/events/utils.js.html +3 -3
- package/coverage/lcov-report/index.html +114 -99
- package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
- package/coverage/lcov-report/integrations/tests/index.html +1 -1
- package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
- package/coverage/lcov-report/middleware/authorized.js.html +166 -64
- package/coverage/lcov-report/middleware/builder.js.html +8 -8
- package/coverage/lcov-report/middleware/currentapp.js.html +29 -29
- package/coverage/lcov-report/middleware/index.html +30 -30
- package/coverage/lcov-report/middleware/joi-validator.js.html +3 -3
- package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
- package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
- package/coverage/lcov-report/middleware/usageQuota.js.html +270 -42
- package/coverage/lcov-report/middleware/utils.js.html +2 -2
- package/coverage/lcov-report/threads/automation.js.html +1 -1
- package/coverage/lcov-report/threads/index.html +15 -15
- package/coverage/lcov-report/threads/index.js.html +10 -10
- package/coverage/lcov-report/threads/query.js.html +23 -8
- package/coverage/lcov-report/threads/utils.js.html +14 -14
- package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
- package/coverage/lcov-report/utilities/budibaseDir.js.html +7 -7
- package/coverage/lcov-report/utilities/centralPath.js.html +4 -4
- package/coverage/lcov-report/utilities/csvParser.js.html +6 -6
- package/coverage/lcov-report/utilities/global.js.html +26 -26
- package/coverage/lcov-report/utilities/index.html +19 -19
- package/coverage/lcov-report/utilities/index.js.html +25 -25
- package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +8 -8
- package/coverage/lcov-report/utilities/queue/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/index.html +27 -27
- package/coverage/lcov-report/utilities/rowProcessor/index.js.html +115 -79
- package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +75 -27
- package/coverage/lcov-report/utilities/scriptRunner.js.html +5 -5
- package/coverage/lcov-report/utilities/security.js.html +8 -8
- package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
- package/coverage/lcov-report/utilities/usageQuota/index.html +141 -0
- package/coverage/lcov-report/utilities/usageQuota/index.js.html +356 -0
- package/coverage/lcov-report/utilities/usageQuota/rows.js.html +302 -0
- package/coverage/lcov-report/{api/controllers/hosting.js.html → utilities/usageQuota/usageQuoteReset.js.html} +36 -48
- package/coverage/lcov-report/utilities/users.js.html +5 -5
- package/coverage/lcov-report/utilities/workerRequests.js.html +39 -108
- package/coverage/lcov.info +3656 -3057
- package/dist/api/controllers/application.js +61 -34
- package/dist/api/controllers/auth.js +4 -0
- package/dist/api/controllers/datasource.js +11 -0
- package/dist/api/controllers/migrations.js +21 -0
- package/dist/api/controllers/row/internal.js +30 -53
- package/dist/api/controllers/row/internalSearch.js +12 -4
- package/dist/api/controllers/row/staticFormula.js +147 -0
- package/dist/api/controllers/row/utils.js +6 -8
- package/dist/api/controllers/static/index.js +59 -10
- package/dist/api/controllers/table/bulkFormula.js +160 -0
- package/dist/api/controllers/table/index.js +12 -9
- package/dist/api/controllers/table/internal.js +9 -0
- package/dist/api/controllers/table/utils.js +41 -12
- package/dist/api/controllers/user.js +2 -0
- package/dist/api/routes/index.js +2 -2
- package/dist/api/routes/migrations.js +9 -0
- package/dist/api/routes/static.js +2 -1
- package/dist/app.js +35 -5
- package/dist/automations/steps/createRow.js +2 -4
- package/dist/automations/steps/deleteRow.js +1 -4
- package/dist/constants/index.js +7 -0
- package/dist/db/linkedRows/index.js +4 -5
- package/dist/definitions/datasource.js +1 -0
- package/dist/environment.js +2 -0
- package/dist/integrations/base/sql.js +13 -6
- package/dist/integrations/googlesheets.js +340 -0
- package/dist/integrations/index.js +3 -0
- package/dist/integrations/utils.js +57 -9
- package/dist/middleware/authorized.js +55 -30
- package/dist/middleware/usageQuota.js +80 -13
- package/dist/migrations/functions/appUrls.js +46 -0
- package/dist/migrations/functions/quotas1.js +50 -0
- package/dist/migrations/functions/usageQuotas/index.js +20 -0
- package/dist/migrations/functions/usageQuotas/syncApps.js +32 -0
- package/dist/migrations/functions/usageQuotas/syncRows.js +35 -0
- package/dist/migrations/functions/userEmailViewCasing.js +24 -0
- package/dist/migrations/index.js +62 -0
- package/dist/package.json +8 -5
- package/dist/tests/utilities/TestConfiguration.js +13 -6
- package/dist/threads/query.js +4 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utilities/rowProcessor/index.js +18 -11
- package/dist/utilities/rowProcessor/utils.js +14 -4
- package/dist/utilities/{usageQuota.js → usageQuota/index.js} +44 -31
- package/dist/utilities/usageQuota/rows.js +77 -0
- package/dist/utilities/usageQuota/usageQuoteReset.js +16 -0
- package/dist/utilities/workerRequests.js +0 -20
- package/nodemon.json +1 -1
- package/package.json +9 -6
- package/src/api/controllers/application.js +52 -24
- package/src/api/controllers/auth.js +4 -0
- package/src/api/controllers/datasource.js +12 -0
- package/src/api/controllers/migrations.js +13 -0
- package/src/api/controllers/row/internal.js +29 -50
- package/src/api/controllers/row/internalSearch.js +12 -4
- package/src/api/controllers/row/staticFormula.js +157 -0
- package/src/api/controllers/row/utils.js +6 -12
- package/src/api/controllers/static/index.js +63 -10
- package/src/api/controllers/table/bulkFormula.js +183 -0
- package/src/api/controllers/table/index.js +16 -24
- package/src/api/controllers/table/internal.js +9 -1
- package/src/api/controllers/table/utils.js +57 -9
- package/src/api/controllers/user.js +2 -0
- package/src/api/routes/index.js +2 -2
- package/src/api/routes/migrations.js +14 -0
- package/src/api/routes/static.js +5 -0
- package/src/api/routes/tests/application.spec.js +2 -2
- package/src/api/routes/tests/auth.spec.js +1 -2
- package/src/api/routes/tests/static.spec.js +98 -0
- package/src/app.ts +18 -5
- package/src/automations/steps/createRow.js +2 -4
- package/src/automations/steps/deleteRow.js +1 -4
- package/src/constants/index.js +9 -0
- package/src/db/linkedRows/index.js +4 -5
- package/src/definitions/common.ts +3 -0
- package/src/definitions/datasource.ts +3 -0
- package/src/environment.js +2 -0
- package/src/integrations/base/sql.ts +14 -7
- package/src/integrations/googlesheets.ts +353 -0
- package/src/integrations/index.ts +3 -0
- package/src/integrations/tests/sql.spec.js +16 -0
- package/src/integrations/utils.ts +71 -11
- package/src/middleware/authorized.js +64 -30
- package/src/middleware/tests/authorized.spec.js +6 -4
- package/src/middleware/tests/usageQuota.spec.js +18 -24
- package/src/middleware/usageQuota.js +91 -15
- package/src/migrations/functions/appUrls.ts +34 -0
- package/src/migrations/functions/quotas1.ts +20 -0
- package/src/migrations/functions/tests/appUrls.spec.js +29 -0
- package/src/migrations/functions/tests/quotas1.spec.js +27 -0
- package/src/migrations/functions/tests/userEmailViewCasing.spec.js +25 -0
- package/src/migrations/functions/usageQuotas/index.ts +8 -0
- package/src/migrations/functions/usageQuotas/syncApps.ts +18 -0
- package/src/migrations/functions/usageQuotas/syncRows.ts +21 -0
- package/src/migrations/functions/usageQuotas/tests/syncApps.spec.js +37 -0
- package/src/migrations/functions/usageQuotas/tests/syncRows.spec.js +43 -0
- package/src/migrations/functions/userEmailViewCasing.ts +13 -0
- package/src/migrations/index.ts +56 -0
- package/src/tests/utilities/TestConfiguration.js +14 -6
- package/src/threads/query.js +5 -0
- package/src/utilities/rowProcessor/index.js +24 -12
- package/src/utilities/rowProcessor/utils.js +23 -7
- package/src/utilities/tests/usageQuota/usageQuota.spec.js +72 -0
- package/src/utilities/usageQuota/index.js +92 -0
- package/src/utilities/usageQuota/rows.js +74 -0
- package/src/utilities/usageQuota/usageQuoteReset.js +18 -0
- package/src/utilities/workerRequests.js +0 -23
- package/builder/assets/index.39e01193.css +0 -1
- package/dist/api/controllers/hosting.js +0 -30
- package/dist/api/routes/hosting.js +0 -11
- package/src/api/controllers/hosting.js +0 -22
- package/src/api/routes/hosting.js +0 -13
- package/src/api/routes/tests/hosting.spec.js +0 -36
- package/src/utilities/usageQuota.js +0 -73
|
@@ -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
|
|
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
|
|
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
|
-
|
|
40
|
+
if (limit != null) {
|
|
41
|
+
this.limit = limit
|
|
42
|
+
}
|
|
41
43
|
return this
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
setSort(sort) {
|
|
45
|
-
|
|
47
|
+
if (sort != null) {
|
|
48
|
+
this.sort = sort
|
|
49
|
+
}
|
|
46
50
|
return this
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
setSortOrder(sortOrder) {
|
|
50
|
-
|
|
54
|
+
if (sortOrder != null) {
|
|
55
|
+
this.sortOrder = sortOrder
|
|
56
|
+
}
|
|
51
57
|
return this
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
setSortType(sortType) {
|
|
55
|
-
|
|
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
|
+
}
|
|
@@ -58,22 +58,16 @@ exports.validate = async ({ appId, tableId, row, table }) => {
|
|
|
58
58
|
let res
|
|
59
59
|
|
|
60
60
|
// Validate.js doesn't seem to handle array
|
|
61
|
-
if (type === FieldTypes.ARRAY) {
|
|
62
|
-
|
|
63
|
-
Array.isArray(row[fieldName]) && row[fieldName].length > 0
|
|
64
|
-
|
|
65
|
-
// Check values are valid if values are specified
|
|
66
|
-
if (hasValues) {
|
|
61
|
+
if (type === FieldTypes.ARRAY && row[fieldName]) {
|
|
62
|
+
if (row[fieldName].length) {
|
|
67
63
|
row[fieldName].map(val => {
|
|
68
64
|
if (!constraints.inclusion.includes(val)) {
|
|
69
|
-
errors[fieldName] = "
|
|
65
|
+
errors[fieldName] = "Field not in list"
|
|
70
66
|
}
|
|
71
67
|
})
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (constraints.presence === true && !hasValues) {
|
|
76
|
-
errors[fieldName] = "Required field"
|
|
68
|
+
} else if (constraints.presence && row[fieldName].length === 0) {
|
|
69
|
+
// non required MultiSelect creates an empty array, which should not throw errors
|
|
70
|
+
errors[fieldName] = [`${fieldName} is required`]
|
|
77
71
|
}
|
|
78
72
|
} else if (type === FieldTypes.JSON && typeof row[fieldName] === "string") {
|
|
79
73
|
// this should only happen if there is an error
|
|
@@ -5,7 +5,7 @@ const { resolve, join } = require("../../../utilities/centralPath")
|
|
|
5
5
|
const uuid = require("uuid")
|
|
6
6
|
const { ObjectStoreBuckets } = require("../../../constants")
|
|
7
7
|
const { processString } = require("@budibase/string-templates")
|
|
8
|
-
const {
|
|
8
|
+
const { getAllApps } = require("@budibase/backend-core/db")
|
|
9
9
|
const CouchDB = require("../../../db")
|
|
10
10
|
const {
|
|
11
11
|
loadHandlebarsFile,
|
|
@@ -17,6 +17,8 @@ const { clientLibraryPath } = require("../../../utilities")
|
|
|
17
17
|
const { upload } = require("../../../utilities/fileSystem")
|
|
18
18
|
const { attachmentsRelativeURL } = require("../../../utilities")
|
|
19
19
|
const { DocumentTypes } = require("../../../db/utils")
|
|
20
|
+
const AWS = require("aws-sdk")
|
|
21
|
+
const AWS_REGION = env.AWS_REGION ? env.AWS_REGION : "eu-west-1"
|
|
20
22
|
|
|
21
23
|
async function prepareUpload({ s3Key, bucket, metadata, file }) {
|
|
22
24
|
const response = await upload({
|
|
@@ -37,12 +39,18 @@ async function prepareUpload({ s3Key, bucket, metadata, file }) {
|
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
async function
|
|
41
|
-
// the "appId" component of the URL
|
|
42
|
+
async function getAppIdFromUrl(ctx) {
|
|
43
|
+
// the "appId" component of the URL can be the id or the custom url
|
|
42
44
|
let possibleAppUrl = `/${encodeURI(ctx.params.appId).toLowerCase()}`
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
|
|
46
|
+
// search prod apps for a url that matches, exclude dev where id is always used
|
|
47
|
+
const apps = await getAllApps(CouchDB, { dev: false })
|
|
48
|
+
const app = apps.filter(
|
|
49
|
+
a => a.url && a.url.toLowerCase() === possibleAppUrl
|
|
50
|
+
)[0]
|
|
51
|
+
|
|
52
|
+
if (app && app.appId) {
|
|
53
|
+
return app.appId
|
|
46
54
|
} else {
|
|
47
55
|
return ctx.params.appId
|
|
48
56
|
}
|
|
@@ -75,10 +83,7 @@ exports.uploadFile = async function (ctx) {
|
|
|
75
83
|
}
|
|
76
84
|
|
|
77
85
|
exports.serveApp = async function (ctx) {
|
|
78
|
-
let appId = ctx
|
|
79
|
-
if (env.SELF_HOSTED) {
|
|
80
|
-
appId = await checkForSelfHostedURL(ctx)
|
|
81
|
-
}
|
|
86
|
+
let appId = await getAppIdFromUrl(ctx)
|
|
82
87
|
const App = require("./templates/BudibaseApp.svelte").default
|
|
83
88
|
const db = new CouchDB(appId, { skip_setup: true })
|
|
84
89
|
const appInfo = await db.get(DocumentTypes.APP_METADATA)
|
|
@@ -104,3 +109,51 @@ exports.serveClientLibrary = async function (ctx) {
|
|
|
104
109
|
root: join(NODE_MODULES_PATH, "@budibase", "client", "dist"),
|
|
105
110
|
})
|
|
106
111
|
}
|
|
112
|
+
|
|
113
|
+
exports.getSignedUploadURL = async function (ctx) {
|
|
114
|
+
const database = new CouchDB(ctx.appId)
|
|
115
|
+
|
|
116
|
+
// Ensure datasource is valid
|
|
117
|
+
let datasource
|
|
118
|
+
try {
|
|
119
|
+
const { datasourceId } = ctx.params
|
|
120
|
+
datasource = await database.get(datasourceId)
|
|
121
|
+
if (!datasource) {
|
|
122
|
+
ctx.throw(400, "The specified datasource could not be found")
|
|
123
|
+
}
|
|
124
|
+
} catch (error) {
|
|
125
|
+
ctx.throw(400, "The specified datasource could not be found")
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Ensure we aren't using a custom endpoint
|
|
129
|
+
if (datasource?.config?.endpoint) {
|
|
130
|
+
ctx.throw(400, "S3 datasources with custom endpoints are not supported")
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Determine type of datasource and generate signed URL
|
|
134
|
+
let signedUrl
|
|
135
|
+
let publicUrl
|
|
136
|
+
if (datasource.source === "S3") {
|
|
137
|
+
const { bucket, key } = ctx.request.body || {}
|
|
138
|
+
if (!bucket || !key) {
|
|
139
|
+
ctx.throw(400, "bucket and key values are required")
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const s3 = new AWS.S3({
|
|
144
|
+
region: AWS_REGION,
|
|
145
|
+
accessKeyId: datasource?.config?.accessKeyId,
|
|
146
|
+
secretAccessKey: datasource?.config?.secretAccessKey,
|
|
147
|
+
apiVersion: "2006-03-01",
|
|
148
|
+
signatureVersion: "v4",
|
|
149
|
+
})
|
|
150
|
+
const params = { Bucket: bucket, Key: key }
|
|
151
|
+
signedUrl = s3.getSignedUrl("putObject", params)
|
|
152
|
+
publicUrl = `https://${bucket}.s3.${AWS_REGION}.amazonaws.com/${key}`
|
|
153
|
+
} catch (error) {
|
|
154
|
+
ctx.throw(400, error)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
ctx.body = { signedUrl, publicUrl }
|
|
159
|
+
}
|
|
@@ -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
|
+
}
|