@budibase/server 1.0.151 → 1.0.154
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.e05b34c9.js → index.5d695b0f.js} +1 -1
- package/builder/index.html +1 -1
- package/coverage/clover.xml +144 -123
- package/coverage/coverage-final.json +3 -3
- 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/auth.js.html +1 -1
- package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
- 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 +1 -1
- package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
- package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
- package/coverage/lcov-report/api/controllers/index.html +1 -1
- package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
- package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
- package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
- package/coverage/lcov-report/api/controllers/migrations.js.html +1 -1
- package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
- package/coverage/lcov-report/api/controllers/query/index.html +1 -1
- package/coverage/lcov-report/api/controllers/query/validation.js.html +1 -1
- package/coverage/lcov-report/api/controllers/role.js.html +1 -1
- package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/index.html +1 -1
- package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
- package/coverage/lcov-report/api/controllers/script.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/index.html +1 -1
- package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
- package/coverage/lcov-report/api/controllers/user.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
- package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
- package/coverage/lcov-report/api/index.html +1 -1
- package/coverage/lcov-report/api/index.js.html +1 -1
- 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/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 +1 -1
- 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/migrations.js.html +1 -1
- 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/screen.js.html +1 -1
- package/coverage/lcov-report/api/routes/script.js.html +1 -1
- 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/utils/index.html +1 -1
- package/coverage/lcov-report/api/routes/utils/validators.js.html +2 -5
- 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 +1 -1
- package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
- package/coverage/lcov-report/automations/bullboard.js.html +1 -1
- 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 +1 -1
- package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
- package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
- package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
- package/coverage/lcov-report/automations/steps/index.html +1 -1
- package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
- package/coverage/lcov-report/automations/steps/loop.js.html +1 -1
- package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
- package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
- package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
- package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
- package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
- package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
- package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/triggers.js.html +1 -1
- package/coverage/lcov-report/constants/index.html +5 -5
- package/coverage/lcov-report/constants/index.js.html +19 -4
- package/coverage/lcov-report/constants/layouts.js.html +1 -1
- package/coverage/lcov-report/constants/screens.js.html +1 -1
- package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
- package/coverage/lcov-report/db/index.html +1 -1
- package/coverage/lcov-report/db/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
- package/coverage/lcov-report/db/newid.js.html +1 -1
- package/coverage/lcov-report/db/utils.js.html +1 -1
- 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 +1 -1
- package/coverage/lcov-report/events/utils.js.html +1 -1
- package/coverage/lcov-report/index.html +21 -21
- 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/builder.js.html +1 -1
- package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
- package/coverage/lcov-report/middleware/index.html +1 -1
- package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
- package/coverage/lcov-report/middleware/publicApi.js.html +1 -1
- 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/utils.js.html +1 -1
- package/coverage/lcov-report/threads/automation.js.html +117 -63
- package/coverage/lcov-report/threads/index.html +19 -19
- package/coverage/lcov-report/threads/query.js.html +1 -1
- package/coverage/lcov-report/threads/utils.js.html +1 -1
- package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
- package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
- package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
- package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
- package/coverage/lcov-report/utilities/global.js.html +1 -1
- package/coverage/lcov-report/utilities/index.html +1 -1
- package/coverage/lcov-report/utilities/index.js.html +1 -1
- package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +1 -1
- 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 +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
- package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
- package/coverage/lcov-report/utilities/security.js.html +1 -1
- package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
- package/coverage/lcov-report/utilities/usageQuota/index.html +1 -1
- package/coverage/lcov-report/utilities/usageQuota/rows.js.html +1 -1
- package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +1 -1
- package/coverage/lcov-report/utilities/users.js.html +1 -1
- package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
- package/coverage/lcov.info +242 -207
- package/dist/api/controllers/row/index.js +12 -17
- package/dist/api/routes/utils/validators.js +0 -1
- package/dist/constants/index.js +4 -0
- package/dist/package.json +5 -5
- package/dist/threads/automation.js +46 -21
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/src/api/controllers/row/index.ts +19 -15
- package/src/api/routes/tests/row.spec.js +87 -5
- package/src/api/routes/utils/validators.js +0 -1
- package/src/constants/index.js +5 -0
- package/src/threads/automation.js +49 -31
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.
|
|
4
|
+
"version": "1.0.154",
|
|
5
5
|
"description": "Budibase Web Server",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -69,10 +69,10 @@
|
|
|
69
69
|
"license": "GPL-3.0",
|
|
70
70
|
"dependencies": {
|
|
71
71
|
"@apidevtools/swagger-parser": "^10.0.3",
|
|
72
|
-
"@budibase/backend-core": "^1.0.
|
|
73
|
-
"@budibase/client": "^1.0.
|
|
74
|
-
"@budibase/pro": "1.0.
|
|
75
|
-
"@budibase/string-templates": "^1.0.
|
|
72
|
+
"@budibase/backend-core": "^1.0.154",
|
|
73
|
+
"@budibase/client": "^1.0.154",
|
|
74
|
+
"@budibase/pro": "1.0.153",
|
|
75
|
+
"@budibase/string-templates": "^1.0.154",
|
|
76
76
|
"@bull-board/api": "^3.7.0",
|
|
77
77
|
"@bull-board/koa": "^3.7.0",
|
|
78
78
|
"@elastic/elasticsearch": "7.10.0",
|
|
@@ -181,5 +181,5 @@
|
|
|
181
181
|
"optionalDependencies": {
|
|
182
182
|
"oracledb": "^5.3.0"
|
|
183
183
|
},
|
|
184
|
-
"gitHead": "
|
|
184
|
+
"gitHead": "3514528e39203c1404b36ce8392370c34954387e"
|
|
185
185
|
}
|
|
@@ -31,7 +31,9 @@ export async function patch(ctx: any): Promise<any> {
|
|
|
31
31
|
return save(ctx)
|
|
32
32
|
}
|
|
33
33
|
try {
|
|
34
|
-
const { row, table } = await
|
|
34
|
+
const { row, table } = await quotas.addQuery(() =>
|
|
35
|
+
pickApi(tableId).patch(ctx)
|
|
36
|
+
)
|
|
35
37
|
ctx.status = 200
|
|
36
38
|
ctx.eventEmitter &&
|
|
37
39
|
ctx.eventEmitter.emitRow(`row:update`, appId, row, table)
|
|
@@ -42,7 +44,7 @@ export async function patch(ctx: any): Promise<any> {
|
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
const
|
|
47
|
+
export const save = async (ctx: any) => {
|
|
46
48
|
const appId = ctx.appId
|
|
47
49
|
const tableId = getTableId(ctx)
|
|
48
50
|
const body = ctx.request.body
|
|
@@ -51,7 +53,9 @@ const saveRow = async (ctx: any) => {
|
|
|
51
53
|
return patch(ctx)
|
|
52
54
|
}
|
|
53
55
|
try {
|
|
54
|
-
const { row, table } = await
|
|
56
|
+
const { row, table } = await quotas.addRow(() =>
|
|
57
|
+
quotas.addQuery(() => pickApi(tableId).save(ctx))
|
|
58
|
+
)
|
|
55
59
|
ctx.status = 200
|
|
56
60
|
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:save`, appId, row, table)
|
|
57
61
|
ctx.message = `${table.name} saved successfully`
|
|
@@ -61,14 +65,10 @@ const saveRow = async (ctx: any) => {
|
|
|
61
65
|
}
|
|
62
66
|
}
|
|
63
67
|
|
|
64
|
-
export async function save(ctx: any) {
|
|
65
|
-
await quotas.addRow(() => saveRow(ctx))
|
|
66
|
-
}
|
|
67
|
-
|
|
68
68
|
export async function fetchView(ctx: any) {
|
|
69
69
|
const tableId = getTableId(ctx)
|
|
70
70
|
try {
|
|
71
|
-
ctx.body = await pickApi(tableId).fetchView(ctx)
|
|
71
|
+
ctx.body = await quotas.addQuery(() => pickApi(tableId).fetchView(ctx))
|
|
72
72
|
} catch (err) {
|
|
73
73
|
ctx.throw(400, err)
|
|
74
74
|
}
|
|
@@ -77,7 +77,7 @@ export async function fetchView(ctx: any) {
|
|
|
77
77
|
export async function fetch(ctx: any) {
|
|
78
78
|
const tableId = getTableId(ctx)
|
|
79
79
|
try {
|
|
80
|
-
ctx.body = await pickApi(tableId).fetch(ctx)
|
|
80
|
+
ctx.body = await quotas.addQuery(() => pickApi(tableId).fetch(ctx))
|
|
81
81
|
} catch (err) {
|
|
82
82
|
ctx.throw(400, err)
|
|
83
83
|
}
|
|
@@ -86,7 +86,7 @@ export async function fetch(ctx: any) {
|
|
|
86
86
|
export async function find(ctx: any) {
|
|
87
87
|
const tableId = getTableId(ctx)
|
|
88
88
|
try {
|
|
89
|
-
ctx.body = await pickApi(tableId).find(ctx)
|
|
89
|
+
ctx.body = await quotas.addQuery(() => pickApi(tableId).find(ctx))
|
|
90
90
|
} catch (err) {
|
|
91
91
|
ctx.throw(400, err)
|
|
92
92
|
}
|
|
@@ -98,14 +98,16 @@ export async function destroy(ctx: any) {
|
|
|
98
98
|
const tableId = getTableId(ctx)
|
|
99
99
|
let response, row
|
|
100
100
|
if (inputs.rows) {
|
|
101
|
-
let { rows } = await
|
|
101
|
+
let { rows } = await quotas.addQuery(() =>
|
|
102
|
+
pickApi(tableId).bulkDestroy(ctx)
|
|
103
|
+
)
|
|
102
104
|
await quotas.removeRows(rows.length)
|
|
103
105
|
response = rows
|
|
104
106
|
for (let row of rows) {
|
|
105
107
|
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row)
|
|
106
108
|
}
|
|
107
109
|
} else {
|
|
108
|
-
let resp = await pickApi(tableId).destroy(ctx)
|
|
110
|
+
let resp = await quotas.addQuery(() => pickApi(tableId).destroy(ctx))
|
|
109
111
|
await quotas.removeRow()
|
|
110
112
|
response = resp.response
|
|
111
113
|
row = resp.row
|
|
@@ -121,7 +123,7 @@ export async function search(ctx: any) {
|
|
|
121
123
|
const tableId = getTableId(ctx)
|
|
122
124
|
try {
|
|
123
125
|
ctx.status = 200
|
|
124
|
-
ctx.body = await pickApi(tableId).search(ctx)
|
|
126
|
+
ctx.body = await quotas.addQuery(() => pickApi(tableId).search(ctx))
|
|
125
127
|
} catch (err) {
|
|
126
128
|
ctx.throw(400, err)
|
|
127
129
|
}
|
|
@@ -139,7 +141,9 @@ export async function validate(ctx: any) {
|
|
|
139
141
|
export async function fetchEnrichedRow(ctx: any) {
|
|
140
142
|
const tableId = getTableId(ctx)
|
|
141
143
|
try {
|
|
142
|
-
ctx.body = await
|
|
144
|
+
ctx.body = await quotas.addQuery(() =>
|
|
145
|
+
pickApi(tableId).fetchEnrichedRow(ctx)
|
|
146
|
+
)
|
|
143
147
|
} catch (err) {
|
|
144
148
|
ctx.throw(400, err)
|
|
145
149
|
}
|
|
@@ -148,7 +152,7 @@ export async function fetchEnrichedRow(ctx: any) {
|
|
|
148
152
|
export const exportRows = async (ctx: any) => {
|
|
149
153
|
const tableId = getTableId(ctx)
|
|
150
154
|
try {
|
|
151
|
-
ctx.body = await pickApi(tableId).exportRows(ctx)
|
|
155
|
+
ctx.body = await quotas.addQuery(() => pickApi(tableId).exportRows(ctx))
|
|
152
156
|
} catch (err) {
|
|
153
157
|
ctx.throw(400, err)
|
|
154
158
|
}
|
|
@@ -3,6 +3,7 @@ const setup = require("./utilities")
|
|
|
3
3
|
const { basicRow } = setup.structures
|
|
4
4
|
const { doInAppContext } = require("@budibase/backend-core/context")
|
|
5
5
|
const { doInTenant } = require("@budibase/backend-core/tenancy")
|
|
6
|
+
const { quotas, QuotaUsageType, StaticQuotaName, MonthlyQuotaName } = require("@budibase/pro")
|
|
6
7
|
|
|
7
8
|
// mock the fetch for the search system
|
|
8
9
|
jest.mock("node-fetch")
|
|
@@ -28,9 +29,29 @@ describe("/rows", () => {
|
|
|
28
29
|
.expect('Content-Type', /json/)
|
|
29
30
|
.expect(status)
|
|
30
31
|
|
|
32
|
+
const getRowUsage = async () => {
|
|
33
|
+
return config.doInContext(null, () => quotas.getCurrentUsageValue(QuotaUsageType.STATIC, StaticQuotaName.ROWS))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const getQueryUsage = async () => {
|
|
37
|
+
return config.doInContext(null, () => quotas.getCurrentUsageValue(QuotaUsageType.MONTHLY, MonthlyQuotaName.QUERIES))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const assertRowUsage = async (expected) => {
|
|
41
|
+
const usage = await getRowUsage()
|
|
42
|
+
expect(usage).toBe(expected)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const assertQueryUsage = async (expected) => {
|
|
46
|
+
const usage = await getQueryUsage()
|
|
47
|
+
expect(usage).toBe(expected)
|
|
48
|
+
}
|
|
31
49
|
|
|
32
50
|
describe("save, load, update", () => {
|
|
33
51
|
it("returns a success message when the row is created", async () => {
|
|
52
|
+
const rowUsage = await getRowUsage()
|
|
53
|
+
const queryUsage = await getQueryUsage()
|
|
54
|
+
|
|
34
55
|
const res = await request
|
|
35
56
|
.post(`/api/${row.tableId}/rows`)
|
|
36
57
|
.send(row)
|
|
@@ -40,10 +61,14 @@ describe("/rows", () => {
|
|
|
40
61
|
expect(res.res.statusMessage).toEqual(`${table.name} saved successfully`)
|
|
41
62
|
expect(res.body.name).toEqual("Test Contact")
|
|
42
63
|
expect(res.body._rev).toBeDefined()
|
|
64
|
+
await assertRowUsage(rowUsage + 1)
|
|
65
|
+
await assertQueryUsage(queryUsage + 1)
|
|
43
66
|
})
|
|
44
67
|
|
|
45
68
|
it("updates a row successfully", async () => {
|
|
46
69
|
const existing = await config.createRow()
|
|
70
|
+
const rowUsage = await getRowUsage()
|
|
71
|
+
const queryUsage = await getQueryUsage()
|
|
47
72
|
|
|
48
73
|
const res = await request
|
|
49
74
|
.post(`/api/${table._id}/rows`)
|
|
@@ -59,10 +84,13 @@ describe("/rows", () => {
|
|
|
59
84
|
|
|
60
85
|
expect(res.res.statusMessage).toEqual(`${table.name} updated successfully.`)
|
|
61
86
|
expect(res.body.name).toEqual("Updated Name")
|
|
87
|
+
await assertRowUsage(rowUsage)
|
|
88
|
+
await assertQueryUsage(queryUsage + 1)
|
|
62
89
|
})
|
|
63
90
|
|
|
64
91
|
it("should load a row", async () => {
|
|
65
92
|
const existing = await config.createRow()
|
|
93
|
+
const queryUsage = await getQueryUsage()
|
|
66
94
|
|
|
67
95
|
const res = await request
|
|
68
96
|
.get(`/api/${table._id}/rows/${existing._id}`)
|
|
@@ -76,6 +104,7 @@ describe("/rows", () => {
|
|
|
76
104
|
_rev: existing._rev,
|
|
77
105
|
type: "row",
|
|
78
106
|
})
|
|
107
|
+
await assertQueryUsage(queryUsage + 1)
|
|
79
108
|
})
|
|
80
109
|
|
|
81
110
|
it("should list all rows for given tableId", async () => {
|
|
@@ -86,6 +115,7 @@ describe("/rows", () => {
|
|
|
86
115
|
}
|
|
87
116
|
await config.createRow()
|
|
88
117
|
await config.createRow(newRow)
|
|
118
|
+
const queryUsage = await getQueryUsage()
|
|
89
119
|
|
|
90
120
|
const res = await request
|
|
91
121
|
.get(`/api/${table._id}/rows`)
|
|
@@ -96,15 +126,19 @@ describe("/rows", () => {
|
|
|
96
126
|
expect(res.body.length).toBe(2)
|
|
97
127
|
expect(res.body.find(r => r.name === newRow.name)).toBeDefined()
|
|
98
128
|
expect(res.body.find(r => r.name === row.name)).toBeDefined()
|
|
129
|
+
await assertQueryUsage(queryUsage + 1)
|
|
99
130
|
})
|
|
100
131
|
|
|
101
132
|
it("load should return 404 when row does not exist", async () => {
|
|
102
133
|
await config.createRow()
|
|
134
|
+
const queryUsage = await getQueryUsage()
|
|
135
|
+
|
|
103
136
|
await request
|
|
104
137
|
.get(`/api/${table._id}/rows/not-a-valid-id`)
|
|
105
138
|
.set(config.defaultHeaders())
|
|
106
139
|
.expect('Content-Type', /json/)
|
|
107
140
|
.expect(404)
|
|
141
|
+
await assertQueryUsage(queryUsage) // no change
|
|
108
142
|
})
|
|
109
143
|
|
|
110
144
|
it("row values are coerced", async () => {
|
|
@@ -202,6 +236,9 @@ describe("/rows", () => {
|
|
|
202
236
|
it("should update only the fields that are supplied", async () => {
|
|
203
237
|
const existing = await config.createRow()
|
|
204
238
|
|
|
239
|
+
const rowUsage = await getRowUsage()
|
|
240
|
+
const queryUsage = await getQueryUsage()
|
|
241
|
+
|
|
205
242
|
const res = await request
|
|
206
243
|
.patch(`/api/${table._id}/rows`)
|
|
207
244
|
.send({
|
|
@@ -222,10 +259,15 @@ describe("/rows", () => {
|
|
|
222
259
|
|
|
223
260
|
expect(savedRow.body.description).toEqual(existing.description)
|
|
224
261
|
expect(savedRow.body.name).toEqual("Updated Name")
|
|
262
|
+
await assertRowUsage(rowUsage)
|
|
263
|
+
await assertQueryUsage(queryUsage + 2) // account for the second load
|
|
225
264
|
})
|
|
226
265
|
|
|
227
266
|
it("should throw an error when given improper types", async () => {
|
|
228
267
|
const existing = await config.createRow()
|
|
268
|
+
const rowUsage = await getRowUsage()
|
|
269
|
+
const queryUsage = await getQueryUsage()
|
|
270
|
+
|
|
229
271
|
await request
|
|
230
272
|
.patch(`/api/${table._id}/rows`)
|
|
231
273
|
.send({
|
|
@@ -236,12 +278,18 @@ describe("/rows", () => {
|
|
|
236
278
|
})
|
|
237
279
|
.set(config.defaultHeaders())
|
|
238
280
|
.expect(400)
|
|
281
|
+
|
|
282
|
+
await assertRowUsage(rowUsage)
|
|
283
|
+
await assertQueryUsage(queryUsage)
|
|
239
284
|
})
|
|
240
285
|
})
|
|
241
286
|
|
|
242
287
|
describe("destroy", () => {
|
|
243
288
|
it("should be able to delete a row", async () => {
|
|
244
289
|
const createdRow = await config.createRow(row)
|
|
290
|
+
const rowUsage = await getRowUsage()
|
|
291
|
+
const queryUsage = await getQueryUsage()
|
|
292
|
+
|
|
245
293
|
const res = await request
|
|
246
294
|
.delete(`/api/${table._id}/rows`)
|
|
247
295
|
.send({
|
|
@@ -253,11 +301,16 @@ describe("/rows", () => {
|
|
|
253
301
|
.expect('Content-Type', /json/)
|
|
254
302
|
.expect(200)
|
|
255
303
|
expect(res.body[0]._id).toEqual(createdRow._id)
|
|
304
|
+
await assertRowUsage(rowUsage -1)
|
|
305
|
+
await assertQueryUsage(queryUsage +1)
|
|
256
306
|
})
|
|
257
307
|
})
|
|
258
308
|
|
|
259
309
|
describe("validate", () => {
|
|
260
310
|
it("should return no errors on valid row", async () => {
|
|
311
|
+
const rowUsage = await getRowUsage()
|
|
312
|
+
const queryUsage = await getQueryUsage()
|
|
313
|
+
|
|
261
314
|
const res = await request
|
|
262
315
|
.post(`/api/${table._id}/rows/validate`)
|
|
263
316
|
.send({ name: "ivan" })
|
|
@@ -267,9 +320,14 @@ describe("/rows", () => {
|
|
|
267
320
|
|
|
268
321
|
expect(res.body.valid).toBe(true)
|
|
269
322
|
expect(Object.keys(res.body.errors)).toEqual([])
|
|
323
|
+
await assertRowUsage(rowUsage)
|
|
324
|
+
await assertQueryUsage(queryUsage)
|
|
270
325
|
})
|
|
271
326
|
|
|
272
327
|
it("should errors on invalid row", async () => {
|
|
328
|
+
const rowUsage = await getRowUsage()
|
|
329
|
+
const queryUsage = await getQueryUsage()
|
|
330
|
+
|
|
273
331
|
const res = await request
|
|
274
332
|
.post(`/api/${table._id}/rows/validate`)
|
|
275
333
|
.send({ name: 1 })
|
|
@@ -279,7 +337,8 @@ describe("/rows", () => {
|
|
|
279
337
|
|
|
280
338
|
expect(res.body.valid).toBe(false)
|
|
281
339
|
expect(Object.keys(res.body.errors)).toEqual(["name"])
|
|
282
|
-
|
|
340
|
+
await assertRowUsage(rowUsage)
|
|
341
|
+
await assertQueryUsage(queryUsage)
|
|
283
342
|
})
|
|
284
343
|
})
|
|
285
344
|
|
|
@@ -287,6 +346,9 @@ describe("/rows", () => {
|
|
|
287
346
|
it("should be able to delete a bulk set of rows", async () => {
|
|
288
347
|
const row1 = await config.createRow()
|
|
289
348
|
const row2 = await config.createRow()
|
|
349
|
+
const rowUsage = await getRowUsage()
|
|
350
|
+
const queryUsage = await getQueryUsage()
|
|
351
|
+
|
|
290
352
|
const res = await request
|
|
291
353
|
.delete(`/api/${table._id}/rows`)
|
|
292
354
|
.send({
|
|
@@ -298,14 +360,20 @@ describe("/rows", () => {
|
|
|
298
360
|
.set(config.defaultHeaders())
|
|
299
361
|
.expect('Content-Type', /json/)
|
|
300
362
|
.expect(200)
|
|
363
|
+
|
|
301
364
|
expect(res.body.length).toEqual(2)
|
|
302
365
|
await loadRow(row1._id, 404)
|
|
366
|
+
await assertRowUsage(rowUsage - 2)
|
|
367
|
+
await assertQueryUsage(queryUsage +1)
|
|
303
368
|
})
|
|
304
369
|
})
|
|
305
370
|
|
|
306
371
|
describe("fetchView", () => {
|
|
307
372
|
it("should be able to fetch tables contents via 'view'", async () => {
|
|
308
373
|
const row = await config.createRow()
|
|
374
|
+
const rowUsage = await getRowUsage()
|
|
375
|
+
const queryUsage = await getQueryUsage()
|
|
376
|
+
|
|
309
377
|
const res = await request
|
|
310
378
|
.get(`/api/views/${table._id}`)
|
|
311
379
|
.set(config.defaultHeaders())
|
|
@@ -313,18 +381,29 @@ describe("/rows", () => {
|
|
|
313
381
|
.expect(200)
|
|
314
382
|
expect(res.body.length).toEqual(1)
|
|
315
383
|
expect(res.body[0]._id).toEqual(row._id)
|
|
384
|
+
await assertRowUsage(rowUsage)
|
|
385
|
+
await assertQueryUsage(queryUsage +1)
|
|
316
386
|
})
|
|
317
387
|
|
|
318
388
|
it("should throw an error if view doesn't exist", async () => {
|
|
389
|
+
const rowUsage = await getRowUsage()
|
|
390
|
+
const queryUsage = await getQueryUsage()
|
|
391
|
+
|
|
319
392
|
await request
|
|
320
393
|
.get(`/api/views/derp`)
|
|
321
394
|
.set(config.defaultHeaders())
|
|
322
395
|
.expect(404)
|
|
396
|
+
|
|
397
|
+
await assertRowUsage(rowUsage)
|
|
398
|
+
await assertQueryUsage(queryUsage)
|
|
323
399
|
})
|
|
324
400
|
|
|
325
401
|
it("should be able to run on a view", async () => {
|
|
326
402
|
const view = await config.createView()
|
|
327
403
|
const row = await config.createRow()
|
|
404
|
+
const rowUsage = await getRowUsage()
|
|
405
|
+
const queryUsage = await getQueryUsage()
|
|
406
|
+
|
|
328
407
|
const res = await request
|
|
329
408
|
.get(`/api/views/${view.name}`)
|
|
330
409
|
.set(config.defaultHeaders())
|
|
@@ -332,11 +411,10 @@ describe("/rows", () => {
|
|
|
332
411
|
.expect(200)
|
|
333
412
|
expect(res.body.length).toEqual(1)
|
|
334
413
|
expect(res.body[0]._id).toEqual(row._id)
|
|
335
|
-
})
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
describe("user testing", () => {
|
|
339
414
|
|
|
415
|
+
await assertRowUsage(rowUsage)
|
|
416
|
+
await assertQueryUsage(queryUsage + 1)
|
|
417
|
+
})
|
|
340
418
|
})
|
|
341
419
|
|
|
342
420
|
describe("fetchEnrichedRows", () => {
|
|
@@ -356,6 +434,8 @@ describe("/rows", () => {
|
|
|
356
434
|
})
|
|
357
435
|
return { table, firstRow, secondRow }
|
|
358
436
|
})
|
|
437
|
+
const rowUsage = await getRowUsage()
|
|
438
|
+
const queryUsage = await getQueryUsage()
|
|
359
439
|
|
|
360
440
|
// test basic enrichment
|
|
361
441
|
const resBasic = await request
|
|
@@ -376,6 +456,8 @@ describe("/rows", () => {
|
|
|
376
456
|
expect(resEnriched.body.link[0]._id).toBe(firstRow._id)
|
|
377
457
|
expect(resEnriched.body.link[0].name).toBe("Test Contact")
|
|
378
458
|
expect(resEnriched.body.link[0].description).toBe("original description")
|
|
459
|
+
await assertRowUsage(rowUsage)
|
|
460
|
+
await assertQueryUsage(queryUsage +2)
|
|
379
461
|
})
|
|
380
462
|
})
|
|
381
463
|
|
|
@@ -167,7 +167,6 @@ exports.screenValidator = () => {
|
|
|
167
167
|
_id: Joi.string().required(),
|
|
168
168
|
_component: Joi.string().required(),
|
|
169
169
|
_children: Joi.array().required(),
|
|
170
|
-
_instanceName: Joi.string().required(),
|
|
171
170
|
_styles: Joi.object().required(),
|
|
172
171
|
type: OPTIONAL_STRING,
|
|
173
172
|
table: OPTIONAL_STRING,
|
package/src/constants/index.js
CHANGED
|
@@ -208,5 +208,10 @@ exports.AutomationErrors = {
|
|
|
208
208
|
FAILURE_CONDITION: "FAILURE_CONDITION_MET",
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
+
exports.LoopStepTypes = {
|
|
212
|
+
ARRAY: "Array",
|
|
213
|
+
STRING: "String",
|
|
214
|
+
}
|
|
215
|
+
|
|
211
216
|
// pass through the list from the auth/core lib
|
|
212
217
|
exports.ObjectStoreBuckets = ObjectStoreBuckets
|
|
@@ -8,7 +8,7 @@ const { DocumentTypes } = require("../db/utils")
|
|
|
8
8
|
const { doInTenant } = require("@budibase/backend-core/tenancy")
|
|
9
9
|
const { definitions: triggerDefs } = require("../automations/triggerInfo")
|
|
10
10
|
const { doInAppContext, getAppDB } = require("@budibase/backend-core/context")
|
|
11
|
-
const { AutomationErrors } = require("../constants")
|
|
11
|
+
const { AutomationErrors, LoopStepTypes } = require("../constants")
|
|
12
12
|
const FILTER_STEP_ID = actions.ACTION_DEFINITIONS.FILTER.stepId
|
|
13
13
|
const LOOP_STEP_ID = actions.ACTION_DEFINITIONS.LOOP.stepId
|
|
14
14
|
|
|
@@ -17,6 +17,41 @@ const STOPPED_STATUS = { success: false, status: "STOPPED" }
|
|
|
17
17
|
const { cloneDeep } = require("lodash/fp")
|
|
18
18
|
const env = require("../environment")
|
|
19
19
|
|
|
20
|
+
function typecastForLooping(loopStep, input) {
|
|
21
|
+
if (!input || !input.binding) {
|
|
22
|
+
return null
|
|
23
|
+
}
|
|
24
|
+
const isArray = Array.isArray(input.binding),
|
|
25
|
+
isString = typeof input.binding === "string"
|
|
26
|
+
try {
|
|
27
|
+
switch (loopStep.inputs.option) {
|
|
28
|
+
case LoopStepTypes.ARRAY:
|
|
29
|
+
if (isString) {
|
|
30
|
+
return JSON.parse(input.binding)
|
|
31
|
+
}
|
|
32
|
+
break
|
|
33
|
+
case LoopStepTypes.STRING:
|
|
34
|
+
if (isArray) {
|
|
35
|
+
return input.binding.join(",")
|
|
36
|
+
}
|
|
37
|
+
break
|
|
38
|
+
}
|
|
39
|
+
} catch (err) {
|
|
40
|
+
throw new Error("Unable to cast to correct type")
|
|
41
|
+
}
|
|
42
|
+
return input.binding
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getLoopIterations(loopStep, input) {
|
|
46
|
+
const binding = typecastForLooping(loopStep, input)
|
|
47
|
+
if (!loopStep || !binding) {
|
|
48
|
+
return 1
|
|
49
|
+
}
|
|
50
|
+
return Array.isArray(binding)
|
|
51
|
+
? binding.length
|
|
52
|
+
: automationUtils.stringSplit(binding).length
|
|
53
|
+
}
|
|
54
|
+
|
|
20
55
|
/**
|
|
21
56
|
* The automation orchestrator is a class responsible for executing automations.
|
|
22
57
|
* It handles the context of the automation and makes sure each step gets the correct
|
|
@@ -107,7 +142,9 @@ class Orchestrator {
|
|
|
107
142
|
let loopSteps = []
|
|
108
143
|
for (let step of automation.definition.steps) {
|
|
109
144
|
stepCount++
|
|
110
|
-
let input
|
|
145
|
+
let input,
|
|
146
|
+
iterations = 1,
|
|
147
|
+
iterationCount = 0
|
|
111
148
|
if (step.stepId === LOOP_STEP_ID) {
|
|
112
149
|
loopStep = step
|
|
113
150
|
loopStepNumber = stepCount
|
|
@@ -116,13 +153,9 @@ class Orchestrator {
|
|
|
116
153
|
|
|
117
154
|
if (loopStep) {
|
|
118
155
|
input = await processObject(loopStep.inputs, this._context)
|
|
156
|
+
iterations = getLoopIterations(loopStep, input)
|
|
119
157
|
}
|
|
120
|
-
|
|
121
|
-
? Array.isArray(input.binding)
|
|
122
|
-
? input.binding.length
|
|
123
|
-
: automationUtils.stringSplit(input.binding).length
|
|
124
|
-
: 1
|
|
125
|
-
let iterationCount = 0
|
|
158
|
+
|
|
126
159
|
for (let index = 0; index < iterations; index++) {
|
|
127
160
|
let originalStepInput = cloneDeep(step.inputs)
|
|
128
161
|
|
|
@@ -132,18 +165,11 @@ class Orchestrator {
|
|
|
132
165
|
loopStep.inputs,
|
|
133
166
|
cloneDeep(this._context)
|
|
134
167
|
)
|
|
135
|
-
newInput = automationUtils.cleanInputValues(
|
|
136
|
-
newInput,
|
|
137
|
-
loopStep.schema.inputs
|
|
138
|
-
)
|
|
139
168
|
|
|
140
169
|
let tempOutput = { items: loopSteps, iterations: iterationCount }
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
(loopStep.inputs.option === "String" &&
|
|
145
|
-
typeof newInput.binding !== "string")
|
|
146
|
-
) {
|
|
170
|
+
try {
|
|
171
|
+
newInput.binding = typecastForLooping(loopStep, newInput)
|
|
172
|
+
} catch (err) {
|
|
147
173
|
this.updateContextAndOutput(loopStepNumber, step, tempOutput, {
|
|
148
174
|
status: AutomationErrors.INCORRECT_TYPE,
|
|
149
175
|
success: false,
|
|
@@ -205,21 +231,13 @@ class Orchestrator {
|
|
|
205
231
|
}
|
|
206
232
|
|
|
207
233
|
let isFailure = false
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
this._context.steps[loopStepNumber].currentItem
|
|
213
|
-
).some(value => {
|
|
214
|
-
return (
|
|
215
|
-
this._context.steps[loopStepNumber].currentItem[value] ===
|
|
216
|
-
loopStep.inputs.failure
|
|
217
|
-
)
|
|
234
|
+
const currentItem = this._context.steps[loopStepNumber]?.currentItem
|
|
235
|
+
if (currentItem && typeof currentItem === "object") {
|
|
236
|
+
isFailure = Object.keys(currentItem).some(value => {
|
|
237
|
+
return currentItem[value] === loopStep.inputs.failure
|
|
218
238
|
})
|
|
219
239
|
} else {
|
|
220
|
-
isFailure =
|
|
221
|
-
this._context.steps[loopStepNumber]?.currentItem ===
|
|
222
|
-
loopStep.inputs.failure
|
|
240
|
+
isFailure = currentItem && currentItem === loopStep.inputs.failure
|
|
223
241
|
}
|
|
224
242
|
|
|
225
243
|
if (isFailure) {
|