@lenne.tech/nest-server 3.1.3 → 3.3.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/dist/core/common/args/filter.args.d.ts +5 -0
- package/dist/core/common/args/filter.args.js +10 -0
- package/dist/core/common/args/filter.args.js.map +1 -1
- package/dist/core/common/args/pagination.args.d.ts +8 -1
- package/dist/core/common/args/pagination.args.js +24 -6
- package/dist/core/common/args/pagination.args.js.map +1 -1
- package/dist/core/common/decorators/restricted.decorator.d.ts +3 -0
- package/dist/core/common/decorators/restricted.decorator.js +16 -5
- package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
- package/dist/core/common/helpers/context.helper.js +15 -5
- package/dist/core/common/helpers/context.helper.js.map +1 -1
- package/dist/core/common/helpers/filter.helper.d.ts +3 -3
- package/dist/core/common/helpers/filter.helper.js.map +1 -1
- package/dist/core/common/helpers/graphql.helper.d.ts +4 -4
- package/dist/core/common/helpers/input.helper.js +13 -7
- package/dist/core/common/helpers/input.helper.js.map +1 -1
- package/dist/core/common/helpers/service.helper.d.ts +11 -8
- package/dist/core/common/helpers/service.helper.js +50 -12
- package/dist/core/common/helpers/service.helper.js.map +1 -1
- package/dist/core/common/inputs/combined-filter.input.d.ts +8 -2
- package/dist/core/common/inputs/combined-filter.input.js +16 -5
- package/dist/core/common/inputs/combined-filter.input.js.map +1 -1
- package/dist/core/common/inputs/core-input.input.d.ts +8 -0
- package/dist/core/common/inputs/core-input.input.js +15 -0
- package/dist/core/common/inputs/core-input.input.js.map +1 -0
- package/dist/core/common/inputs/filter.input.d.ts +7 -1
- package/dist/core/common/inputs/filter.input.js +14 -1
- package/dist/core/common/inputs/filter.input.js.map +1 -1
- package/dist/core/common/inputs/single-filter.input.d.ts +2 -1
- package/dist/core/common/inputs/single-filter.input.js +10 -1
- package/dist/core/common/inputs/single-filter.input.js.map +1 -1
- package/dist/core/common/inputs/sort.input.d.ts +2 -1
- package/dist/core/common/inputs/sort.input.js +7 -1
- package/dist/core/common/inputs/sort.input.js.map +1 -1
- package/dist/core/common/interceptors/check-response.interceptor.js +1 -1
- package/dist/core/common/interceptors/check-response.interceptor.js.map +1 -1
- package/dist/core/common/interfaces/core-persistence-model.interface.d.ts +1 -0
- package/dist/core/common/interfaces/server-options.interface.d.ts +3 -15
- package/dist/core/common/models/core-model.model.d.ts +6 -0
- package/dist/core/common/models/core-model.model.js +14 -5
- package/dist/core/common/models/core-model.model.js.map +1 -1
- package/dist/core/common/models/core-persistence.model.d.ts +5 -29
- package/dist/core/common/models/core-persistence.model.js +19 -41
- package/dist/core/common/models/core-persistence.model.js.map +1 -1
- package/dist/core/common/pipes/check-input.pipe.d.ts +2 -3
- package/dist/core/common/pipes/check-input.pipe.js +5 -20
- package/dist/core/common/pipes/check-input.pipe.js.map +1 -1
- package/dist/core/common/pipes/map-and-validate.pipe.d.ts +4 -0
- package/dist/core/common/pipes/map-and-validate.pipe.js +40 -0
- package/dist/core/common/pipes/map-and-validate.pipe.js.map +1 -0
- package/dist/core/common/types/plain-input.type.d.ts +3 -0
- package/dist/core/common/types/plain-input.type.js +3 -0
- package/dist/core/common/types/plain-input.type.js.map +1 -0
- package/dist/core/modules/auth/core-auth.model.d.ts +3 -1
- package/dist/core/modules/auth/core-auth.model.js +7 -1
- package/dist/core/modules/auth/core-auth.model.js.map +1 -1
- package/dist/core/modules/auth/core-auth.resolver.d.ts +1 -1
- package/dist/core/modules/user/core-user.model.d.ts +3 -0
- package/dist/core/modules/user/core-user.model.js +9 -4
- package/dist/core/modules/user/core-user.model.js.map +1 -1
- package/dist/core/modules/user/core-user.service.d.ts +9 -13
- package/dist/core/modules/user/core-user.service.js +38 -67
- package/dist/core/modules/user/core-user.service.js.map +1 -1
- package/dist/core/modules/user/inputs/core-user-create.input.js +4 -0
- package/dist/core/modules/user/inputs/core-user-create.input.js.map +1 -1
- package/dist/core/modules/user/inputs/core-user.input.d.ts +2 -1
- package/dist/core/modules/user/inputs/core-user.input.js +12 -2
- package/dist/core/modules/user/inputs/core-user.input.js.map +1 -1
- package/dist/core.module.js +4 -4
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/server/common/models/persistence.model.d.ts +4 -2
- package/dist/server/common/models/persistence.model.js +6 -2
- package/dist/server/common/models/persistence.model.js.map +1 -1
- package/dist/server/modules/auth/auth.model.d.ts +1 -0
- package/dist/server/modules/auth/auth.model.js +4 -0
- package/dist/server/modules/auth/auth.model.js.map +1 -1
- package/dist/server/modules/user/inputs/user-create.input.js.map +1 -1
- package/dist/server/modules/user/inputs/user.input.js.map +1 -1
- package/dist/server/modules/user/user.model.d.ts +3 -2
- package/dist/server/modules/user/user.model.js +9 -5
- package/dist/server/modules/user/user.model.js.map +1 -1
- package/dist/server/modules/user/user.resolver.d.ts +2 -2
- package/dist/server/modules/user/user.resolver.js +10 -12
- package/dist/server/modules/user/user.resolver.js.map +1 -1
- package/dist/server/modules/user/user.service.d.ts +6 -6
- package/dist/server/modules/user/user.service.js +12 -4
- package/dist/server/modules/user/user.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +26 -26
- package/src/core/common/args/filter.args.ts +22 -1
- package/src/core/common/args/pagination.args.ts +42 -7
- package/src/core/common/decorators/restricted.decorator.ts +24 -5
- package/src/core/common/helpers/context.helper.ts +14 -3
- package/src/core/common/helpers/filter.helper.ts +3 -3
- package/src/core/common/helpers/input.helper.ts +17 -11
- package/src/core/common/helpers/service.helper.ts +96 -31
- package/src/core/common/inputs/combined-filter.input.ts +30 -9
- package/src/core/common/inputs/core-input.input.ts +36 -0
- package/src/core/common/inputs/filter.input.ts +27 -3
- package/src/core/common/inputs/single-filter.input.ts +7 -6
- package/src/core/common/inputs/sort.input.ts +4 -3
- package/src/core/common/interceptors/check-response.interceptor.ts +2 -2
- package/src/core/common/interfaces/core-persistence-model.interface.ts +1 -0
- package/src/core/common/interfaces/server-options.interface.ts +3 -33
- package/src/core/common/models/core-model.model.ts +42 -3
- package/src/core/common/models/core-persistence.model.ts +33 -120
- package/src/core/common/pipes/check-input.pipe.ts +13 -33
- package/src/core/common/pipes/map-and-validate.pipe.ts +32 -0
- package/src/core/common/types/plain-input.type.ts +6 -0
- package/src/core/modules/auth/core-auth.model.ts +15 -1
- package/src/core/modules/auth/core-auth.resolver.ts +1 -1
- package/src/core/modules/user/core-user.model.ts +16 -4
- package/src/core/modules/user/core-user.service.ts +59 -115
- package/src/core/modules/user/inputs/core-user-create.input.ts +5 -1
- package/src/core/modules/user/inputs/core-user.input.ts +13 -8
- package/src/core.module.ts +13 -6
- package/src/index.ts +6 -2
- package/src/server/common/models/persistence.model.ts +16 -2
- package/src/server/modules/auth/auth.model.ts +13 -0
- package/src/server/modules/user/inputs/user-create.input.ts +4 -0
- package/src/server/modules/user/inputs/user.input.ts +4 -0
- package/src/server/modules/user/user.model.ts +18 -5
- package/src/server/modules/user/user.resolver.ts +15 -19
- package/src/server/modules/user/user.service.ts +22 -7
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Field, InputType } from '@nestjs/graphql';
|
|
2
|
+
import { ModelHelper } from '../helpers/model.helper';
|
|
2
3
|
import { CombinedFilterInput } from './combined-filter.input';
|
|
4
|
+
import { CoreInput } from './core-input.input';
|
|
3
5
|
import { SingleFilterInput } from './single-filter.input';
|
|
4
6
|
|
|
5
7
|
/**
|
|
@@ -8,7 +10,7 @@ import { SingleFilterInput } from './single-filter.input';
|
|
|
8
10
|
@InputType({
|
|
9
11
|
description: 'Input for filtering. The `singleFilter` will be ignored if the `combinedFilter` is set.',
|
|
10
12
|
})
|
|
11
|
-
export class FilterInput {
|
|
13
|
+
export class FilterInput extends CoreInput {
|
|
12
14
|
/**
|
|
13
15
|
* Combination of multiple filters via logical operator
|
|
14
16
|
*/
|
|
@@ -16,7 +18,7 @@ export class FilterInput {
|
|
|
16
18
|
description: 'Combination of multiple filters via logical operator',
|
|
17
19
|
nullable: true,
|
|
18
20
|
})
|
|
19
|
-
combinedFilter?: CombinedFilterInput;
|
|
21
|
+
combinedFilter?: CombinedFilterInput = undefined;
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
24
|
* Filter for a single property
|
|
@@ -25,5 +27,27 @@ export class FilterInput {
|
|
|
25
27
|
description: 'Filter for a single property',
|
|
26
28
|
nullable: true,
|
|
27
29
|
})
|
|
28
|
-
singleFilter?: SingleFilterInput;
|
|
30
|
+
singleFilter?: SingleFilterInput = undefined;
|
|
31
|
+
|
|
32
|
+
// ===================================================================================================================
|
|
33
|
+
// Methods
|
|
34
|
+
// ===================================================================================================================
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Mapping for Subtypes
|
|
38
|
+
*/
|
|
39
|
+
map(
|
|
40
|
+
data: Partial<this> | Record<string, any>,
|
|
41
|
+
options: {
|
|
42
|
+
cloneDeep?: boolean;
|
|
43
|
+
funcAllowed?: boolean;
|
|
44
|
+
mapId?: boolean;
|
|
45
|
+
} = {}
|
|
46
|
+
): this {
|
|
47
|
+
super.map(data, options);
|
|
48
|
+
this.combinedFilter = data.combinedFilter ? CombinedFilterInput.map(data.combinedFilter, options) : undefined;
|
|
49
|
+
this.singleFilter = data.singleFilter ? SingleFilterInput.map(data.singleFilter, options) : undefined;
|
|
50
|
+
Object.keys(this).forEach((key) => this[key] === undefined && delete this[key]);
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
29
53
|
}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { Field, InputType } from '@nestjs/graphql';
|
|
2
2
|
import { ComparisonOperatorEnum } from '../enums/comparison-operator.enum';
|
|
3
3
|
import { JSON } from '../scalars/json.scalar';
|
|
4
|
+
import { CoreInput } from './core-input.input';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Input for a configuration of a filter
|
|
7
8
|
*/
|
|
8
9
|
@InputType({ description: 'Input for a configuration of a filter' })
|
|
9
|
-
export class SingleFilterInput {
|
|
10
|
+
export class SingleFilterInput extends CoreInput {
|
|
10
11
|
/**
|
|
11
12
|
* Name of the property to be used for the filter'
|
|
12
13
|
*/
|
|
13
14
|
@Field({ description: 'Name of the property to be used for the filter' })
|
|
14
|
-
field: string;
|
|
15
|
+
field: string = undefined;
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* [Negate operator](https://docs.mongodb.com/manual/reference/operator/query/not/)
|
|
@@ -20,7 +21,7 @@ export class SingleFilterInput {
|
|
|
20
21
|
description: '[Negate operator](https://docs.mongodb.com/manual/reference/operator/query/not/)',
|
|
21
22
|
nullable: true,
|
|
22
23
|
})
|
|
23
|
-
not?: boolean;
|
|
24
|
+
not?: boolean = undefined;
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* [Comparison operator](https://docs.mongodb.com/manual/reference/operator/query-comparison/)
|
|
@@ -28,7 +29,7 @@ export class SingleFilterInput {
|
|
|
28
29
|
@Field((type) => ComparisonOperatorEnum, {
|
|
29
30
|
description: '[Comparison operator](https://docs.mongodb.com/manual/reference/operator/query-comparison/)',
|
|
30
31
|
})
|
|
31
|
-
operator: ComparisonOperatorEnum;
|
|
32
|
+
operator: ComparisonOperatorEnum = undefined;
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* [Options](https://docs.mongodb.com/manual/reference/operator/query/regex/#op._S_options) for
|
|
@@ -40,8 +41,8 @@ export class SingleFilterInput {
|
|
|
40
41
|
'[REGEX](https://docs.mongodb.com/manual/reference/operator/query/regex/) operator',
|
|
41
42
|
nullable: true,
|
|
42
43
|
})
|
|
43
|
-
options?: string;
|
|
44
|
+
options?: string = undefined;
|
|
44
45
|
|
|
45
46
|
@Field((type) => JSON, { description: 'Value of the property' })
|
|
46
|
-
value: any;
|
|
47
|
+
value: any = undefined;
|
|
47
48
|
}
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { Field, InputType } from '@nestjs/graphql';
|
|
2
2
|
import { SortOrderEnum } from '../enums/sort-order.emum';
|
|
3
|
+
import { CoreInput } from './core-input.input';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Sorting the returned elements
|
|
6
7
|
*/
|
|
7
8
|
@InputType({ description: 'Sorting the returned elements' })
|
|
8
|
-
export class SortInput {
|
|
9
|
+
export class SortInput extends CoreInput {
|
|
9
10
|
/**
|
|
10
11
|
* Field that is to be used for sorting
|
|
11
12
|
*/
|
|
12
13
|
@Field({ description: 'Field that is to be used for sorting' })
|
|
13
|
-
field: string;
|
|
14
|
+
field: string = undefined;
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* SortInput order of the field
|
|
17
18
|
*/
|
|
18
19
|
@Field((type) => SortOrderEnum, { description: 'SortInput order of the field' })
|
|
19
|
-
order: SortOrderEnum;
|
|
20
|
+
order: SortOrderEnum = undefined;
|
|
20
21
|
}
|
|
@@ -19,8 +19,8 @@ export class CheckResponseInterceptor implements NestInterceptor {
|
|
|
19
19
|
// Response interception
|
|
20
20
|
return next.handle().pipe(
|
|
21
21
|
map((data) => {
|
|
22
|
-
// Prepare data for current user
|
|
23
|
-
return checkRestricted(data, currentUser);
|
|
22
|
+
// Prepare response data for current user
|
|
23
|
+
return checkRestricted(data, currentUser, { throwError: false });
|
|
24
24
|
})
|
|
25
25
|
);
|
|
26
26
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ApolloDriverConfig } from '@nestjs/apollo';
|
|
2
2
|
import { JwtModuleOptions } from '@nestjs/jwt';
|
|
3
|
+
import { MongooseModuleOptions } from '@nestjs/mongoose/dist/interfaces/mongoose-options.interface';
|
|
3
4
|
import { ServeStaticOptions } from '@nestjs/platform-express/interfaces/serve-static-options.interface';
|
|
4
5
|
import * as SMTPTransport from 'nodemailer/lib/smtp-transport';
|
|
5
|
-
import { MongooseModuleOptions } from '@nestjs/mongoose/dist/interfaces/mongoose-options.interface';
|
|
6
6
|
import { MailjetOptions } from './mailjet-options.interface';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -20,37 +20,7 @@ export interface IServerOptions {
|
|
|
20
20
|
* see https://docs.nestjs.com/graphql/quick-start
|
|
21
21
|
* and https://www.apollographql.com/docs/apollo-server/api/apollo-server/
|
|
22
22
|
*/
|
|
23
|
-
graphQl?:
|
|
24
|
-
/**
|
|
25
|
-
* Autogenerated schema file
|
|
26
|
-
* e.g. 'schema.gql'
|
|
27
|
-
*/
|
|
28
|
-
autoSchemaFile?: string;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Function for context manipulation
|
|
32
|
-
* e.g. ({ req }) => ({ req })
|
|
33
|
-
*/
|
|
34
|
-
context?: (context: { [key: string]: any; req: any }) => { [key: string]: any; req: any };
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Enables and disables development mode helpers of apollo
|
|
38
|
-
* e.g. true
|
|
39
|
-
*/
|
|
40
|
-
debug?: boolean;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Determines whether or not to install subscription handlers
|
|
44
|
-
* e.g. true
|
|
45
|
-
*/
|
|
46
|
-
installSubscriptionHandlers?: boolean;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Enables and disables schema introspection
|
|
50
|
-
* e.g. true
|
|
51
|
-
*/
|
|
52
|
-
introspection?: boolean;
|
|
53
|
-
} & GqlModuleOptions;
|
|
23
|
+
graphQl?: ApolloDriverConfig;
|
|
54
24
|
|
|
55
25
|
/**
|
|
56
26
|
* Configuration of JavaScript Web Token (JWT) module
|
|
@@ -2,8 +2,23 @@ import { ModelHelper } from '../helpers/model.helper';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Core Model
|
|
5
|
+
*
|
|
6
|
+
* HINT: All properties (in this class and all classes that extend this class) must be initialized with a default
|
|
7
|
+
* value or undefined otherwise the property will not be recognized via Object.keys (this is necessary for mapping).
|
|
8
|
+
* If the property is initialized with a default value (e.g. an empty array or boolean), there is a risk that the
|
|
9
|
+
* current value will be overwritten during mapping without this being intentional, so all values should be initialized
|
|
10
|
+
* with undefined if possible. If necessary and useful, the init method can then be used deliberately:
|
|
11
|
+
* const coreModel = item ? CoreModel.map(item).init() : CoreModel.init();
|
|
5
12
|
*/
|
|
6
13
|
export abstract class CoreModel {
|
|
14
|
+
/**
|
|
15
|
+
* Static init method
|
|
16
|
+
*/
|
|
17
|
+
public static init<T extends CoreModel>(this: new (...args: any[]) => T, ...args: any[]): T {
|
|
18
|
+
const item = new this();
|
|
19
|
+
return item.init(args);
|
|
20
|
+
}
|
|
21
|
+
|
|
7
22
|
/**
|
|
8
23
|
* Static map method
|
|
9
24
|
*/
|
|
@@ -13,13 +28,19 @@ export abstract class CoreModel {
|
|
|
13
28
|
options: {
|
|
14
29
|
cloneDeep?: boolean;
|
|
15
30
|
funcAllowed?: boolean;
|
|
31
|
+
init?: any;
|
|
16
32
|
item?: T;
|
|
17
33
|
mapId?: boolean;
|
|
18
34
|
} = {}
|
|
19
35
|
): T {
|
|
36
|
+
const config = {
|
|
37
|
+
init: true,
|
|
38
|
+
...options,
|
|
39
|
+
};
|
|
40
|
+
|
|
20
41
|
const item = options.item || new this();
|
|
21
42
|
delete options.item;
|
|
22
|
-
return item.map(data,
|
|
43
|
+
return item.map(data, config);
|
|
23
44
|
}
|
|
24
45
|
|
|
25
46
|
/**
|
|
@@ -36,13 +57,27 @@ export abstract class CoreModel {
|
|
|
36
57
|
options: {
|
|
37
58
|
cloneDeep?: boolean;
|
|
38
59
|
funcAllowed?: boolean;
|
|
60
|
+
init?: any;
|
|
39
61
|
item?: T;
|
|
40
62
|
mapId?: boolean;
|
|
41
63
|
} = {}
|
|
42
64
|
): T {
|
|
65
|
+
const config = {
|
|
66
|
+
init: true,
|
|
67
|
+
...options,
|
|
68
|
+
};
|
|
69
|
+
|
|
43
70
|
const item = options.item || new this();
|
|
44
71
|
delete options.item;
|
|
45
|
-
return item.mapDeep(data,
|
|
72
|
+
return item.mapDeep(data, config);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Initialize instance with default values instead of undefined
|
|
77
|
+
* Should be overwritten in child class to organize the defaults
|
|
78
|
+
*/
|
|
79
|
+
public init<T extends CoreModel>(...args: any[]): this {
|
|
80
|
+
return this;
|
|
46
81
|
}
|
|
47
82
|
|
|
48
83
|
/**
|
|
@@ -53,16 +88,18 @@ export abstract class CoreModel {
|
|
|
53
88
|
options: {
|
|
54
89
|
cloneDeep?: boolean;
|
|
55
90
|
funcAllowed?: boolean;
|
|
91
|
+
init?: any;
|
|
56
92
|
mapId?: boolean;
|
|
57
93
|
} = {}
|
|
58
94
|
): this {
|
|
59
95
|
const config = {
|
|
60
96
|
cloneDeep: false,
|
|
61
97
|
funcAllowed: false,
|
|
98
|
+
init: undefined,
|
|
62
99
|
mapId: false,
|
|
63
100
|
...options,
|
|
64
101
|
};
|
|
65
|
-
return ModelHelper.map(data, this, config);
|
|
102
|
+
return config.init ? ModelHelper.map(data, this, config).init(config.init) : ModelHelper.map(data, this, config);
|
|
66
103
|
}
|
|
67
104
|
|
|
68
105
|
/**
|
|
@@ -78,12 +115,14 @@ export abstract class CoreModel {
|
|
|
78
115
|
options: {
|
|
79
116
|
cloneDeep?: boolean;
|
|
80
117
|
funcAllowed?: boolean;
|
|
118
|
+
init?: any;
|
|
81
119
|
mapId?: boolean;
|
|
82
120
|
} = {}
|
|
83
121
|
): this {
|
|
84
122
|
const config = {
|
|
85
123
|
cloneDeep: true,
|
|
86
124
|
funcAllowed: false,
|
|
125
|
+
init: undefined,
|
|
87
126
|
mapId: false,
|
|
88
127
|
...options,
|
|
89
128
|
};
|
|
@@ -1,32 +1,38 @@
|
|
|
1
1
|
import { Field, ID, ObjectType } from '@nestjs/graphql';
|
|
2
|
-
import * as _ from 'lodash';
|
|
3
|
-
import { ModelHelper } from '../helpers/model.helper';
|
|
4
2
|
import { Prop, Schema } from '@nestjs/mongoose';
|
|
5
|
-
import
|
|
3
|
+
import { Types } from 'mongoose';
|
|
4
|
+
import { CoreModel } from './core-model.model';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Metadata for persistent objects
|
|
9
8
|
*
|
|
10
9
|
* The models are a combination of Mongoose Entities and TypeGraphQL Types
|
|
10
|
+
*
|
|
11
|
+
* HINT: All properties (in this class and all classes that extend this class) must be initialized with a default
|
|
12
|
+
* value or undefined otherwise the property will not be recognized via Object.keys (this is necessary for mapping).
|
|
13
|
+
* If the property is initialized with a default value (e.g. an empty array or boolean), there is a risk that the
|
|
14
|
+
* current value will be overwritten during mapping without this being intentional, so all values should be initialized
|
|
15
|
+
* with undefined if possible. If necessary and useful, the init method can then be used deliberately:
|
|
16
|
+
* const corePersistenceModel = item ? CorePersistenceModel.map(item).init() : CorePersistenceModel.init();
|
|
11
17
|
*/
|
|
12
18
|
@ObjectType({
|
|
13
19
|
description: 'Persistence model which will be saved in DB',
|
|
14
20
|
isAbstract: true,
|
|
15
21
|
})
|
|
16
|
-
@Schema()
|
|
17
|
-
export abstract class CorePersistenceModel {
|
|
22
|
+
@Schema({ timestamps: true })
|
|
23
|
+
export abstract class CorePersistenceModel extends CoreModel {
|
|
18
24
|
// ===========================================================================
|
|
19
25
|
// Getter
|
|
20
26
|
// ===========================================================================
|
|
27
|
+
|
|
21
28
|
get _id() {
|
|
22
|
-
return new
|
|
29
|
+
return new Types.ObjectId(this.id);
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
// ===========================================================================
|
|
26
33
|
// Properties
|
|
27
|
-
//
|
|
28
|
-
// TestFields: https://typegraphql.ml/docs/types-and-fields.html
|
|
29
34
|
// ===========================================================================
|
|
35
|
+
|
|
30
36
|
/**
|
|
31
37
|
* ID of the persistence object as string
|
|
32
38
|
*/
|
|
@@ -37,11 +43,11 @@ export abstract class CorePersistenceModel {
|
|
|
37
43
|
id: string = undefined;
|
|
38
44
|
|
|
39
45
|
/**
|
|
40
|
-
* Created date
|
|
46
|
+
* Created date, is set automatically by mongoose
|
|
41
47
|
*/
|
|
42
48
|
@Field({ description: 'Created date', nullable: true })
|
|
43
|
-
@Prop()
|
|
44
|
-
createdAt: Date =
|
|
49
|
+
@Prop({ onCreate: () => new Date() })
|
|
50
|
+
createdAt: Date = undefined;
|
|
45
51
|
|
|
46
52
|
/**
|
|
47
53
|
* Labels of the object
|
|
@@ -51,7 +57,7 @@ export abstract class CorePersistenceModel {
|
|
|
51
57
|
nullable: true,
|
|
52
58
|
})
|
|
53
59
|
@Prop([String])
|
|
54
|
-
labels: string[] =
|
|
60
|
+
labels: string[] = undefined;
|
|
55
61
|
|
|
56
62
|
/**
|
|
57
63
|
* IDs of the Owners
|
|
@@ -61,7 +67,7 @@ export abstract class CorePersistenceModel {
|
|
|
61
67
|
nullable: true,
|
|
62
68
|
})
|
|
63
69
|
@Prop([String])
|
|
64
|
-
ownerIds: string[] =
|
|
70
|
+
ownerIds: string[] = undefined;
|
|
65
71
|
|
|
66
72
|
/**
|
|
67
73
|
* Tags for the object
|
|
@@ -71,122 +77,29 @@ export abstract class CorePersistenceModel {
|
|
|
71
77
|
nullable: true,
|
|
72
78
|
})
|
|
73
79
|
@Prop([String])
|
|
74
|
-
tags: string[] =
|
|
80
|
+
tags: string[] = undefined;
|
|
75
81
|
|
|
76
82
|
/**
|
|
77
|
-
* Updated date
|
|
83
|
+
* Updated date is set automatically by mongoose
|
|
78
84
|
*/
|
|
79
85
|
@Field({ description: 'Updated date', nullable: true })
|
|
80
86
|
@Prop({ onUpdate: () => new Date() })
|
|
81
|
-
updatedAt: Date =
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Static map method
|
|
85
|
-
*/
|
|
86
|
-
public static map<T extends CorePersistenceModel>(
|
|
87
|
-
this: new (...args: any[]) => T,
|
|
88
|
-
data: Partial<T> | Record<string, any>,
|
|
89
|
-
options: {
|
|
90
|
-
cloneDeep?: boolean;
|
|
91
|
-
funcAllowed?: boolean;
|
|
92
|
-
item?: T;
|
|
93
|
-
mapId?: boolean;
|
|
94
|
-
merge?: boolean;
|
|
95
|
-
} = {}
|
|
96
|
-
): T {
|
|
97
|
-
const item = options.item || new this();
|
|
98
|
-
delete options.item;
|
|
99
|
-
return item.map(data, options);
|
|
100
|
-
}
|
|
87
|
+
updatedAt: Date = undefined;
|
|
101
88
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
* Alias for map with cloneDeep = true
|
|
106
|
-
*
|
|
107
|
-
* MapDeep prevents side effects, because objects will be cloned
|
|
108
|
-
* (cloneDeep = true), but it will be slower than a simple map
|
|
109
|
-
*/
|
|
110
|
-
public static mapDeep<T extends CorePersistenceModel>(
|
|
111
|
-
this: new (...args: any[]) => T,
|
|
112
|
-
data: Partial<T> | Record<string, any>,
|
|
113
|
-
options: {
|
|
114
|
-
cloneDeep?: boolean;
|
|
115
|
-
funcAllowed?: boolean;
|
|
116
|
-
item?: T;
|
|
117
|
-
mapId?: boolean;
|
|
118
|
-
merge?: boolean;
|
|
119
|
-
} = {}
|
|
120
|
-
): T {
|
|
121
|
-
const item = options.item || new this();
|
|
122
|
-
delete options.item;
|
|
123
|
-
return item.mapDeep(data, options);
|
|
124
|
-
}
|
|
89
|
+
// ===========================================================================
|
|
90
|
+
// Methods
|
|
91
|
+
// ===========================================================================
|
|
125
92
|
|
|
126
93
|
/**
|
|
127
|
-
*
|
|
94
|
+
* Initialize instance with default values instead of undefined
|
|
128
95
|
*/
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
} = {}
|
|
137
|
-
): this {
|
|
138
|
-
const config = {
|
|
139
|
-
cloneDeep: false,
|
|
140
|
-
funcAllowed: false,
|
|
141
|
-
mapId: false,
|
|
142
|
-
merge: false,
|
|
143
|
-
...options,
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
// Prepare data
|
|
147
|
-
let preparedData = data;
|
|
148
|
-
preparedData = ModelHelper.prepareMap(preparedData, this, config);
|
|
149
|
-
if (config.cloneDeep) {
|
|
150
|
-
preparedData = _.cloneDeep(preparedData);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Assign
|
|
154
|
-
if (this['assign'] !== 'function') {
|
|
155
|
-
if (!config.merge) {
|
|
156
|
-
Object.assign(this, preparedData);
|
|
157
|
-
}
|
|
158
|
-
} else {
|
|
159
|
-
this['assign'](preparedData, { mergeObjects: config.merge });
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Return
|
|
96
|
+
init() {
|
|
97
|
+
super.init();
|
|
98
|
+
this.createdAt = this.createdAt === undefined ? new Date() : this.createdAt;
|
|
99
|
+
this.labels = this.labels === undefined ? [] : this.labels;
|
|
100
|
+
this.ownerIds = this.ownerIds === undefined ? [] : this.ownerIds;
|
|
101
|
+
this.tags = this.tags === undefined ? [] : this.tags;
|
|
102
|
+
this.updatedAt = this.tags === undefined ? this.createdAt : this.updatedAt;
|
|
163
103
|
return this;
|
|
164
104
|
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Map deep method
|
|
168
|
-
*
|
|
169
|
-
* Alias for map with cloneDeep = true
|
|
170
|
-
*
|
|
171
|
-
* MapDeep prevents side effects, because objects will be cloned
|
|
172
|
-
* (cloneDeep = true), but it will be slower than a simple map
|
|
173
|
-
*/
|
|
174
|
-
public mapDeep(
|
|
175
|
-
data: Partial<this> | Record<string, any>,
|
|
176
|
-
options: {
|
|
177
|
-
cloneDeep?: boolean;
|
|
178
|
-
funcAllowed?: boolean;
|
|
179
|
-
mapId?: boolean;
|
|
180
|
-
merge?: boolean;
|
|
181
|
-
} = {}
|
|
182
|
-
): this {
|
|
183
|
-
const config = {
|
|
184
|
-
cloneDeep: true,
|
|
185
|
-
funcAllowed: false,
|
|
186
|
-
mapId: false,
|
|
187
|
-
merge: false,
|
|
188
|
-
...options,
|
|
189
|
-
};
|
|
190
|
-
return this.map(data, config);
|
|
191
|
-
}
|
|
192
105
|
}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { ArgumentMetadata,
|
|
1
|
+
import { ArgumentMetadata, Inject, Injectable, PipeTransform } from '@nestjs/common';
|
|
2
2
|
import { CONTEXT } from '@nestjs/graphql';
|
|
3
|
-
import { plainToClass } from 'class-transformer';
|
|
4
|
-
import { validate, ValidationError } from 'class-validator';
|
|
5
|
-
import { checkRestricted } from '../decorators/restricted.decorator';
|
|
6
3
|
import { Context } from '../helpers/context.helper';
|
|
4
|
+
import { InputHelper } from '../helpers/input.helper';
|
|
7
5
|
|
|
8
6
|
/**
|
|
9
7
|
* The CheckInputPipe checks the permissibility of individual properties of inputs for the resolvers
|
|
10
8
|
* in relation to the current user
|
|
9
|
+
*
|
|
10
|
+
* ATTENTION: Pipe does not work yet, because context is missing: https://github.com/nestjs/graphql/issues/325
|
|
11
|
+
* Once this works MapAndValidate can be replaced in the CoreModule with this pipe.
|
|
11
12
|
*/
|
|
12
|
-
@Injectable(
|
|
13
|
-
export class CheckInputPipe implements PipeTransform
|
|
13
|
+
@Injectable()
|
|
14
|
+
export class CheckInputPipe implements PipeTransform {
|
|
14
15
|
/**
|
|
15
16
|
* Constructor to inject context
|
|
16
17
|
*/
|
|
@@ -19,35 +20,14 @@ export class CheckInputPipe implements PipeTransform<any> {
|
|
|
19
20
|
/**
|
|
20
21
|
* Check input
|
|
21
22
|
*/
|
|
22
|
-
async transform(value: any,
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
return value;
|
|
26
|
-
}
|
|
23
|
+
async transform(value: any, metadata: ArgumentMetadata) {
|
|
24
|
+
// Get meta type
|
|
25
|
+
const metatype = metadata?.metatype;
|
|
27
26
|
|
|
28
|
-
//
|
|
27
|
+
// Get user
|
|
29
28
|
const { user }: any = Context.getData(this.context);
|
|
30
|
-
value = checkRestricted(value, user);
|
|
31
29
|
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
const object = plainToClass(metatype, plainValue);
|
|
35
|
-
const errors: ValidationError[] = await validate(object);
|
|
36
|
-
|
|
37
|
-
// Check errors
|
|
38
|
-
if (errors.length > 0) {
|
|
39
|
-
throw new BadRequestException('Validation failed');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Everything is ok
|
|
43
|
-
return value;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Checks if it is a basic type
|
|
48
|
-
*/
|
|
49
|
-
protected isBasicType(metatype: any): boolean {
|
|
50
|
-
const types = [String, Boolean, Number, Array, Object, Buffer, ArrayBuffer];
|
|
51
|
-
return types.includes(metatype);
|
|
30
|
+
// Check and return
|
|
31
|
+
return InputHelper.check(value, user, metatype);
|
|
52
32
|
}
|
|
53
33
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
|
|
2
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
|
+
import { validate } from 'class-validator';
|
|
4
|
+
import { InputHelper } from '../helpers/input.helper';
|
|
5
|
+
|
|
6
|
+
@Injectable()
|
|
7
|
+
export class MapAndValidatePipe implements PipeTransform {
|
|
8
|
+
async transform(value: any, metadata: ArgumentMetadata) {
|
|
9
|
+
const { metatype } = metadata;
|
|
10
|
+
|
|
11
|
+
if (typeof value !== 'object' || !metatype || InputHelper.isBasicType(metatype)) {
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Convert to metatype
|
|
16
|
+
if (!(value instanceof metatype)) {
|
|
17
|
+
if ((metatype as any)?.map) {
|
|
18
|
+
value = (metatype as any)?.map(value);
|
|
19
|
+
} else {
|
|
20
|
+
value = plainToInstance(metatype, value);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Validate
|
|
25
|
+
const errors = await validate(value);
|
|
26
|
+
if (errors.length > 0) {
|
|
27
|
+
throw new BadRequestException('Input validation failed');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Field, ObjectType } from '@nestjs/graphql';
|
|
2
|
+
import { CoreModel } from '../../common/models/core-model.model';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* CoreAuth model for the response after the sign in
|
|
5
6
|
*/
|
|
6
7
|
@ObjectType({ description: 'CoreAuth', isAbstract: true })
|
|
7
|
-
export class CoreAuthModel {
|
|
8
|
+
export class CoreAuthModel extends CoreModel {
|
|
8
9
|
// ===================================================================================================================
|
|
9
10
|
// Properties
|
|
10
11
|
// ===================================================================================================================
|
|
@@ -14,4 +15,17 @@ export class CoreAuthModel {
|
|
|
14
15
|
*/
|
|
15
16
|
@Field({ description: 'JavaScript Web Token (JWT)' })
|
|
16
17
|
token: string = undefined;
|
|
18
|
+
|
|
19
|
+
// ===================================================================================================================
|
|
20
|
+
// Properties
|
|
21
|
+
// ===================================================================================================================
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Initialize instance with default values instead of undefined
|
|
25
|
+
*/
|
|
26
|
+
init() {
|
|
27
|
+
super.init();
|
|
28
|
+
// Nothing more to initialize yet
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
17
31
|
}
|
|
@@ -20,7 +20,7 @@ export class CoreAuthResolver {
|
|
|
20
20
|
* Get user via ID
|
|
21
21
|
*/
|
|
22
22
|
@Query((returns) => CoreAuthModel, { description: 'Get JWT token' })
|
|
23
|
-
async signIn(@Args('email') email: string, @Args('password') password: string): Promise<CoreAuthModel
|
|
23
|
+
async signIn(@Args('email') email: string, @Args('password') password: string): Promise<Partial<CoreAuthModel>> {
|
|
24
24
|
return await this.authService.signIn(email, password);
|
|
25
25
|
}
|
|
26
26
|
}
|