@opra/mongodb 1.30.1 → 1.31.1

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/README.md CHANGED
@@ -74,5 +74,5 @@ Available under [MIT](LICENSE) license.
74
74
  [downloads-url]: https://npmjs.org/package/@opra/mongodb
75
75
  [ci-test-image]: https://github.com/panates/opra/actions/workflows/test.yml/badge.svg
76
76
  [ci-test-url]: https://github.com/panates/opra/actions/workflows/test.yml
77
- [coveralls-image]: https://coveralls.io/repos/github/panates/opra/badge.svg?branch=main
77
+ [coveralls-image]: https://coveralls.io/repos/github/panates/opra/badge.svg?branch=dev
78
78
  [coveralls-url]: https://coveralls.io/github/panates/opra?branch=main
@@ -1,5 +1,14 @@
1
- import { ApiField, ComplexType } from '@opra/common';
1
+ import { ApiField, ArrayType, ComplexType } from '@opra/common';
2
2
  const FIELD_NAME_PATTERN = /^([-><*:])?(.+)$/;
3
+ /** An array field (`isArray: true`) is declared as `ArrayType(Note)`, not
4
+ * `Note` directly — its own `.type` is the array wrapper. Every "is this
5
+ * an array-of-nested-entity field" check below needs the item type, not
6
+ * the wrapper, however many `[]` layers deep it goes. */
7
+ function unwrapArrayType(type) {
8
+ while (type instanceof ArrayType)
9
+ type = type.type;
10
+ return type;
11
+ }
3
12
  /**
4
13
  * MongoPatchGenerator is responsible for generating MongoDB update patches from DTO objects.
5
14
  * It handles nested objects, array operations ($push, $pull), and field unsetting.
@@ -92,7 +101,8 @@ export class MongoPatchGenerator {
92
101
  result = true;
93
102
  continue;
94
103
  }
95
- if (field.type instanceof ComplexType) {
104
+ const itemType = unwrapArrayType(field.type);
105
+ if (itemType instanceof ComplexType) {
96
106
  if (!value)
97
107
  continue;
98
108
  if (field.isArray) {
@@ -101,7 +111,7 @@ export class MongoPatchGenerator {
101
111
  ctx.initArrayFields.push(pathDot + field.name);
102
112
  if (!value.length)
103
113
  continue;
104
- keyField = field.keyField || field.type.keyField;
114
+ keyField = field.keyField || itemType.keyField;
105
115
  if (keyField) {
106
116
  for (let v of value) {
107
117
  /* Increase arrayIndex and determine a new name for array filter */
@@ -114,7 +124,7 @@ export class MongoPatchGenerator {
114
124
  /* Remove key field from object */
115
125
  delete v[keyField];
116
126
  /* Process each object in array */
117
- if (this._processComplexType(ctx, field.type, pathDot + field.name + `.$[${arrayFilterName}]`, v, scope)) {
127
+ if (this._processComplexType(ctx, itemType, pathDot + field.name + `.$[${arrayFilterName}]`, v, scope)) {
118
128
  result = true;
119
129
  /* Add array filter */
120
130
  ctx.arrayFilters = ctx.arrayFilters || [];
@@ -131,7 +141,7 @@ export class MongoPatchGenerator {
131
141
  if (!(typeof value === 'object'))
132
142
  continue;
133
143
  /* Process nested object */
134
- if (this._processComplexType(ctx, field.type, pathDot + field.name, value, scope)) {
144
+ if (this._processComplexType(ctx, itemType, pathDot + field.name, value, scope)) {
135
145
  result = true;
136
146
  }
137
147
  continue;
@@ -157,13 +167,14 @@ export class MongoPatchGenerator {
157
167
  if (!(field && field.isArray))
158
168
  continue;
159
169
  ctx.$push = ctx.$push || {};
160
- if (field.type instanceof ComplexType) {
161
- keyField = field.keyField || field.type.keyField;
170
+ const itemType = unwrapArrayType(field.type);
171
+ if (itemType instanceof ComplexType) {
172
+ keyField = field.keyField || itemType.keyField;
162
173
  if (keyField) {
163
174
  if (Array.isArray(value)) {
164
175
  value.forEach(v => {
165
176
  if (!v[keyField]) {
166
- throw new TypeError(`You must provide a key value of ${field.type.name} for $push operation.`);
177
+ throw new TypeError(`You must provide a key value of ${itemType.name} for $push operation.`);
167
178
  }
168
179
  });
169
180
  ctx.$push[pathDot + key] = { $each: value };
@@ -171,7 +182,7 @@ export class MongoPatchGenerator {
171
182
  }
172
183
  else {
173
184
  if (!value[keyField]) {
174
- throw new TypeError(`You must provide a key value of ${field.type.name} for $push operation.`);
185
+ throw new TypeError(`You must provide a key value of ${itemType.name} for $push operation.`);
175
186
  }
176
187
  ctx.$push[pathDot + key] = value;
177
188
  result = true;
@@ -200,8 +211,9 @@ export class MongoPatchGenerator {
200
211
  if (!(field && field.isArray))
201
212
  continue;
202
213
  ctx.$pull = ctx.$pull || {};
203
- if (field.type instanceof ComplexType) {
204
- keyField = field.keyField || field.type.keyField;
214
+ const itemType = unwrapArrayType(field.type);
215
+ if (itemType instanceof ComplexType) {
216
+ keyField = field.keyField || itemType.keyField;
205
217
  if (!keyField)
206
218
  continue;
207
219
  ctx.$pull[pathDot + key] = {
@@ -1,5 +1,13 @@
1
- import { ApiField, ComplexType, FieldsProjection, parseFieldsProjection, } from '@opra/common';
1
+ import { ApiField, ArrayType, ComplexType, FieldsProjection, parseFieldsProjection, } from '@opra/common';
2
2
  import mongodb, {} from 'mongodb';
3
+ /** An array field (`isArray: true`) is declared as `ArrayType(Note)`, not
4
+ * `Note` directly — its own `.type` is the array wrapper, not the item
5
+ * type a nested projection needs to recurse into. */
6
+ function unwrapArrayType(type) {
7
+ while (type instanceof ArrayType)
8
+ type = type.type;
9
+ return type;
10
+ }
3
11
  /**
4
12
  * Prepares the MongoDB projection object based on the data type and requested projection.
5
13
  *
@@ -44,10 +52,10 @@ export function prepare(dataType, target, projection, scope) {
44
52
  (defaultFields && field.exclusive && !p)) {
45
53
  continue;
46
54
  }
47
- if (field.type instanceof ComplexType &&
48
- typeof p?.projection === 'object') {
55
+ const itemType = unwrapArrayType(field.type);
56
+ if (itemType instanceof ComplexType && typeof p?.projection === 'object') {
49
57
  target[fieldName] = {};
50
- prepare(field.type, target[fieldName], p.projection);
58
+ prepare(itemType, target[fieldName], p.projection);
51
59
  continue;
52
60
  }
53
61
  target[fieldName] = 1;
package/package.json CHANGED
@@ -1,19 +1,25 @@
1
1
  {
2
2
  "name": "@opra/mongodb",
3
- "version": "1.30.1",
3
+ "version": "1.31.1",
4
4
  "description": "Opra MongoDB adapter package",
5
5
  "author": "Panates",
6
+ "contributors": [
7
+ "Eray Hanoglu <e.hanoglu@panates.com>",
8
+ "Ilker Gurelli <i.gurelli@panates.com>",
9
+ "Onur Tokel <o.tokel@panates.com>",
10
+ "Bircan Yuruk <b.yuruk@panates.com>"
11
+ ],
6
12
  "license": "MIT",
7
13
  "homepage": "https://www.oprajs.com",
8
14
  "dependencies": {
9
- "@jsopen/objects": "^2.3.1",
15
+ "@jsopen/objects": "^2.3.4",
10
16
  "tslib": "^2.8.1",
11
- "valgen": "^7.0.0"
17
+ "valgen": "^7.0.3"
12
18
  },
13
19
  "peerDependencies": {
14
- "@opra/common": "^1.30.1",
15
- "@opra/core": "^1.30.1",
16
- "@opra/http": "^1.30.1",
20
+ "@opra/common": "^1.31.1",
21
+ "@opra/core": "^1.31.1",
22
+ "@opra/http": "^1.31.1",
17
23
  "mongodb": "^7.0.0"
18
24
  },
19
25
  "exports": {
@@ -42,4 +48,4 @@
42
48
  "publishConfig": {
43
49
  "access": "public"
44
50
  }
45
- }
51
+ }
@@ -156,7 +156,11 @@ export declare class MongoNestedService<T extends mongodb.Document> extends Mong
156
156
  /**
157
157
  * Constructs a new instance
158
158
  *
159
- * @param dataType - The data type of the array elements.
159
+ * @param dataType - The data type of the *document* that owns the array
160
+ * field — not the element type. This service reads `fieldName` off it to
161
+ * determine the element type itself (see the `dataType` getter), so
162
+ * passing the element type here leaves it looking for the field on the
163
+ * wrong type and failing with "Field (...) does not exist".
160
164
  * @param fieldName - The name of the field in the document representing the array.
161
165
  * @param [options] - The options for the array service.
162
166
  * @constructor
@@ -1,5 +1,5 @@
1
1
  import { omit } from '@jsopen/objects';
2
- import { ComplexType, NotAcceptableError, ResourceNotAvailableError, } from '@opra/common';
2
+ import { ArrayType, ComplexType, NotAcceptableError, ResourceNotAvailableError, } from '@opra/common';
3
3
  import { ExecutionContext, ServiceBase } from '@opra/core';
4
4
  import mongodb from 'mongodb';
5
5
  import { isNotNullish } from 'valgen';
@@ -31,7 +31,11 @@ export class MongoNestedService extends MongoService {
31
31
  /**
32
32
  * Constructs a new instance
33
33
  *
34
- * @param dataType - The data type of the array elements.
34
+ * @param dataType - The data type of the *document* that owns the array
35
+ * field — not the element type. This service reads `fieldName` off it to
36
+ * determine the element type itself (see the `dataType` getter), so
37
+ * passing the element type here leaves it looking for the field on the
38
+ * wrong type and failing with "Field (...) does not exist".
35
39
  * @param fieldName - The name of the field in the document representing the array.
36
40
  * @param [options] - The options for the array service.
37
41
  * @constructor
@@ -50,7 +54,13 @@ export class MongoNestedService extends MongoService {
50
54
  * @throws {@link NotAcceptableError} If the data type is not a ComplexType.
51
55
  */
52
56
  get dataType() {
53
- const t = super.dataType.getField(this.fieldName, this.scope).type;
57
+ // The field is declared as `ArrayType(Note)` (e.g. `notes: Note[]`) —
58
+ // its own `.type` is the array wrapper, not the item type directly;
59
+ // unwrap it (however many `[]` layers deep) before checking, same as
60
+ // the client-side schema viewer already does for display.
61
+ let t = super.dataType.getField(this.fieldName, this.scope).type;
62
+ while (t instanceof ArrayType)
63
+ t = t.type;
54
64
  if (!(t instanceof ComplexType))
55
65
  throw new NotAcceptableError(`Data type "${t.name}" is not a ComplexType`);
56
66
  return t;