@lenne.tech/nest-server 9.0.9 → 9.0.11
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/dist/config.env.js +3 -0
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/helpers/db.helper.d.ts +1 -0
- package/dist/core/common/helpers/db.helper.js +17 -1
- package/dist/core/common/helpers/db.helper.js.map +1 -1
- package/dist/core/common/helpers/filter.helper.d.ts +3 -1
- package/dist/core/common/helpers/filter.helper.js +21 -6
- package/dist/core/common/helpers/filter.helper.js.map +1 -1
- package/dist/core/common/inputs/single-filter.input.d.ts +1 -0
- package/dist/core/common/inputs/single-filter.input.js +8 -0
- package/dist/core/common/inputs/single-filter.input.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +1 -0
- package/dist/core/common/services/crud.service.js +3 -3
- package/dist/core/common/services/crud.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/config.env.ts +3 -0
- package/src/core/common/helpers/db.helper.ts +19 -1
- package/src/core/common/helpers/filter.helper.ts +31 -8
- package/src/core/common/inputs/single-filter.input.ts +9 -0
- package/src/core/common/interfaces/server-options.interface.ts +7 -0
- package/src/core/common/services/crud.service.ts +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "9.0.
|
|
3
|
+
"version": "9.0.11",
|
|
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",
|
|
@@ -102,12 +102,12 @@
|
|
|
102
102
|
"@types/jest": "29.1.2",
|
|
103
103
|
"@types/lodash": "4.14.186",
|
|
104
104
|
"@types/multer": "1.4.7",
|
|
105
|
-
"@types/node": "18.8.
|
|
105
|
+
"@types/node": "18.8.4",
|
|
106
106
|
"@types/nodemailer": "6.4.6",
|
|
107
107
|
"@types/passport": "1.0.11",
|
|
108
108
|
"@types/supertest": "2.0.12",
|
|
109
|
-
"@typescript-eslint/eslint-plugin": "5.
|
|
110
|
-
"@typescript-eslint/parser": "5.
|
|
109
|
+
"@typescript-eslint/eslint-plugin": "5.40.0",
|
|
110
|
+
"@typescript-eslint/parser": "5.40.0",
|
|
111
111
|
"coffeescript": "2.7.0",
|
|
112
112
|
"eslint": "8.25.0",
|
|
113
113
|
"eslint-config-prettier": "8.5.0",
|
package/src/config.env.ts
CHANGED
|
@@ -11,6 +11,7 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
11
11
|
// Local environment
|
|
12
12
|
// ===========================================================================
|
|
13
13
|
local: {
|
|
14
|
+
automaticObjectIdFiltering: true,
|
|
14
15
|
cronJobs: {
|
|
15
16
|
sayHello: {
|
|
16
17
|
cronTime: CronExpression.EVERY_10_SECONDS,
|
|
@@ -72,6 +73,7 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
72
73
|
// Development environment
|
|
73
74
|
// ===========================================================================
|
|
74
75
|
development: {
|
|
76
|
+
automaticObjectIdFiltering: true,
|
|
75
77
|
email: {
|
|
76
78
|
smtp: {
|
|
77
79
|
auth: {
|
|
@@ -124,6 +126,7 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
124
126
|
// Production environment
|
|
125
127
|
// ===========================================================================
|
|
126
128
|
production: {
|
|
129
|
+
automaticObjectIdFiltering: true,
|
|
127
130
|
email: {
|
|
128
131
|
smtp: {
|
|
129
132
|
auth: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FieldNode, GraphQLResolveInfo, SelectionNode } from 'graphql';
|
|
2
2
|
import * as _ from 'lodash';
|
|
3
|
-
import { Document, Model, PopulateOptions, Query,
|
|
3
|
+
import { Document, Model, PopulateOptions, Query, Types } from 'mongoose';
|
|
4
4
|
import { ResolveSelector } from '../interfaces/resolve-selector.interface';
|
|
5
5
|
import { CoreModel } from '../models/core-model.model';
|
|
6
6
|
import { FieldSelection } from '../types/field-selection.type';
|
|
@@ -79,6 +79,24 @@ export function addIds(
|
|
|
79
79
|
return result;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Checks if string is a valid ID or if the string array contains only valid IDs that can be used in mongodb ObjectId
|
|
84
|
+
*/
|
|
85
|
+
export function checkStringIds(ids: string | string[]): boolean {
|
|
86
|
+
if (!Array.isArray(ids)) {
|
|
87
|
+
ids = [ids];
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
for (const id of ids) {
|
|
91
|
+
if (typeof id !== 'string' || id.length !== 24 || !Types.ObjectId.isValid(id)) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return true;
|
|
96
|
+
} catch (e) {}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
82
100
|
/**
|
|
83
101
|
* Checks if all IDs are equal
|
|
84
102
|
*/
|
|
@@ -5,8 +5,9 @@ import { LogicalOperatorEnum } from '../enums/logical-operator.enum';
|
|
|
5
5
|
import { SortOrderEnum } from '../enums/sort-order.emum';
|
|
6
6
|
import { FilterInput } from '../inputs/filter.input';
|
|
7
7
|
import { SortInput } from '../inputs/sort.input';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { ConfigService } from '../services/config.service';
|
|
9
|
+
import { checkStringIds, getObjectIds } from './db.helper';
|
|
10
|
+
import { assignPlain, clone } from './input.helper';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Helper for filter handling
|
|
@@ -110,12 +111,21 @@ export function convertFilterArgsToQuery<T = any>(filterArgs: Partial<FilterArgs
|
|
|
110
111
|
/**
|
|
111
112
|
* Generate filter query
|
|
112
113
|
*/
|
|
113
|
-
export function generateFilterQuery<T = any>(
|
|
114
|
+
export function generateFilterQuery<T = any>(
|
|
115
|
+
filter?: Partial<FilterInput>,
|
|
116
|
+
options?: { automaticObjectIdFiltering?: boolean }
|
|
117
|
+
): FilterQuery<T> | any {
|
|
114
118
|
// Check filter
|
|
115
119
|
if (!filter) {
|
|
116
120
|
return undefined;
|
|
117
121
|
}
|
|
118
122
|
|
|
123
|
+
// Configuration
|
|
124
|
+
const config = {
|
|
125
|
+
automaticObjectIdFiltering: ConfigService.get('automaticObjectIdFiltering'),
|
|
126
|
+
...options,
|
|
127
|
+
};
|
|
128
|
+
|
|
119
129
|
// Init result
|
|
120
130
|
const result: any = {};
|
|
121
131
|
|
|
@@ -124,15 +134,15 @@ export function generateFilterQuery<T = any>(filter?: Partial<FilterInput>): Fil
|
|
|
124
134
|
switch (filter.combinedFilter.logicalOperator) {
|
|
125
135
|
case LogicalOperatorEnum.AND:
|
|
126
136
|
return {
|
|
127
|
-
$and: filter.combinedFilter.filters.map((item: FilterInput) => generateFilterQuery(item)),
|
|
137
|
+
$and: filter.combinedFilter.filters.map((item: FilterInput) => generateFilterQuery(item, options)),
|
|
128
138
|
};
|
|
129
139
|
case LogicalOperatorEnum.NOR:
|
|
130
140
|
return {
|
|
131
|
-
$nor: filter.combinedFilter.filters.map((item: FilterInput) => generateFilterQuery(item)),
|
|
141
|
+
$nor: filter.combinedFilter.filters.map((item: FilterInput) => generateFilterQuery(item, options)),
|
|
132
142
|
};
|
|
133
143
|
case LogicalOperatorEnum.OR:
|
|
134
144
|
return {
|
|
135
|
-
$or: filter.combinedFilter.filters.map((item: FilterInput) => generateFilterQuery(item)),
|
|
145
|
+
$or: filter.combinedFilter.filters.map((item: FilterInput) => generateFilterQuery(item, options)),
|
|
136
146
|
};
|
|
137
147
|
}
|
|
138
148
|
}
|
|
@@ -140,14 +150,27 @@ export function generateFilterQuery<T = any>(filter?: Partial<FilterInput>): Fil
|
|
|
140
150
|
// Process single filter
|
|
141
151
|
if (filter.singleFilter) {
|
|
142
152
|
// Init variables
|
|
143
|
-
const { not, options, field, convertToObjectId } = filter.singleFilter;
|
|
153
|
+
const { not, options, field, convertToObjectId, isReference } = filter.singleFilter;
|
|
144
154
|
let value = filter.singleFilter.value;
|
|
145
155
|
|
|
146
156
|
// Convert value to object ID(s)
|
|
147
|
-
if (convertToObjectId) {
|
|
157
|
+
if (convertToObjectId || isReference) {
|
|
148
158
|
value = getObjectIds(value);
|
|
149
159
|
}
|
|
150
160
|
|
|
161
|
+
// Check if value is a string ID and automatic ObjectID filtering is activated
|
|
162
|
+
else if (config.automaticObjectIdFiltering && checkStringIds(value)) {
|
|
163
|
+
// Set both the string filter and the ObjectID filtering in an OR construction
|
|
164
|
+
const alternativeQuery = clone(filter.singleFilter, { circles: false });
|
|
165
|
+
alternativeQuery.value = getObjectIds(value);
|
|
166
|
+
return {
|
|
167
|
+
$or: [
|
|
168
|
+
generateFilterQuery(filter.singleFilter, Object.assign({}, config, { automaticObjectIdFiltering: false })),
|
|
169
|
+
generateFilterQuery(alternativeQuery, Object.assign({}, config, { automaticObjectIdFiltering: false })),
|
|
170
|
+
],
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
151
174
|
// Convert filter
|
|
152
175
|
switch (filter.singleFilter.operator) {
|
|
153
176
|
case ComparisonOperatorEnum.EQ:
|
|
@@ -23,6 +23,15 @@ export class SingleFilterInput extends CoreInput {
|
|
|
23
23
|
@Field({ description: 'Name of the property to be used for the filter' })
|
|
24
24
|
field: string = undefined;
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Process value as reference
|
|
28
|
+
*/
|
|
29
|
+
@Field({
|
|
30
|
+
description: 'Process value as reference',
|
|
31
|
+
nullable: true,
|
|
32
|
+
})
|
|
33
|
+
isReference?: boolean = undefined;
|
|
34
|
+
|
|
26
35
|
/**
|
|
27
36
|
* [Negate operator](https://docs.mongodb.com/manual/reference/operator/query/not/)
|
|
28
37
|
*/
|
|
@@ -13,6 +13,13 @@ import { MailjetOptions } from './mailjet-options.interface';
|
|
|
13
13
|
* Options for the server
|
|
14
14
|
*/
|
|
15
15
|
export interface IServerOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Automatically detect ObjectIds in string values in FilterQueries
|
|
18
|
+
* and expand them as OR query with string and ObjectId
|
|
19
|
+
* See generateFilterQuery in Filter helper (src/core/common/helpers/filter.helper.ts)
|
|
20
|
+
*/
|
|
21
|
+
automaticObjectIdFiltering?: boolean;
|
|
22
|
+
|
|
16
23
|
/**
|
|
17
24
|
* Cron jobs configuration object with the name of the cron job function as key
|
|
18
25
|
* and the cron expression or config as value
|
|
@@ -197,9 +197,9 @@ export abstract class CrudService<T extends CoreModel = any> extends ModuleServi
|
|
|
197
197
|
aggregation.push({ $facet: facet });
|
|
198
198
|
|
|
199
199
|
// Find and process db items
|
|
200
|
-
const dbResult = (await this.mainDbModel.aggregate(aggregation).exec())[0];
|
|
201
|
-
dbResult.totalCount = dbResult.totalCount[0]
|
|
202
|
-
dbResult.items = dbResult.items
|
|
200
|
+
const dbResult = (await this.mainDbModel.aggregate(aggregation).exec())[0] || {};
|
|
201
|
+
dbResult.totalCount = dbResult.totalCount?.[0]?.total || 0;
|
|
202
|
+
dbResult.items = dbResult.items?.map((item) => this.mainDbModel.hydrate(item)) || [];
|
|
203
203
|
return dbResult;
|
|
204
204
|
},
|
|
205
205
|
{ input: filter, outputPath: 'items', serviceOptions }
|