@budibase/server 1.0.160 → 1.0.163
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.f55956ef.js → index.cdd48cbf.js} +3 -3
- package/builder/index.html +1 -1
- package/coverage/clover.xml +96 -88
- package/coverage/coverage-final.json +2 -2
- 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 +24 -24
- package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/utils.js.html +14 -8
- package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +111 -48
- 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 +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 +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 +1 -1
- package/coverage/lcov-report/constants/index.js.html +1 -1
- 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 +19 -19
- 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 +1 -1
- package/coverage/lcov-report/threads/index.html +1 -1
- 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 +164 -148
- package/dist/api/controllers/view/utils.js +4 -2
- package/dist/api/controllers/view/viewBuilder.js +22 -8
- package/dist/integrations/mongodb.js +77 -13
- package/dist/package.json +5 -5
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/src/api/controllers/view/tests/__snapshots__/viewBuilder.spec.js.snap +58 -1
- package/src/api/controllers/view/tests/viewBuilder.spec.js +18 -0
- package/src/api/controllers/view/utils.js +4 -2
- package/src/api/controllers/view/viewBuilder.js +32 -11
- package/src/integrations/mongodb.ts +112 -13
- package/src/integrations/tests/mongo.spec.js +34 -17
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.163",
|
|
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.163",
|
|
73
|
+
"@budibase/client": "^1.0.163",
|
|
74
|
+
"@budibase/pro": "1.0.162",
|
|
75
|
+
"@budibase/string-templates": "^1.0.163",
|
|
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": "6bc08dfaaf5c104032dcb3b6f4d851c0ab051631"
|
|
185
185
|
}
|
|
@@ -1,9 +1,66 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
|
+
exports[`viewBuilder Calculate and filter creates a view with the calculation statistics and filter schema 1`] = `
|
|
4
|
+
Object {
|
|
5
|
+
"map": "function (doc) {
|
|
6
|
+
if ((doc.tableId === \\"14f1c4e94d6a47b682ce89d35d4c78b0\\" && !(
|
|
7
|
+
doc[\\"myField\\"] === undefined ||
|
|
8
|
+
doc[\\"myField\\"] === null ||
|
|
9
|
+
doc[\\"myField\\"] === \\"\\" ||
|
|
10
|
+
(Array.isArray(doc[\\"myField\\"]) && doc[\\"myField\\"].length === 0)
|
|
11
|
+
)) && (doc[\\"age\\"] > 17)) {
|
|
12
|
+
emit(doc[\\"_id\\"], doc[\\"myField\\"]);
|
|
13
|
+
}
|
|
14
|
+
}",
|
|
15
|
+
"meta": Object {
|
|
16
|
+
"calculation": "stats",
|
|
17
|
+
"field": "myField",
|
|
18
|
+
"filters": Array [
|
|
19
|
+
Object {
|
|
20
|
+
"condition": "MT",
|
|
21
|
+
"key": "age",
|
|
22
|
+
"value": 17,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
"groupBy": undefined,
|
|
26
|
+
"schema": Object {
|
|
27
|
+
"avg": Object {
|
|
28
|
+
"type": "number",
|
|
29
|
+
},
|
|
30
|
+
"count": Object {
|
|
31
|
+
"type": "number",
|
|
32
|
+
},
|
|
33
|
+
"field": Object {
|
|
34
|
+
"type": "string",
|
|
35
|
+
},
|
|
36
|
+
"max": Object {
|
|
37
|
+
"type": "number",
|
|
38
|
+
},
|
|
39
|
+
"min": Object {
|
|
40
|
+
"type": "number",
|
|
41
|
+
},
|
|
42
|
+
"sum": Object {
|
|
43
|
+
"type": "number",
|
|
44
|
+
},
|
|
45
|
+
"sumsqr": Object {
|
|
46
|
+
"type": "number",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
"tableId": "14f1c4e94d6a47b682ce89d35d4c78b0",
|
|
50
|
+
},
|
|
51
|
+
"reduce": "_stats",
|
|
52
|
+
}
|
|
53
|
+
`;
|
|
54
|
+
|
|
3
55
|
exports[`viewBuilder Calculate creates a view with the calculation statistics schema 1`] = `
|
|
4
56
|
Object {
|
|
5
57
|
"map": "function (doc) {
|
|
6
|
-
if (doc.tableId === \\"14f1c4e94d6a47b682ce89d35d4c78b0\\"
|
|
58
|
+
if ((doc.tableId === \\"14f1c4e94d6a47b682ce89d35d4c78b0\\" && !(
|
|
59
|
+
doc[\\"myField\\"] === undefined ||
|
|
60
|
+
doc[\\"myField\\"] === null ||
|
|
61
|
+
doc[\\"myField\\"] === \\"\\" ||
|
|
62
|
+
(Array.isArray(doc[\\"myField\\"]) && doc[\\"myField\\"].length === 0)
|
|
63
|
+
)) ) {
|
|
7
64
|
emit(doc[\\"_id\\"], doc[\\"myField\\"]);
|
|
8
65
|
}
|
|
9
66
|
}",
|
|
@@ -44,4 +44,22 @@ describe("viewBuilder", () => {
|
|
|
44
44
|
})).toMatchSnapshot()
|
|
45
45
|
})
|
|
46
46
|
})
|
|
47
|
+
|
|
48
|
+
describe("Calculate and filter", () => {
|
|
49
|
+
it("creates a view with the calculation statistics and filter schema", () => {
|
|
50
|
+
expect(viewTemplate({
|
|
51
|
+
"name": "Calculate View",
|
|
52
|
+
"field": "myField",
|
|
53
|
+
"calculation": "stats",
|
|
54
|
+
"tableId": "14f1c4e94d6a47b682ce89d35d4c78b0",
|
|
55
|
+
"filters": [
|
|
56
|
+
{
|
|
57
|
+
"value": 17,
|
|
58
|
+
"condition": "MT",
|
|
59
|
+
"key": "age",
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
})).toMatchSnapshot()
|
|
63
|
+
})
|
|
64
|
+
})
|
|
47
65
|
});
|
|
@@ -7,6 +7,7 @@ const {
|
|
|
7
7
|
} = require("../../../db/utils")
|
|
8
8
|
const env = require("../../../environment")
|
|
9
9
|
const { getAppDB } = require("@budibase/backend-core/context")
|
|
10
|
+
const viewBuilder = require("./viewBuilder")
|
|
10
11
|
|
|
11
12
|
exports.getView = async viewName => {
|
|
12
13
|
const db = getAppDB()
|
|
@@ -114,7 +115,8 @@ exports.deleteView = async viewName => {
|
|
|
114
115
|
exports.migrateToInMemoryView = async (db, viewName) => {
|
|
115
116
|
// delete the view initially
|
|
116
117
|
const designDoc = await db.get("_design/database")
|
|
117
|
-
|
|
118
|
+
// run the view back through the view builder to update it
|
|
119
|
+
const view = viewBuilder(designDoc.views[viewName].meta)
|
|
118
120
|
delete designDoc.views[viewName]
|
|
119
121
|
await db.put(designDoc)
|
|
120
122
|
await exports.saveView(db, null, viewName, view)
|
|
@@ -123,7 +125,7 @@ exports.migrateToInMemoryView = async (db, viewName) => {
|
|
|
123
125
|
exports.migrateToDesignView = async (db, viewName) => {
|
|
124
126
|
let view = await db.get(generateMemoryViewID(viewName))
|
|
125
127
|
const designDoc = await db.get("_design/database")
|
|
126
|
-
designDoc.views[viewName] = view.view
|
|
128
|
+
designDoc.views[viewName] = viewBuilder(view.view.meta)
|
|
127
129
|
await db.put(designDoc)
|
|
128
130
|
await db.remove(view._id, view._rev)
|
|
129
131
|
}
|
|
@@ -10,6 +10,12 @@ const TOKEN_MAP = {
|
|
|
10
10
|
OR: "||",
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
const CONDITIONS = {
|
|
14
|
+
EMPTY: "EMPTY",
|
|
15
|
+
NOT_EMPTY: "NOT_EMPTY",
|
|
16
|
+
CONTAINS: "CONTAINS",
|
|
17
|
+
}
|
|
18
|
+
|
|
13
19
|
const isEmptyExpression = key => {
|
|
14
20
|
return `(
|
|
15
21
|
doc["${key}"] === undefined ||
|
|
@@ -77,13 +83,13 @@ function parseFilterExpression(filters) {
|
|
|
77
83
|
expression.push(TOKEN_MAP[filter.conjunction])
|
|
78
84
|
}
|
|
79
85
|
|
|
80
|
-
if (filter.condition ===
|
|
86
|
+
if (filter.condition === CONDITIONS.CONTAINS) {
|
|
81
87
|
expression.push(
|
|
82
88
|
`doc["${filter.key}"].${TOKEN_MAP[filter.condition]}("${filter.value}")`
|
|
83
89
|
)
|
|
84
|
-
} else if (filter.condition ===
|
|
90
|
+
} else if (filter.condition === CONDITIONS.EMPTY) {
|
|
85
91
|
expression.push(isEmptyExpression(filter.key))
|
|
86
|
-
} else if (filter.condition ===
|
|
92
|
+
} else if (filter.condition === CONDITIONS.NOT_EMPTY) {
|
|
87
93
|
expression.push(`!${isEmptyExpression(filter.key)}`)
|
|
88
94
|
} else {
|
|
89
95
|
const value =
|
|
@@ -125,22 +131,37 @@ function viewTemplate({ field, tableId, groupBy, filters = [], calculation }) {
|
|
|
125
131
|
if (filters && filters.length > 0 && filters[0].conjunction) {
|
|
126
132
|
delete filters[0].conjunction
|
|
127
133
|
}
|
|
128
|
-
const parsedFilters = parseFilterExpression(filters)
|
|
129
|
-
const filterExpression = parsedFilters ? `&& (${parsedFilters})` : ""
|
|
130
|
-
|
|
131
|
-
const emitExpression = parseEmitExpression(field, groupBy)
|
|
132
|
-
|
|
133
|
-
const reduction = field && calculation ? { reduce: `_${calculation}` } : {}
|
|
134
134
|
|
|
135
|
-
let schema = null
|
|
135
|
+
let schema = null,
|
|
136
|
+
statFilter = null
|
|
136
137
|
|
|
137
138
|
if (calculation) {
|
|
138
139
|
schema = {
|
|
139
140
|
...(groupBy ? GROUP_PROPERTY : FIELD_PROPERTY),
|
|
140
141
|
...SCHEMA_MAP[calculation],
|
|
141
142
|
}
|
|
143
|
+
if (
|
|
144
|
+
!filters.find(
|
|
145
|
+
filter =>
|
|
146
|
+
filter.key === field && filter.condition === CONDITIONS.NOT_EMPTY
|
|
147
|
+
)
|
|
148
|
+
) {
|
|
149
|
+
statFilter = parseFilterExpression([
|
|
150
|
+
{ key: field, condition: CONDITIONS.NOT_EMPTY },
|
|
151
|
+
])
|
|
152
|
+
}
|
|
142
153
|
}
|
|
143
154
|
|
|
155
|
+
const parsedFilters = parseFilterExpression(filters)
|
|
156
|
+
const filterExpression = parsedFilters ? `&& (${parsedFilters})` : ""
|
|
157
|
+
|
|
158
|
+
const emitExpression = parseEmitExpression(field, groupBy)
|
|
159
|
+
const tableExpression = `doc.tableId === "${tableId}"`
|
|
160
|
+
const coreExpression = statFilter
|
|
161
|
+
? `(${tableExpression} && ${statFilter})`
|
|
162
|
+
: tableExpression
|
|
163
|
+
const reduction = field && calculation ? { reduce: `_${calculation}` } : {}
|
|
164
|
+
|
|
144
165
|
return {
|
|
145
166
|
meta: {
|
|
146
167
|
field,
|
|
@@ -151,7 +172,7 @@ function viewTemplate({ field, tableId, groupBy, filters = [], calculation }) {
|
|
|
151
172
|
calculation,
|
|
152
173
|
},
|
|
153
174
|
map: `function (doc) {
|
|
154
|
-
if (
|
|
175
|
+
if (${coreExpression} ${filterExpression}) {
|
|
155
176
|
${emitExpression}
|
|
156
177
|
}
|
|
157
178
|
}`,
|
|
@@ -4,10 +4,18 @@ import {
|
|
|
4
4
|
QueryTypes,
|
|
5
5
|
} from "../definitions/datasource"
|
|
6
6
|
import { IntegrationBase } from "./base/IntegrationBase"
|
|
7
|
+
import {
|
|
8
|
+
MongoClient,
|
|
9
|
+
ObjectID,
|
|
10
|
+
FilterQuery,
|
|
11
|
+
UpdateQuery,
|
|
12
|
+
FindOneAndUpdateOption,
|
|
13
|
+
UpdateOneOptions,
|
|
14
|
+
UpdateManyOptions,
|
|
15
|
+
CommonOptions,
|
|
16
|
+
} from "mongodb"
|
|
7
17
|
|
|
8
18
|
module MongoDBModule {
|
|
9
|
-
const { MongoClient } = require("mongodb")
|
|
10
|
-
|
|
11
19
|
interface MongoDBConfig {
|
|
12
20
|
connectionString: string
|
|
13
21
|
db: string
|
|
@@ -76,20 +84,76 @@ module MongoDBModule {
|
|
|
76
84
|
return this.client.connect()
|
|
77
85
|
}
|
|
78
86
|
|
|
87
|
+
createObjectIds(json: any): object {
|
|
88
|
+
const self = this
|
|
89
|
+
function interpolateObjectIds(json: any) {
|
|
90
|
+
for (let field of Object.keys(json)) {
|
|
91
|
+
if (json[field] instanceof Object) {
|
|
92
|
+
json[field] = self.createObjectIds(json[field])
|
|
93
|
+
}
|
|
94
|
+
if (field === "_id" && typeof json[field] === "string") {
|
|
95
|
+
const id = json["_id"].match(
|
|
96
|
+
/(?<=objectid\(['"]).*(?=['"]\))/gi
|
|
97
|
+
)?.[0]
|
|
98
|
+
if (id) {
|
|
99
|
+
json["_id"] = ObjectID.createFromHexString(id)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return json
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (Array.isArray(json)) {
|
|
107
|
+
for (let i = 0; i < json.length; i++) {
|
|
108
|
+
json[i] = interpolateObjectIds(json[i])
|
|
109
|
+
}
|
|
110
|
+
return json
|
|
111
|
+
}
|
|
112
|
+
return interpolateObjectIds(json)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
parseQueryParams(params: string, mode: string) {
|
|
116
|
+
let queryParams = params.split(/(?<=(},)).*{/g)
|
|
117
|
+
let group1 = queryParams[0]
|
|
118
|
+
let group2 = queryParams[2]
|
|
119
|
+
let group3 = queryParams[4]
|
|
120
|
+
if (group1) {
|
|
121
|
+
group1 = JSON.parse(group1.replace(/,+$/, ""))
|
|
122
|
+
}
|
|
123
|
+
if (group2) {
|
|
124
|
+
group2 = JSON.parse("{" + group2.replace(/,+$/, ""))
|
|
125
|
+
}
|
|
126
|
+
if (group3) {
|
|
127
|
+
group3 = JSON.parse("{" + group3.replace(/,+$/, ""))
|
|
128
|
+
}
|
|
129
|
+
if (mode === "update") {
|
|
130
|
+
return {
|
|
131
|
+
filter: group1,
|
|
132
|
+
update: group2,
|
|
133
|
+
options: group3 ?? {},
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
filter: group1,
|
|
138
|
+
options: group2 ?? {},
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
79
142
|
async create(query: { json: object; extra: { [key: string]: string } }) {
|
|
80
143
|
try {
|
|
81
144
|
await this.connect()
|
|
82
145
|
const db = this.client.db(this.config.db)
|
|
83
146
|
const collection = db.collection(query.extra.collection)
|
|
147
|
+
let json = this.createObjectIds(query.json)
|
|
84
148
|
|
|
85
149
|
// For mongodb we add an extra actionType to specify
|
|
86
150
|
// which method we want to call on the collection
|
|
87
151
|
switch (query.extra.actionTypes) {
|
|
88
152
|
case "insertOne": {
|
|
89
|
-
return await collection.insertOne(
|
|
153
|
+
return await collection.insertOne(json)
|
|
90
154
|
}
|
|
91
155
|
case "insertMany": {
|
|
92
|
-
return await collection.
|
|
156
|
+
return await collection.insertMany(json)
|
|
93
157
|
}
|
|
94
158
|
default: {
|
|
95
159
|
throw new Error(
|
|
@@ -110,22 +174,32 @@ module MongoDBModule {
|
|
|
110
174
|
await this.connect()
|
|
111
175
|
const db = this.client.db(this.config.db)
|
|
112
176
|
const collection = db.collection(query.extra.collection)
|
|
177
|
+
let json = this.createObjectIds(query.json)
|
|
113
178
|
|
|
114
179
|
switch (query.extra.actionTypes) {
|
|
115
180
|
case "find": {
|
|
116
|
-
return await collection.find(
|
|
181
|
+
return await collection.find(json).toArray()
|
|
117
182
|
}
|
|
118
183
|
case "findOne": {
|
|
119
|
-
return await collection.findOne(
|
|
184
|
+
return await collection.findOne(json)
|
|
120
185
|
}
|
|
121
186
|
case "findOneAndUpdate": {
|
|
122
|
-
|
|
187
|
+
let findAndUpdateJson = json as {
|
|
188
|
+
filter: FilterQuery<any>
|
|
189
|
+
update: UpdateQuery<any>
|
|
190
|
+
options: FindOneAndUpdateOption<any>
|
|
191
|
+
}
|
|
192
|
+
return await collection.findOneAndUpdate(
|
|
193
|
+
findAndUpdateJson.filter,
|
|
194
|
+
findAndUpdateJson.update,
|
|
195
|
+
findAndUpdateJson.options
|
|
196
|
+
)
|
|
123
197
|
}
|
|
124
198
|
case "count": {
|
|
125
|
-
return await collection.countDocuments(
|
|
199
|
+
return await collection.countDocuments(json)
|
|
126
200
|
}
|
|
127
201
|
case "distinct": {
|
|
128
|
-
return await collection.distinct(
|
|
202
|
+
return await collection.distinct(json)
|
|
129
203
|
}
|
|
130
204
|
default: {
|
|
131
205
|
throw new Error(
|
|
@@ -146,13 +220,30 @@ module MongoDBModule {
|
|
|
146
220
|
await this.connect()
|
|
147
221
|
const db = this.client.db(this.config.db)
|
|
148
222
|
const collection = db.collection(query.extra.collection)
|
|
223
|
+
let queryJson = query.json
|
|
224
|
+
if (typeof queryJson === "string") {
|
|
225
|
+
queryJson = this.parseQueryParams(queryJson, "update")
|
|
226
|
+
}
|
|
227
|
+
let json = this.createObjectIds(queryJson) as {
|
|
228
|
+
filter: FilterQuery<any>
|
|
229
|
+
update: UpdateQuery<any>
|
|
230
|
+
options: object
|
|
231
|
+
}
|
|
149
232
|
|
|
150
233
|
switch (query.extra.actionTypes) {
|
|
151
234
|
case "updateOne": {
|
|
152
|
-
return await collection.updateOne(
|
|
235
|
+
return await collection.updateOne(
|
|
236
|
+
json.filter,
|
|
237
|
+
json.update,
|
|
238
|
+
json.options as UpdateOneOptions
|
|
239
|
+
)
|
|
153
240
|
}
|
|
154
241
|
case "updateMany": {
|
|
155
|
-
return await collection.updateMany(
|
|
242
|
+
return await collection.updateMany(
|
|
243
|
+
json.filter,
|
|
244
|
+
json.update,
|
|
245
|
+
json.options as UpdateManyOptions
|
|
246
|
+
)
|
|
156
247
|
}
|
|
157
248
|
default: {
|
|
158
249
|
throw new Error(
|
|
@@ -173,13 +264,21 @@ module MongoDBModule {
|
|
|
173
264
|
await this.connect()
|
|
174
265
|
const db = this.client.db(this.config.db)
|
|
175
266
|
const collection = db.collection(query.extra.collection)
|
|
267
|
+
let queryJson = query.json
|
|
268
|
+
if (typeof queryJson === "string") {
|
|
269
|
+
queryJson = this.parseQueryParams(queryJson, "delete")
|
|
270
|
+
}
|
|
271
|
+
let json = this.createObjectIds(queryJson) as {
|
|
272
|
+
filter: FilterQuery<any>
|
|
273
|
+
options: CommonOptions
|
|
274
|
+
}
|
|
176
275
|
|
|
177
276
|
switch (query.extra.actionTypes) {
|
|
178
277
|
case "deleteOne": {
|
|
179
|
-
return await collection.deleteOne(
|
|
278
|
+
return await collection.deleteOne(json.filter, json.options)
|
|
180
279
|
}
|
|
181
280
|
case "deleteMany": {
|
|
182
|
-
return await collection.deleteMany(
|
|
281
|
+
return await collection.deleteMany(json.filter, json.options)
|
|
183
282
|
}
|
|
184
283
|
default: {
|
|
185
284
|
throw new Error(
|
|
@@ -4,19 +4,19 @@ jest.mock("mongodb")
|
|
|
4
4
|
|
|
5
5
|
class TestConfiguration {
|
|
6
6
|
constructor(config = {}) {
|
|
7
|
-
this.integration = new MongoDBIntegration.integration(config)
|
|
7
|
+
this.integration = new MongoDBIntegration.integration(config)
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
function disableConsole() {
|
|
12
|
-
jest.spyOn(console,
|
|
13
|
-
console.error.mockImplementation(() => {})
|
|
12
|
+
jest.spyOn(console, "error")
|
|
13
|
+
console.error.mockImplementation(() => {})
|
|
14
14
|
|
|
15
|
-
return console.error.mockRestore
|
|
15
|
+
return console.error.mockRestore
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
describe("MongoDB Integration", () => {
|
|
19
|
-
let config
|
|
19
|
+
let config
|
|
20
20
|
let indexName = "Users"
|
|
21
21
|
|
|
22
22
|
beforeEach(() => {
|
|
@@ -25,12 +25,12 @@ describe("MongoDB Integration", () => {
|
|
|
25
25
|
|
|
26
26
|
it("calls the create method with the correct params", async () => {
|
|
27
27
|
const body = {
|
|
28
|
-
name: "Hello"
|
|
28
|
+
name: "Hello",
|
|
29
29
|
}
|
|
30
30
|
await config.integration.create({
|
|
31
31
|
index: indexName,
|
|
32
32
|
json: body,
|
|
33
|
-
extra: { collection:
|
|
33
|
+
extra: { collection: "testCollection", actionTypes: "insertOne" },
|
|
34
34
|
})
|
|
35
35
|
expect(config.integration.client.insertOne).toHaveBeenCalledWith(body)
|
|
36
36
|
})
|
|
@@ -38,9 +38,9 @@ describe("MongoDB Integration", () => {
|
|
|
38
38
|
it("calls the read method with the correct params", async () => {
|
|
39
39
|
const query = {
|
|
40
40
|
json: {
|
|
41
|
-
address: "test"
|
|
41
|
+
address: "test",
|
|
42
42
|
},
|
|
43
|
-
extra: { collection:
|
|
43
|
+
extra: { collection: "testCollection", actionTypes: "find" },
|
|
44
44
|
}
|
|
45
45
|
const response = await config.integration.read(query)
|
|
46
46
|
expect(config.integration.client.find).toHaveBeenCalledWith(query.json)
|
|
@@ -50,30 +50,47 @@ describe("MongoDB Integration", () => {
|
|
|
50
50
|
it("calls the delete method with the correct params", async () => {
|
|
51
51
|
const query = {
|
|
52
52
|
json: {
|
|
53
|
-
|
|
53
|
+
filter: {
|
|
54
|
+
id: "test",
|
|
55
|
+
},
|
|
56
|
+
options: {
|
|
57
|
+
opt: "option"
|
|
58
|
+
}
|
|
54
59
|
},
|
|
55
|
-
extra: { collection:
|
|
60
|
+
extra: { collection: "testCollection", actionTypes: "deleteOne" },
|
|
56
61
|
}
|
|
57
62
|
await config.integration.delete(query)
|
|
58
|
-
expect(config.integration.client.deleteOne).toHaveBeenCalledWith(query.json)
|
|
63
|
+
expect(config.integration.client.deleteOne).toHaveBeenCalledWith(query.json.filter, query.json.options)
|
|
59
64
|
})
|
|
60
65
|
|
|
61
66
|
it("calls the update method with the correct params", async () => {
|
|
62
67
|
const query = {
|
|
63
68
|
json: {
|
|
64
|
-
|
|
69
|
+
filter: {
|
|
70
|
+
id: "test",
|
|
71
|
+
},
|
|
72
|
+
update: {
|
|
73
|
+
name: "TestName",
|
|
74
|
+
},
|
|
75
|
+
options: {
|
|
76
|
+
upsert: false,
|
|
77
|
+
},
|
|
65
78
|
},
|
|
66
|
-
extra: { collection:
|
|
79
|
+
extra: { collection: "testCollection", actionTypes: "updateOne" },
|
|
67
80
|
}
|
|
68
81
|
await config.integration.update(query)
|
|
69
|
-
expect(config.integration.client.updateOne).toHaveBeenCalledWith(
|
|
82
|
+
expect(config.integration.client.updateOne).toHaveBeenCalledWith(
|
|
83
|
+
query.json.filter,
|
|
84
|
+
query.json.update,
|
|
85
|
+
query.json.options
|
|
86
|
+
)
|
|
70
87
|
})
|
|
71
88
|
|
|
72
89
|
it("throws an error when an invalid query.extra.actionType is passed for each method", async () => {
|
|
73
90
|
const restore = disableConsole()
|
|
74
91
|
|
|
75
92
|
const query = {
|
|
76
|
-
extra: { collection:
|
|
93
|
+
extra: { collection: "testCollection", actionTypes: "deleteOne" },
|
|
77
94
|
}
|
|
78
95
|
|
|
79
96
|
let error = null
|
|
@@ -85,4 +102,4 @@ describe("MongoDB Integration", () => {
|
|
|
85
102
|
expect(error).toBeDefined()
|
|
86
103
|
restore()
|
|
87
104
|
})
|
|
88
|
-
})
|
|
105
|
+
})
|