@lenne.tech/nest-server 10.0.4 → 10.0.6
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 +29 -0
- package/dist/core/common/helpers/filter.helper.js +14 -7
- package/dist/core/common/helpers/filter.helper.js.map +1 -1
- package/dist/core/common/helpers/input.helper.d.ts +2 -1
- package/dist/core/common/helpers/input.helper.js +26 -7
- package/dist/core/common/helpers/input.helper.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +1 -0
- package/dist/core.module.js +2 -0
- package/dist/core.module.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +12 -12
- package/src/core/common/helpers/filter.helper.ts +18 -9
- package/src/core/common/helpers/input.helper.ts +34 -11
- package/src/core/common/interfaces/server-options.interface.ts +17 -1
- package/src/core.module.ts +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "10.0.
|
|
3
|
+
"version": "10.0.6",
|
|
4
4
|
"description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"@nestjs/mongoose": "10.0.1",
|
|
72
72
|
"@nestjs/passport": "10.0.0",
|
|
73
73
|
"@nestjs/platform-express": "10.1.3",
|
|
74
|
-
"@nestjs/schedule": "3.0.
|
|
74
|
+
"@nestjs/schedule": "3.0.2",
|
|
75
75
|
"@nestjs/terminus": "10.0.1",
|
|
76
76
|
"apollo-server-core": "3.11.1",
|
|
77
77
|
"apollo-server-express": "3.11.1",
|
|
@@ -108,30 +108,30 @@
|
|
|
108
108
|
"devDependencies": {
|
|
109
109
|
"@babel/plugin-proposal-private-methods": "7.18.6",
|
|
110
110
|
"@compodoc/compodoc": "1.1.21",
|
|
111
|
-
"@lenne.tech/eslint-config-ts": "0.0.
|
|
111
|
+
"@lenne.tech/eslint-config-ts": "0.0.9",
|
|
112
112
|
"@nestjs/cli": "10.1.11",
|
|
113
|
-
"@nestjs/schematics": "10.0.
|
|
113
|
+
"@nestjs/schematics": "10.0.2",
|
|
114
114
|
"@nestjs/testing": "10.1.3",
|
|
115
115
|
"@swc/cli": "0.1.62",
|
|
116
|
-
"@swc/core": "1.3.
|
|
117
|
-
"@swc/jest": "0.2.
|
|
116
|
+
"@swc/core": "1.3.76",
|
|
117
|
+
"@swc/jest": "0.2.28",
|
|
118
118
|
"@types/compression": "1.7.2",
|
|
119
119
|
"@types/cookie-parser": "1.4.3",
|
|
120
120
|
"@types/cron": "2.0.1",
|
|
121
121
|
"@types/ejs": "3.1.2",
|
|
122
122
|
"@types/express": "4.17.17",
|
|
123
123
|
"@types/jest": "29.5.3",
|
|
124
|
-
"@types/lodash": "4.14.
|
|
124
|
+
"@types/lodash": "4.14.197",
|
|
125
125
|
"@types/multer": "1.4.7",
|
|
126
|
-
"@types/node": "20.4.
|
|
126
|
+
"@types/node": "20.4.9",
|
|
127
127
|
"@types/nodemailer": "6.4.9",
|
|
128
128
|
"@types/passport": "1.0.12",
|
|
129
129
|
"@types/supertest": "2.0.12",
|
|
130
|
-
"@typescript-eslint/eslint-plugin": "6.
|
|
131
|
-
"@typescript-eslint/parser": "6.
|
|
130
|
+
"@typescript-eslint/eslint-plugin": "6.3.0",
|
|
131
|
+
"@typescript-eslint/parser": "6.3.0",
|
|
132
132
|
"coffeescript": "2.7.0",
|
|
133
133
|
"eslint": "8.46.0",
|
|
134
|
-
"eslint-config-prettier": "
|
|
134
|
+
"eslint-config-prettier": "9.0.0",
|
|
135
135
|
"eslint-plugin-unused-imports": "3.0.0",
|
|
136
136
|
"find-file-up": "2.0.1",
|
|
137
137
|
"grunt": "1.6.1",
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
"jest": "29.6.2",
|
|
144
144
|
"npm-watch": "0.11.0",
|
|
145
145
|
"pm2": "5.3.0",
|
|
146
|
-
"prettier": "3.0.
|
|
146
|
+
"prettier": "3.0.1",
|
|
147
147
|
"pretty-quick": "3.1.3",
|
|
148
148
|
"supertest": "6.3.3",
|
|
149
149
|
"ts-jest": "29.1.1",
|
|
@@ -90,8 +90,10 @@ export function findFilter(options?: {
|
|
|
90
90
|
|
|
91
91
|
// Optimizations
|
|
92
92
|
if (!filterOptions[config.type].length) {
|
|
93
|
+
// If there are no conditions, return an empty object
|
|
93
94
|
filterOptions = {};
|
|
94
95
|
} else if (filterOptions[config.type].length === 1) {
|
|
96
|
+
// if there is only one condition, integrate it directly into the filter options
|
|
95
97
|
const additionalProperties = filterOptions[config.type][0];
|
|
96
98
|
delete filterOptions[config.type];
|
|
97
99
|
assignPlain(filterOptions, additionalProperties);
|
|
@@ -150,22 +152,29 @@ export function generateFilterQuery<T = any>(
|
|
|
150
152
|
// Process single filter
|
|
151
153
|
if (filter.singleFilter) {
|
|
152
154
|
// Init variables
|
|
153
|
-
const { not, options,
|
|
155
|
+
const { not, options, convertToObjectId, isReference } = filter.singleFilter;
|
|
156
|
+
let field = filter.singleFilter.field;
|
|
154
157
|
let value = filter.singleFilter.value;
|
|
155
158
|
|
|
156
|
-
// Convert value to object ID(s)
|
|
159
|
+
// Convert value to object ID(s), but don't change the name or the filter itself
|
|
157
160
|
if (convertToObjectId || isReference) {
|
|
158
161
|
value = getObjectIds(value);
|
|
159
162
|
|
|
160
163
|
// Check if value is a string ID and automatic ObjectID filtering is activated
|
|
161
164
|
} else if (config.automaticObjectIdFiltering && checkStringIds(value)) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
165
|
+
if (field === 'id') {
|
|
166
|
+
// Replace field name id with _id and convert value to ObjectId
|
|
167
|
+
field = '_id';
|
|
168
|
+
value = getObjectIds(value);
|
|
169
|
+
} else {
|
|
170
|
+
// For every other fields set both the string filter and the ObjectID filtering in an OR construction
|
|
171
|
+
const alternativeQuery = clone(filter.singleFilter, { circles: false });
|
|
172
|
+
alternativeQuery.value = getObjectIds(value);
|
|
173
|
+
const conf = Object.assign({}, config, { automaticObjectIdFiltering: false });
|
|
174
|
+
return {
|
|
175
|
+
$or: [generateFilterQuery(filter, conf), generateFilterQuery({ singleFilter: alternativeQuery }, conf)],
|
|
176
|
+
};
|
|
177
|
+
}
|
|
169
178
|
}
|
|
170
179
|
|
|
171
180
|
// Convert filter
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as inspector from 'inspector';
|
|
2
|
+
import * as util from 'util';
|
|
1
3
|
import { BadRequestException, UnauthorizedException } from '@nestjs/common';
|
|
2
4
|
import { plainToInstance } from 'class-transformer';
|
|
3
5
|
import { validate } from 'class-validator';
|
|
@@ -323,9 +325,8 @@ export function checkAndGetDate(input: any): Date {
|
|
|
323
325
|
* Clone object
|
|
324
326
|
* @param object Any object
|
|
325
327
|
* @param options Finetuning of rfdc cloning
|
|
326
|
-
* @param options.
|
|
327
|
-
*
|
|
328
|
-
* cloned object (not onto it's prototype, directly onto the object).
|
|
328
|
+
* @param options.checkResult Whether to compare object and cloned object via JSON.stringify and try alternative cloning
|
|
329
|
+
* methods if they are not equal
|
|
329
330
|
* @param options.circles Keeping track of circular references will slow down performance with an additional 25% overhead.
|
|
330
331
|
* Even if an object doesn't have any circular references, the tracking overhead is the cost.
|
|
331
332
|
* By default if an object with a circular reference is passed to rfdc, it will throw
|
|
@@ -333,26 +334,48 @@ export function checkAndGetDate(input: any): Date {
|
|
|
333
334
|
* circular references in the object. If performance is important, try removing the circular
|
|
334
335
|
* reference from the object (set to undefined) and then add it back manually after cloning
|
|
335
336
|
* instead of using this option.
|
|
337
|
+
* @param options.debug Whether to shoe console.debug messages
|
|
338
|
+
* @param options.proto Copy prototype properties as well as own properties into the new object.
|
|
339
|
+
* It's marginally faster to allow enumerable properties on the prototype to be copied into the
|
|
340
|
+
* cloned object (not onto it's prototype, directly onto the object).
|
|
336
341
|
*/
|
|
337
|
-
export function clone(object: any, options?: {
|
|
342
|
+
export function clone(object: any, options?: { checkResult?: boolean; circles?: boolean; proto?: boolean }) {
|
|
338
343
|
const config = {
|
|
339
|
-
|
|
344
|
+
checkResult: true,
|
|
340
345
|
circles: true,
|
|
346
|
+
debug: inspector.url() !== undefined,
|
|
347
|
+
proto: false,
|
|
341
348
|
...options,
|
|
342
349
|
};
|
|
350
|
+
|
|
343
351
|
try {
|
|
344
|
-
|
|
352
|
+
const cloned = rfdc(config)(object);
|
|
353
|
+
if (config.checkResult && !util.isDeepStrictEqual(object, cloned)) {
|
|
354
|
+
throw new Error('Cloned object differs from original object');
|
|
355
|
+
}
|
|
356
|
+
return cloned;
|
|
345
357
|
} catch (e) {
|
|
346
|
-
console.debug(e, config, object, 'automatic try to use rfdc with circles');
|
|
347
358
|
if (!config.circles) {
|
|
359
|
+
if (config.debug) {
|
|
360
|
+
console.debug(e, config, object, 'automatic try to use rfdc with circles');
|
|
361
|
+
}
|
|
348
362
|
try {
|
|
349
|
-
|
|
363
|
+
const clonedWithCircles = rfdc({ ...config, ...{ circles: true } })(object);
|
|
364
|
+
if (config.checkResult && !util.isDeepStrictEqual(object, clonedWithCircles)) {
|
|
365
|
+
throw new Error('Cloned object differs from original object');
|
|
366
|
+
}
|
|
367
|
+
return clonedWithCircles;
|
|
350
368
|
} catch (e) {
|
|
351
|
-
|
|
352
|
-
|
|
369
|
+
if (config.debug) {
|
|
370
|
+
console.debug(e, 'rfcd with circles did not work => automatic use of _.clone!');
|
|
371
|
+
}
|
|
372
|
+
return _.cloneDeep(object);
|
|
353
373
|
}
|
|
354
374
|
} else {
|
|
355
|
-
|
|
375
|
+
if (config.debug) {
|
|
376
|
+
console.debug(e, config, object, 'automatic try to use _.clone instead rfdc');
|
|
377
|
+
}
|
|
378
|
+
return _.cloneDeep(object);
|
|
356
379
|
}
|
|
357
380
|
}
|
|
358
381
|
}
|
|
@@ -60,7 +60,9 @@ export interface IJwt {
|
|
|
60
60
|
export interface IServerOptions {
|
|
61
61
|
/**
|
|
62
62
|
* Automatically detect ObjectIds in string values in FilterQueries
|
|
63
|
-
* and expand them as OR query with string and ObjectId
|
|
63
|
+
* and expand them as OR query with string and ObjectId.
|
|
64
|
+
* Fields with the name "id" are renamed to "_id" and the value is converted to ObjectId,
|
|
65
|
+
* without changing the filter into an OR combined filter.
|
|
64
66
|
* See generateFilterQuery in Filter helper (src/core/common/helpers/filter.helper.ts)
|
|
65
67
|
*/
|
|
66
68
|
automaticObjectIdFiltering?: boolean;
|
|
@@ -319,6 +321,11 @@ export interface IServerOptions {
|
|
|
319
321
|
* Configuration for Mongoose
|
|
320
322
|
*/
|
|
321
323
|
mongoose?: {
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Collation allows users to specify language-specific rules for string comparison,
|
|
327
|
+
* such as rules for letter-case and accent marks.
|
|
328
|
+
*/
|
|
322
329
|
collation?: CollationOptions;
|
|
323
330
|
|
|
324
331
|
/**
|
|
@@ -336,6 +343,15 @@ export interface IServerOptions {
|
|
|
336
343
|
* Mongoose module options
|
|
337
344
|
*/
|
|
338
345
|
options?: MongooseModuleOptions;
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Mongoose supports a separate strictQuery option to avoid strict mode for query filters.
|
|
349
|
+
* This is because empty query filters cause Mongoose to return all documents in the model, which can cause issues.
|
|
350
|
+
* See: https://github.com/Automattic/mongoose/issues/10763
|
|
351
|
+
* and: https://mongoosejs.com/docs/guide.html#strictQuery
|
|
352
|
+
* default: false
|
|
353
|
+
*/
|
|
354
|
+
strictQuery?: boolean;
|
|
339
355
|
};
|
|
340
356
|
|
|
341
357
|
/**
|
package/src/core.module.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { GraphQLModule } from '@nestjs/graphql';
|
|
|
5
5
|
import { MongooseModule } from '@nestjs/mongoose';
|
|
6
6
|
import { Context } from 'apollo-server-core';
|
|
7
7
|
import graphqlUploadExpress = require('graphql-upload/graphqlUploadExpress.js');
|
|
8
|
+
import mongoose from 'mongoose';
|
|
8
9
|
import { merge } from './core/common/helpers/config.helper';
|
|
9
10
|
import { IServerOptions } from './core/common/interfaces/server-options.interface';
|
|
10
11
|
import { MapAndValidatePipe } from './core/common/pipes/map-and-validate.pipe';
|
|
@@ -154,6 +155,11 @@ export class CoreModule implements NestModule {
|
|
|
154
155
|
providers.push(ModelDocService);
|
|
155
156
|
}
|
|
156
157
|
|
|
158
|
+
// Set strict query to false by default
|
|
159
|
+
// See: https://github.com/Automattic/mongoose/issues/10763
|
|
160
|
+
// and: https://mongoosejs.com/docs/guide.html#strictQuery
|
|
161
|
+
mongoose.set('strictQuery', config.mongoose.strictQuery || false);
|
|
162
|
+
|
|
157
163
|
const imports: any[] = [
|
|
158
164
|
MongooseModule.forRoot(config.mongoose.uri, config.mongoose.options),
|
|
159
165
|
GraphQLModule.forRootAsync<ApolloDriverConfig>(
|