@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.
Files changed (172) hide show
  1. package/builder/assets/{index.f55956ef.js → index.cdd48cbf.js} +3 -3
  2. package/builder/index.html +1 -1
  3. package/coverage/clover.xml +96 -88
  4. package/coverage/coverage-final.json +2 -2
  5. package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
  6. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  7. package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
  8. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  9. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  10. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  11. package/coverage/lcov-report/api/controllers/component.js.html +1 -1
  12. package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/index.html +1 -1
  15. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  16. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  18. package/coverage/lcov-report/api/controllers/migrations.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/query/index.html +1 -1
  21. package/coverage/lcov-report/api/controllers/query/validation.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/role.js.html +1 -1
  23. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  24. package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
  25. package/coverage/lcov-report/api/controllers/row/index.html +1 -1
  26. package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +1 -1
  29. package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
  30. package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
  31. package/coverage/lcov-report/api/controllers/script.js.html +1 -1
  32. package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +1 -1
  33. package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
  34. package/coverage/lcov-report/api/controllers/table/index.html +1 -1
  35. package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
  36. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  37. package/coverage/lcov-report/api/controllers/user.js.html +1 -1
  38. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  39. package/coverage/lcov-report/api/controllers/view/index.html +24 -24
  40. package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
  41. package/coverage/lcov-report/api/controllers/view/utils.js.html +14 -8
  42. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +111 -48
  43. package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
  44. package/coverage/lcov-report/api/index.html +1 -1
  45. package/coverage/lcov-report/api/index.js.html +1 -1
  46. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  47. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  48. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  49. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  50. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  51. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/index.html +1 -1
  57. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/migrations.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/utils/index.html +1 -1
  71. package/coverage/lcov-report/api/routes/utils/validators.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  74. package/coverage/lcov-report/automations/actions.js.html +1 -1
  75. package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
  76. package/coverage/lcov-report/automations/bullboard.js.html +1 -1
  77. package/coverage/lcov-report/automations/index.html +1 -1
  78. package/coverage/lcov-report/automations/index.js.html +1 -1
  79. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  80. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  81. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  82. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  83. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  84. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  85. package/coverage/lcov-report/automations/steps/index.html +1 -1
  86. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  87. package/coverage/lcov-report/automations/steps/loop.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  89. package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  96. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  97. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  98. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  99. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  100. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  101. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  102. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  103. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  104. package/coverage/lcov-report/automations/triggers.js.html +1 -1
  105. package/coverage/lcov-report/constants/index.html +1 -1
  106. package/coverage/lcov-report/constants/index.js.html +1 -1
  107. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  108. package/coverage/lcov-report/constants/screens.js.html +1 -1
  109. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  110. package/coverage/lcov-report/db/index.html +1 -1
  111. package/coverage/lcov-report/db/index.js.html +1 -1
  112. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
  113. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  114. package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
  115. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  116. package/coverage/lcov-report/db/newid.js.html +1 -1
  117. package/coverage/lcov-report/db/utils.js.html +1 -1
  118. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  119. package/coverage/lcov-report/events/index.html +1 -1
  120. package/coverage/lcov-report/events/index.js.html +1 -1
  121. package/coverage/lcov-report/events/utils.js.html +1 -1
  122. package/coverage/lcov-report/index.html +19 -19
  123. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  124. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  125. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  126. package/coverage/lcov-report/middleware/builder.js.html +1 -1
  127. package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
  128. package/coverage/lcov-report/middleware/index.html +1 -1
  129. package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
  130. package/coverage/lcov-report/middleware/publicApi.js.html +1 -1
  131. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  132. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  133. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  134. package/coverage/lcov-report/threads/automation.js.html +1 -1
  135. package/coverage/lcov-report/threads/index.html +1 -1
  136. package/coverage/lcov-report/threads/query.js.html +1 -1
  137. package/coverage/lcov-report/threads/utils.js.html +1 -1
  138. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  139. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  140. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  141. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  142. package/coverage/lcov-report/utilities/global.js.html +1 -1
  143. package/coverage/lcov-report/utilities/index.html +1 -1
  144. package/coverage/lcov-report/utilities/index.js.html +1 -1
  145. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +1 -1
  146. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  147. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  148. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  149. package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
  150. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
  151. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
  152. package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
  153. package/coverage/lcov-report/utilities/security.js.html +1 -1
  154. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  155. package/coverage/lcov-report/utilities/usageQuota/index.html +1 -1
  156. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +1 -1
  157. package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +1 -1
  158. package/coverage/lcov-report/utilities/users.js.html +1 -1
  159. package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
  160. package/coverage/lcov.info +164 -148
  161. package/dist/api/controllers/view/utils.js +4 -2
  162. package/dist/api/controllers/view/viewBuilder.js +22 -8
  163. package/dist/integrations/mongodb.js +77 -13
  164. package/dist/package.json +5 -5
  165. package/dist/tsconfig.build.tsbuildinfo +1 -1
  166. package/package.json +6 -6
  167. package/src/api/controllers/view/tests/__snapshots__/viewBuilder.spec.js.snap +58 -1
  168. package/src/api/controllers/view/tests/viewBuilder.spec.js +18 -0
  169. package/src/api/controllers/view/utils.js +4 -2
  170. package/src/api/controllers/view/viewBuilder.js +32 -11
  171. package/src/integrations/mongodb.ts +112 -13
  172. 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.160",
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.160",
73
- "@budibase/client": "^1.0.160",
74
- "@budibase/pro": "1.0.159",
75
- "@budibase/string-templates": "^1.0.160",
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": "1c21bf2e0f438431ef8cffe01914666fdf04099f"
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
- const view = designDoc.views[viewName]
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 === "CONTAINS") {
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 === "EMPTY") {
90
+ } else if (filter.condition === CONDITIONS.EMPTY) {
85
91
  expression.push(isEmptyExpression(filter.key))
86
- } else if (filter.condition === "NOT_EMPTY") {
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 (doc.tableId === "${tableId}" ${filterExpression}) {
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(query.json)
153
+ return await collection.insertOne(json)
90
154
  }
91
155
  case "insertMany": {
92
- return await collection.insertOne(query.json).toArray()
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(query.json).toArray()
181
+ return await collection.find(json).toArray()
117
182
  }
118
183
  case "findOne": {
119
- return await collection.findOne(query.json)
184
+ return await collection.findOne(json)
120
185
  }
121
186
  case "findOneAndUpdate": {
122
- return await collection.findOneAndUpdate(query.json)
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(query.json)
199
+ return await collection.countDocuments(json)
126
200
  }
127
201
  case "distinct": {
128
- return await collection.distinct(query.json)
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(query.json)
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(query.json).toArray()
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(query.json)
278
+ return await collection.deleteOne(json.filter, json.options)
180
279
  }
181
280
  case "deleteMany": {
182
- return await collection.deleteMany(query.json).toArray()
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, 'error');
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: 'testCollection', actionTypes: 'insertOne'}
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: 'testCollection', actionTypes: 'find'}
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
- id: "test"
53
+ filter: {
54
+ id: "test",
55
+ },
56
+ options: {
57
+ opt: "option"
58
+ }
54
59
  },
55
- extra: { collection: 'testCollection', actionTypes: 'deleteOne'}
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
- id: "test"
69
+ filter: {
70
+ id: "test",
71
+ },
72
+ update: {
73
+ name: "TestName",
74
+ },
75
+ options: {
76
+ upsert: false,
77
+ },
65
78
  },
66
- extra: { collection: 'testCollection', actionTypes: 'updateOne'}
79
+ extra: { collection: "testCollection", actionTypes: "updateOne" },
67
80
  }
68
81
  await config.integration.update(query)
69
- expect(config.integration.client.updateOne).toHaveBeenCalledWith(query.json)
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: 'testCollection', actionTypes: 'deleteOne'}
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
+ })