@lenne.tech/nest-server 8.6.17 → 8.6.20
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 +4 -2
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/helpers/common.helper.d.ts +2 -0
- package/dist/core/common/helpers/common.helper.js +16 -0
- package/dist/core/common/helpers/common.helper.js.map +1 -0
- package/dist/core/common/helpers/model.helper.js +8 -6
- package/dist/core/common/helpers/model.helper.js.map +1 -1
- package/dist/core/common/interfaces/cron-job-config.interface.d.ts +2 -0
- package/dist/core/common/services/core-cron-jobs.service.d.ts +1 -0
- package/dist/core/common/services/core-cron-jobs.service.js +34 -9
- package/dist/core/common/services/core-cron-jobs.service.js.map +1 -1
- package/dist/core/common/services/crud.service.d.ts +8 -1
- package/dist/core/common/services/crud.service.js +20 -0
- package/dist/core/common/services/crud.service.js.map +1 -1
- package/dist/core/common/types/remove-methods.type.d.ts +3 -0
- package/dist/core/common/types/remove-methods.type.js +3 -0
- package/dist/core/common/types/remove-methods.type.js.map +1 -0
- package/dist/core/common/types/string-or-object-id.type.d.ts +1 -1
- package/dist/core/modules/file/core-file.service.d.ts +2 -2
- package/dist/core/modules/file/core-file.service.js.map +1 -1
- package/dist/core/modules/file/interfaces/file-service-options.interface.d.ts +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/server/common/services/cron-jobs.service.d.ts +1 -1
- package/dist/server/common/services/cron-jobs.service.js +4 -1
- package/dist/server/common/services/cron-jobs.service.js.map +1 -1
- package/dist/test/test.helper.d.ts +2 -0
- package/dist/test/test.helper.js +38 -1
- package/dist/test/test.helper.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/config.env.ts +4 -2
- package/src/core/common/helpers/common.helper.ts +18 -0
- package/src/core/common/helpers/model.helper.ts +8 -6
- package/src/core/common/interfaces/cron-job-config.interface.ts +15 -2
- package/src/core/common/services/core-cron-jobs.service.ts +48 -9
- package/src/core/common/services/crud.service.ts +37 -2
- package/src/core/common/types/plain-input.type.ts +1 -1
- package/src/core/common/types/remove-methods.type.ts +4 -0
- package/src/core/common/types/string-or-object-id.type.ts +4 -1
- package/src/core/modules/file/core-file.service.ts +2 -2
- package/src/core/modules/file/interfaces/file-service-options.interface.ts +1 -0
- package/src/index.ts +2 -0
- package/src/server/common/services/cron-jobs.service.ts +4 -1
- package/src/test/test.helper.ts +47 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "8.6.
|
|
3
|
+
"version": "8.6.20",
|
|
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",
|
package/src/config.env.ts
CHANGED
|
@@ -12,9 +12,11 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
12
12
|
local: {
|
|
13
13
|
cronJobs: {
|
|
14
14
|
sayHello: {
|
|
15
|
-
cronTime: CronExpression.
|
|
16
|
-
timeZone: 'Europe/Berlin',
|
|
15
|
+
cronTime: CronExpression.EVERY_10_SECONDS,
|
|
17
16
|
runOnInit: false,
|
|
17
|
+
runParallel: 1,
|
|
18
|
+
timeZone: 'Europe/Berlin',
|
|
19
|
+
throwException: false,
|
|
18
20
|
},
|
|
19
21
|
},
|
|
20
22
|
email: {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wait a certain number of milliseconds
|
|
3
|
+
*/
|
|
4
|
+
export function sleep(ms: number) {
|
|
5
|
+
return new Promise<void>((resolve) => {
|
|
6
|
+
setTimeout(() => {
|
|
7
|
+
resolve();
|
|
8
|
+
}, ms);
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Wait a certain number of milliseconds
|
|
14
|
+
* Alias of sleep
|
|
15
|
+
*/
|
|
16
|
+
export function wait(ms: number) {
|
|
17
|
+
return sleep(ms);
|
|
18
|
+
}
|
|
@@ -175,9 +175,10 @@ export function mapClasses<T = Record<string, any>>(
|
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
// Process input
|
|
178
|
-
for (const [prop,
|
|
179
|
-
if (prop in
|
|
180
|
-
const targetClass =
|
|
178
|
+
for (const [prop, mapTarget] of Object.entries(mapping)) {
|
|
179
|
+
if (prop in input) {
|
|
180
|
+
const targetClass = mapTarget as any;
|
|
181
|
+
const value = input[prop];
|
|
181
182
|
|
|
182
183
|
// Process array
|
|
183
184
|
if (Array.isArray(value)) {
|
|
@@ -250,9 +251,10 @@ export async function mapClassesAsync<T = Record<string, any>>(
|
|
|
250
251
|
}
|
|
251
252
|
|
|
252
253
|
// Process input
|
|
253
|
-
for (const [prop,
|
|
254
|
-
if (prop in
|
|
255
|
-
const targetClass =
|
|
254
|
+
for (const [prop, mapTarget] of Object.entries(mapping)) {
|
|
255
|
+
if (prop in input) {
|
|
256
|
+
const targetClass = mapTarget as any;
|
|
257
|
+
const value = input[prop];
|
|
256
258
|
|
|
257
259
|
// Process array
|
|
258
260
|
if (Array.isArray(value)) {
|
|
@@ -24,11 +24,24 @@ export interface CronJobConfig {
|
|
|
24
24
|
onComplete?: CronCommand | null;
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* This will immediately fire your `onTickfunction as soon as the requisit initialization has happened.
|
|
28
|
-
* This option is set to
|
|
27
|
+
* This will immediately fire your `onTickfunction` as soon as the requisit initialization has happened.
|
|
28
|
+
* This option is set to `true` by default.
|
|
29
29
|
*/
|
|
30
30
|
runOnInit?: boolean;
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Depending on how long the execution of a job takes, it may happen that several executions take place at the
|
|
34
|
+
* same time. This can be prevented with `runParallel = false`.This option is set to `true` by default.
|
|
35
|
+
* If a number is specified, it is used as the number of maximum parallel executions.
|
|
36
|
+
*/
|
|
37
|
+
runParallel?: boolean | number;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Whether an exception is thrown or only acknowledged with a console.error.
|
|
41
|
+
* This option is set to `true` by default.
|
|
42
|
+
*/
|
|
43
|
+
throwException?: boolean;
|
|
44
|
+
|
|
32
45
|
/**
|
|
33
46
|
* Specify the timezone for the execution. This will modify the actual time relative to your timezone.
|
|
34
47
|
* If the timezone is invalid, an error is thrown. Can be any string accepted by luxon's `DateTime.setZone()`
|
|
@@ -15,6 +15,11 @@ export abstract class CoreCronJobs {
|
|
|
15
15
|
log: boolean;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Cron jobs that are currently running
|
|
20
|
+
*/
|
|
21
|
+
runningJobs: Record<string, Date[]> = {};
|
|
22
|
+
|
|
18
23
|
// ===================================================================================================================
|
|
19
24
|
// Initializations
|
|
20
25
|
// ===================================================================================================================
|
|
@@ -51,18 +56,20 @@ export abstract class CoreCronJobs {
|
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
// Prepare config
|
|
54
|
-
let
|
|
55
|
-
if (typeof
|
|
56
|
-
|
|
57
|
-
cronTime:
|
|
59
|
+
let conf: CronExpression | string | Date | Falsy | CronJobConfig = CronExpressionOrConfig;
|
|
60
|
+
if (typeof conf === 'string' || conf instanceof Date) {
|
|
61
|
+
conf = {
|
|
62
|
+
cronTime: conf,
|
|
58
63
|
};
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
// Set defaults
|
|
62
|
-
config = {
|
|
63
|
-
timeZone: 'Europe/Berlin',
|
|
67
|
+
const config: CronJobConfig = {
|
|
64
68
|
runOnInit: true,
|
|
65
|
-
|
|
69
|
+
runParallel: true,
|
|
70
|
+
throwException: true,
|
|
71
|
+
timeZone: 'Europe/Berlin',
|
|
72
|
+
...conf,
|
|
66
73
|
};
|
|
67
74
|
|
|
68
75
|
// Check if cron job should be activated
|
|
@@ -81,8 +88,40 @@ export abstract class CoreCronJobs {
|
|
|
81
88
|
// Init cron job
|
|
82
89
|
const job = new CronJob(
|
|
83
90
|
config.cronTime,
|
|
84
|
-
() => {
|
|
85
|
-
|
|
91
|
+
async () => {
|
|
92
|
+
// Get current processes of cron job
|
|
93
|
+
const dates = this.runningJobs[name];
|
|
94
|
+
|
|
95
|
+
// Check if parallel execution is allowed and if so how many can run in parallel
|
|
96
|
+
if (
|
|
97
|
+
dates?.length &&
|
|
98
|
+
(!config.runParallel || (typeof config.runParallel === 'number' && dates.length >= config.runParallel))
|
|
99
|
+
) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Prepare the acquisition of parallel job executions
|
|
104
|
+
if (!this.runningJobs[name]) {
|
|
105
|
+
this.runningJobs[name] = [];
|
|
106
|
+
}
|
|
107
|
+
const date = new Date();
|
|
108
|
+
this.runningJobs[name].push(date);
|
|
109
|
+
|
|
110
|
+
// Execute the job and wait until job process is done
|
|
111
|
+
try {
|
|
112
|
+
await this[name]();
|
|
113
|
+
} catch (e) {
|
|
114
|
+
// Remove job from running list
|
|
115
|
+
this.runningJobs[name] = this.runningJobs[name].filter((item) => item !== date);
|
|
116
|
+
if (config.throwException) {
|
|
117
|
+
throw e;
|
|
118
|
+
} else {
|
|
119
|
+
console.error(e);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Remove job from running list
|
|
124
|
+
this.runningJobs[name] = this.runningJobs[name].filter((item) => item !== date);
|
|
86
125
|
},
|
|
87
126
|
null,
|
|
88
127
|
true,
|
|
@@ -2,6 +2,7 @@ import { NotFoundException } from '@nestjs/common';
|
|
|
2
2
|
import { FilterQuery, QueryOptions } from 'mongoose';
|
|
3
3
|
import { FilterArgs } from '../args/filter.args';
|
|
4
4
|
import { merge } from '../helpers/config.helper';
|
|
5
|
+
import { getStringIds } from '../helpers/db.helper';
|
|
5
6
|
import { convertFilterArgsToQuery } from '../helpers/filter.helper';
|
|
6
7
|
import { assignPlain } from '../helpers/input.helper';
|
|
7
8
|
import { ServiceOptions } from '../interfaces/service-options.interface';
|
|
@@ -58,6 +59,34 @@ export abstract class CrudService<T extends CoreModel = any> extends ModuleServi
|
|
|
58
59
|
);
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Find and update
|
|
64
|
+
*/
|
|
65
|
+
async findAndUpdate(
|
|
66
|
+
filter: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions },
|
|
67
|
+
update: any,
|
|
68
|
+
serviceOptions?: ServiceOptions
|
|
69
|
+
): Promise<T[]> {
|
|
70
|
+
const dbItems: T[] = await this.find(filter, serviceOptions);
|
|
71
|
+
if (!dbItems?.length) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
const promises: Promise<T>[] = [];
|
|
75
|
+
for (const dbItem of dbItems) {
|
|
76
|
+
promises.push(
|
|
77
|
+
new Promise(async (resolve, reject) => {
|
|
78
|
+
try {
|
|
79
|
+
const item = await this.update(getStringIds(dbItem as any), update, serviceOptions);
|
|
80
|
+
resolve(item);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
reject(e);
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
return await Promise.all(promises);
|
|
88
|
+
}
|
|
89
|
+
|
|
61
90
|
/**
|
|
62
91
|
* CRUD alias for get
|
|
63
92
|
*/
|
|
@@ -66,12 +95,18 @@ export abstract class CrudService<T extends CoreModel = any> extends ModuleServi
|
|
|
66
95
|
/**
|
|
67
96
|
* CRUD alias for find
|
|
68
97
|
*/
|
|
69
|
-
async read(
|
|
98
|
+
async read(
|
|
99
|
+
filter: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions },
|
|
100
|
+
serviceOptions?: ServiceOptions
|
|
101
|
+
): Promise<T[]>;
|
|
70
102
|
|
|
71
103
|
/**
|
|
72
104
|
* CRUD alias for get or find
|
|
73
105
|
*/
|
|
74
|
-
async read(
|
|
106
|
+
async read(
|
|
107
|
+
input: string | FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions },
|
|
108
|
+
serviceOptions?: ServiceOptions
|
|
109
|
+
): Promise<T | T[]> {
|
|
75
110
|
if (typeof input === 'string') {
|
|
76
111
|
return this.get(input, serviceOptions);
|
|
77
112
|
} else {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import { Types } from 'mongoose';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Everything which will be used by getStringIds or getObjectIds (see helpers/db.helper.ts)
|
|
5
|
+
*/
|
|
6
|
+
export type StringOrObjectId<T = any> = string | Types.ObjectId | T;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NotFoundException } from '@nestjs/common';
|
|
2
|
-
import { GridFSBucket, GridFSBucketReadStreamOptions } from 'mongodb';
|
|
2
|
+
import { GridFSBucket, GridFSBucketReadStream, GridFSBucketReadStreamOptions } from 'mongodb';
|
|
3
3
|
import { Connection, Types } from 'mongoose';
|
|
4
4
|
import { createBucket, MongoGridFSOptions, MongooseGridFS } from 'mongoose-gridfs';
|
|
5
5
|
import { FilterArgs } from '../../common/args/filter.args';
|
|
@@ -115,7 +115,7 @@ export abstract class CoreFileService {
|
|
|
115
115
|
if (!(await this.checkRights(id, { ...serviceOptions, checkInputType: 'id' }))) {
|
|
116
116
|
return null;
|
|
117
117
|
}
|
|
118
|
-
return this.files.openDownloadStream(getObjectIds(id));
|
|
118
|
+
return this.files.openDownloadStream(getObjectIds(id)) as GridFSBucketReadStream;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
/**
|
package/src/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ export * from './core/common/enums/logical-operator.enum';
|
|
|
18
18
|
export * from './core/common/enums/process-type.enum';
|
|
19
19
|
export * from './core/common/enums/role.enum';
|
|
20
20
|
export * from './core/common/enums/sort-order.emum';
|
|
21
|
+
export * from './core/common/helpers/common.helper';
|
|
21
22
|
export * from './core/common/helpers/config.helper';
|
|
22
23
|
export * from './core/common/helpers/context.helper';
|
|
23
24
|
export * from './core/common/helpers/db.helper';
|
|
@@ -61,6 +62,7 @@ export * from './core/common/types/field-selection.type';
|
|
|
61
62
|
export * from './core/common/types/ids.type';
|
|
62
63
|
export * from './core/common/types/maybe-promise.type';
|
|
63
64
|
export * from './core/common/types/plain-input.type';
|
|
65
|
+
export * from './core/common/types/remove-methods.type';
|
|
64
66
|
export * from './core/common/types/require-only-one.type';
|
|
65
67
|
export * from './core/common/types/required-at-least-one.type';
|
|
66
68
|
export * from './core/common/types/string-or-object-id.type';
|
|
@@ -20,7 +20,10 @@ export class CronJobs extends CoreCronJobs {
|
|
|
20
20
|
// Cron jobs
|
|
21
21
|
// ===================================================================================================================
|
|
22
22
|
|
|
23
|
-
protected sayHello() {
|
|
23
|
+
protected async sayHello() {
|
|
24
24
|
console.log('Hello :)');
|
|
25
|
+
await new Promise<void>((resolve) => {
|
|
26
|
+
setTimeout(() => resolve(), 30000);
|
|
27
|
+
});
|
|
25
28
|
}
|
|
26
29
|
}
|
package/src/test/test.helper.ts
CHANGED
|
@@ -4,10 +4,12 @@ import * as fs from 'fs';
|
|
|
4
4
|
import { createClient } from 'graphql-ws';
|
|
5
5
|
import { jsonToGraphQLQuery } from 'json-to-graphql-query';
|
|
6
6
|
import * as LightMyRequest from 'light-my-request';
|
|
7
|
+
import { Types } from 'mongoose';
|
|
7
8
|
import * as superagent from 'superagent';
|
|
8
9
|
import * as supertest from 'supertest';
|
|
9
10
|
import * as util from 'util';
|
|
10
11
|
import * as ws from 'ws';
|
|
12
|
+
import { getStringIds } from '../core/common/helpers/db.helper';
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* GraphQL request type
|
|
@@ -98,6 +100,11 @@ export interface TestGraphQLOptions {
|
|
|
98
100
|
*/
|
|
99
101
|
logError?: boolean;
|
|
100
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Whether to prepare arguments (like dates)
|
|
105
|
+
*/
|
|
106
|
+
prepareArguments?: boolean;
|
|
107
|
+
|
|
101
108
|
/**
|
|
102
109
|
* Status Code = 400
|
|
103
110
|
*/
|
|
@@ -187,6 +194,7 @@ export class TestHelper {
|
|
|
187
194
|
statusCode: 200,
|
|
188
195
|
log: false,
|
|
189
196
|
logError: false,
|
|
197
|
+
prepareArguments: true,
|
|
190
198
|
...options,
|
|
191
199
|
};
|
|
192
200
|
|
|
@@ -233,7 +241,9 @@ export class TestHelper {
|
|
|
233
241
|
|
|
234
242
|
// Set arguments
|
|
235
243
|
if (graphql.arguments) {
|
|
236
|
-
queryObj[graphql.type][graphql.name].__args =
|
|
244
|
+
queryObj[graphql.type][graphql.name].__args = config.prepareArguments
|
|
245
|
+
? this.prepareArguments(graphql.arguments)
|
|
246
|
+
: graphql.arguments;
|
|
237
247
|
}
|
|
238
248
|
|
|
239
249
|
// Create request payload query
|
|
@@ -330,6 +340,39 @@ export class TestHelper {
|
|
|
330
340
|
return result;
|
|
331
341
|
}
|
|
332
342
|
|
|
343
|
+
/**
|
|
344
|
+
* Prepare arguments
|
|
345
|
+
*/
|
|
346
|
+
prepareArguments(args: any, objects?: WeakMap<any, any>) {
|
|
347
|
+
if (!args) {
|
|
348
|
+
return args;
|
|
349
|
+
}
|
|
350
|
+
if (args instanceof Date) {
|
|
351
|
+
return args.toISOString();
|
|
352
|
+
}
|
|
353
|
+
if (args instanceof Types.ObjectId) {
|
|
354
|
+
return getStringIds(args);
|
|
355
|
+
}
|
|
356
|
+
if (!objects) {
|
|
357
|
+
objects = new WeakMap<any, any>();
|
|
358
|
+
}
|
|
359
|
+
if (typeof args === 'object' && objects.get(args)) {
|
|
360
|
+
return objects.get(args);
|
|
361
|
+
}
|
|
362
|
+
if (Array.isArray(args)) {
|
|
363
|
+
objects.set(args, args);
|
|
364
|
+
return args.map((item) => this.prepareArguments(item, objects));
|
|
365
|
+
}
|
|
366
|
+
if (typeof args === 'object') {
|
|
367
|
+
objects.set(args, args);
|
|
368
|
+
for (const [key, value] of Object.entries(args)) {
|
|
369
|
+
args[key] = this.prepareArguments(value, objects);
|
|
370
|
+
}
|
|
371
|
+
return args;
|
|
372
|
+
}
|
|
373
|
+
return args;
|
|
374
|
+
}
|
|
375
|
+
|
|
333
376
|
/**
|
|
334
377
|
* Prepare GraphQL fields for request
|
|
335
378
|
* @param fields
|
|
@@ -424,6 +467,9 @@ export class TestHelper {
|
|
|
424
467
|
}
|
|
425
468
|
|
|
426
469
|
// Response
|
|
470
|
+
if (log) {
|
|
471
|
+
console.log(requestConfig);
|
|
472
|
+
}
|
|
427
473
|
const response = await (variables ? request : request.send(requestConfig.payload));
|
|
428
474
|
return this.processResponse(response, statusCode, log, logError);
|
|
429
475
|
}
|