@nest-yalc-2/app 2.2.1 → 2.2.4
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/package.json +10 -2
- package/src/__tests__/app-bootstrap-base.helper.spec.ts +86 -38
- package/src/__tests__/app-bootstrap-standalone.helper.spec.ts +35 -35
- package/src/__tests__/app-bootstrap.helper.spec.ts +162 -98
- package/src/__tests__/app-config.service.spec.ts +55 -55
- package/src/__tests__/app-context.service.spec.ts +28 -28
- package/src/__tests__/app.events.spec.ts +12 -12
- package/src/__tests__/app.helper.spec.ts +71 -204
- package/src/__tests__/base-app-module.helper.spec.ts +295 -231
- package/src/__tests__/base-app.controller.spec.ts +90 -90
- package/src/__tests__/base-app.service.spec.ts +39 -39
- package/src/__tests__/cls.module.spec.ts +33 -33
- package/src/__tests__/life-cycle-handler.service.spec.ts +57 -48
- package/src/__tests__/unwrap-result.interceptor.spec.ts +52 -52
- package/src/app-bootstrap-base.helper.ts +206 -206
- package/src/app-bootstrap-standalone.helper.ts +67 -67
- package/src/app-bootstrap.helper.js +9 -9
- package/src/app-bootstrap.helper.ts +279 -279
- package/src/app-config.service.ts +61 -61
- package/src/app-context.module.ts +6 -6
- package/src/app-context.service.ts +16 -16
- package/src/app-imports.factory.js +2 -2
- package/src/app-imports.factory.js.map +1 -1
- package/src/app-imports.factory.ts +321 -321
- package/src/app.events.ts +8 -8
- package/src/app.helper.ts +73 -73
- package/src/base-app-module.helper.d.ts +2 -2
- package/src/base-app-module.helper.js +8 -5
- package/src/base-app-module.helper.js.map +1 -1
- package/src/base-app-module.helper.ts +457 -453
- package/src/base-app.controller.ts +51 -51
- package/src/base-app.interface.ts +55 -55
- package/src/base-app.service.ts +33 -33
- package/src/cls.module.d.ts +2 -1
- package/src/cls.module.js.map +1 -1
- package/src/cls.module.ts +76 -75
- package/src/conf.type.ts +22 -22
- package/src/def.const.ts +33 -33
- package/src/global.enum.ts +6 -6
- package/src/life-cycle-handler.service.ts +66 -66
- package/src/unwrap-result.interceptor.ts +32 -32
- package/src/yalc-controller.interface.ts +6 -6
|
@@ -1,321 +1,321 @@
|
|
|
1
|
-
/* istanbul ignore file */
|
|
2
|
-
|
|
3
|
-
import { ImportType } from '@node-yalc/interfaces/nestjs.type.js';
|
|
4
|
-
import { Logger, ModuleMetadata, Provider } from '@nestjs/common';
|
|
5
|
-
import { HttpModule } from '@nestjs/axios';
|
|
6
|
-
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
7
|
-
import { GraphQLSchema } from 'graphql';
|
|
8
|
-
import {
|
|
9
|
-
wrapSchema,
|
|
10
|
-
RenameTypes,
|
|
11
|
-
RenameRootFields,
|
|
12
|
-
RenameRootTypes,
|
|
13
|
-
RenameInterfaceFields,
|
|
14
|
-
RenameInputObjectFields,
|
|
15
|
-
} from '@graphql-tools/wrap';
|
|
16
|
-
import { AppContextModule } from './app-context.module.js';
|
|
17
|
-
import { AppContextService } from './app-context.service.js';
|
|
18
|
-
import { IDbConfType } from '@nest-yalc-2/database/conf.interface.js';
|
|
19
|
-
import { getConfNameByConnection } from '@nest-yalc-2/database/conn.helper.js';
|
|
20
|
-
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
21
|
-
import { BuildSchemaOptions, GraphQLModule } from '@nestjs/graphql';
|
|
22
|
-
import { JwtModule } from '@nestjs/jwt';
|
|
23
|
-
import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter';
|
|
24
|
-
import { IServiceConf } from './conf.type.js';
|
|
25
|
-
import { GraphQLError } from 'graphql/error';
|
|
26
|
-
import { GraphQLFormattedError } from 'graphql/error';
|
|
27
|
-
// import { AppLoggerModule } from '@nest-yalc-2/logger/app-logger.module.js';
|
|
28
|
-
import { TypeORMLogger } from '@nest-yalc-2/logger/typeorm-logger.js';
|
|
29
|
-
import { CURAPP_CONF_ALIAS } from './def.const.js';
|
|
30
|
-
import { AppEvents } from './app.events.js';
|
|
31
|
-
import { GqlComplexityPlugin } from '@nest-yalc-2/graphql/plugins/gql-complexity.plugin.js';
|
|
32
|
-
import { ClassType } from '@node-yalc/types/globals.js';
|
|
33
|
-
import { isClass } from '@node-yalc/utils/class.helper.js';
|
|
34
|
-
import {
|
|
35
|
-
ApolloFederationDriver,
|
|
36
|
-
ApolloFederationDriverConfig,
|
|
37
|
-
} from '@nestjs/apollo';
|
|
38
|
-
|
|
39
|
-
import * as dotenv from 'dotenv';
|
|
40
|
-
dotenv.config(); // preload .env root file before all the others
|
|
41
|
-
|
|
42
|
-
type ApolloServerPlugin = NonNullable<
|
|
43
|
-
ApolloFederationDriverConfig['plugins']
|
|
44
|
-
>[number];
|
|
45
|
-
|
|
46
|
-
export interface IAppImportsFactory {
|
|
47
|
-
envPath?: string | string[];
|
|
48
|
-
setupEvents?: boolean;
|
|
49
|
-
setupJwt?: boolean;
|
|
50
|
-
keepDBConnectionAlive?: boolean;
|
|
51
|
-
operationPrefix?: string;
|
|
52
|
-
buildSchemaOptions?: BuildSchemaOptions;
|
|
53
|
-
gqlPlugin?: ClassType<ApolloServerPlugin>[];
|
|
54
|
-
disablePlayground?: boolean;
|
|
55
|
-
}
|
|
56
|
-
type PickModuleMetadata = 'imports' | 'providers' | 'exports';
|
|
57
|
-
export type AppModuleMetadata = Required<
|
|
58
|
-
Pick<ModuleMetadata, PickModuleMetadata>
|
|
59
|
-
>;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* This factory function can be reused within tests to
|
|
63
|
-
* create a test app module with the same imports of the actual app
|
|
64
|
-
*/
|
|
65
|
-
export function AppDependencyFactory(
|
|
66
|
-
_context: string,
|
|
67
|
-
confs: any[],
|
|
68
|
-
dbConnNames: string[],
|
|
69
|
-
gqlModules: any[],
|
|
70
|
-
options?: IAppImportsFactory,
|
|
71
|
-
): AppModuleMetadata {
|
|
72
|
-
const {
|
|
73
|
-
setupEvents = true,
|
|
74
|
-
setupJwt = true,
|
|
75
|
-
keepDBConnectionAlive = false,
|
|
76
|
-
buildSchemaOptions = {},
|
|
77
|
-
disablePlayground = false,
|
|
78
|
-
} = options ?? {};
|
|
79
|
-
|
|
80
|
-
const envPath: string[] = [];
|
|
81
|
-
|
|
82
|
-
if (!options?.envPath) {
|
|
83
|
-
envPath.push('.env'); // user-defined env (git-ignored)
|
|
84
|
-
|
|
85
|
-
if (process.env.NODE_ENV) {
|
|
86
|
-
envPath.push(`.env.${process.env.NODE_ENV}`); // user-defined env (git-ignored)
|
|
87
|
-
|
|
88
|
-
// .env.shared is always loaded except in production
|
|
89
|
-
if (process.env.NODE_ENV !== 'production')
|
|
90
|
-
envPath.push('conf/dist/.env.shared');
|
|
91
|
-
|
|
92
|
-
if (process.env.DOCKER_CONTAINER === '1')
|
|
93
|
-
envPath.push(`conf/dist/.env.docker.${process.env.NODE_ENV}`);
|
|
94
|
-
else {
|
|
95
|
-
envPath.push(`conf/dist/.env.local.${process.env.NODE_ENV}`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
Logger.debug?.(`Using ${envPath}, for environment: ${process.env.NODE_ENV}`);
|
|
101
|
-
|
|
102
|
-
const configModule = ConfigModule.forRoot({
|
|
103
|
-
load: confs,
|
|
104
|
-
envFilePath: envPath,
|
|
105
|
-
// isGlobal: true,
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// configModule and AppContextModule must be global to avoid
|
|
109
|
-
// import (re-instantiating) them when they are required by other modules
|
|
110
|
-
const imports: ImportType[] = [
|
|
111
|
-
configModule,
|
|
112
|
-
// AppLoggerModule.forRoot(context, configModule),
|
|
113
|
-
AppContextModule,
|
|
114
|
-
HttpModule,
|
|
115
|
-
];
|
|
116
|
-
|
|
117
|
-
if (dbConnNames.length) {
|
|
118
|
-
dbConnNames.forEach((connName) => {
|
|
119
|
-
imports.push(
|
|
120
|
-
TypeOrmModule.forRootAsync({
|
|
121
|
-
inject: [ConfigService, TypeORMLogger],
|
|
122
|
-
name: connName,
|
|
123
|
-
useFactory: async (
|
|
124
|
-
configService: ConfigService,
|
|
125
|
-
logger: TypeORMLogger,
|
|
126
|
-
) => {
|
|
127
|
-
const conf = configService.get<IDbConfType>(
|
|
128
|
-
getConfNameByConnection(connName),
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
logger.log?.(
|
|
132
|
-
'log',
|
|
133
|
-
`Connecting DB ${connName} to host: ${conf?.host}`,
|
|
134
|
-
);
|
|
135
|
-
return {
|
|
136
|
-
...conf,
|
|
137
|
-
logger: conf?.logging ? logger : undefined,
|
|
138
|
-
maxQueryExecutionTime: 1000,
|
|
139
|
-
keepConnectionAlive: keepDBConnectionAlive,
|
|
140
|
-
};
|
|
141
|
-
},
|
|
142
|
-
}),
|
|
143
|
-
);
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const providers: Provider<any>[] = [];
|
|
148
|
-
|
|
149
|
-
class ApolloPluginsModule {
|
|
150
|
-
static forRoot() {
|
|
151
|
-
return {
|
|
152
|
-
module: ApolloPluginsModule,
|
|
153
|
-
providers: [
|
|
154
|
-
{
|
|
155
|
-
provide: 'APOLLO_PLUGINS',
|
|
156
|
-
useFactory: (...gqlPlugins: ApolloServerPlugin[]) => {
|
|
157
|
-
return gqlPlugins;
|
|
158
|
-
},
|
|
159
|
-
inject: [...(options?.gqlPlugin ?? [])],
|
|
160
|
-
},
|
|
161
|
-
],
|
|
162
|
-
exports: ['APOLLO_PLUGINS'],
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (gqlModules.length) {
|
|
168
|
-
imports.push(
|
|
169
|
-
GraphQLModule.forRootAsync<ApolloFederationDriverConfig>({
|
|
170
|
-
driver: ApolloFederationDriver,
|
|
171
|
-
imports: [ApolloPluginsModule.forRoot()],
|
|
172
|
-
useFactory: async (
|
|
173
|
-
configService: ConfigService,
|
|
174
|
-
appContext: AppContextService,
|
|
175
|
-
eventEmitter: EventEmitter2,
|
|
176
|
-
gqlPluginList: ApolloServerPlugin[],
|
|
177
|
-
) => {
|
|
178
|
-
const conf = configService.get<IServiceConf>(CURAPP_CONF_ALIAS);
|
|
179
|
-
return {
|
|
180
|
-
autoSchemaFile: true,
|
|
181
|
-
buildSchemaOptions,
|
|
182
|
-
// can cause performance issues (https://docs.nestjs.com/graphql/other-features#execute-enhancers-at-the-field-resolver-level)
|
|
183
|
-
// be careful
|
|
184
|
-
fieldResolverEnhancers: ['interceptors', 'guards', 'filters'],
|
|
185
|
-
transformAutoSchemaFile: true,
|
|
186
|
-
transformSchema: (graphQLSchema: GraphQLSchema) => {
|
|
187
|
-
const opPrefix =
|
|
188
|
-
options?.operationPrefix ?? conf?.operationPrefix;
|
|
189
|
-
|
|
190
|
-
const rename = (name: string) => {
|
|
191
|
-
// _service, _entities etc. are used by federation and must not be renamed
|
|
192
|
-
if (!opPrefix || name.startsWith('_')) return name;
|
|
193
|
-
|
|
194
|
-
// orphaned types must keep their non-prefixed name
|
|
195
|
-
if (
|
|
196
|
-
options?.buildSchemaOptions?.orphanedTypes?.some(
|
|
197
|
-
(cl) => isClass(cl) && cl.name === name,
|
|
198
|
-
)
|
|
199
|
-
)
|
|
200
|
-
return name;
|
|
201
|
-
|
|
202
|
-
return `${opPrefix}_${name}`;
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
const transformed = wrapSchema({
|
|
206
|
-
schema: graphQLSchema,
|
|
207
|
-
transforms: [
|
|
208
|
-
new RenameInputObjectFields((_, name) => rename(name)),
|
|
209
|
-
new RenameInterfaceFields((_, name) => rename(name)),
|
|
210
|
-
new RenameRootTypes((name) => rename(name)),
|
|
211
|
-
new RenameTypes((name) => rename(name)),
|
|
212
|
-
new RenameRootFields((_operationName, name) => rename(name)),
|
|
213
|
-
],
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
appContext.setSchema(transformed);
|
|
217
|
-
|
|
218
|
-
return transformed;
|
|
219
|
-
},
|
|
220
|
-
include: gqlModules,
|
|
221
|
-
useGlobalPrefix: true,
|
|
222
|
-
// Makes sure not too much info. is revealed when reporting errors in non-dev stages
|
|
223
|
-
// @url: https://github.com/nestjs/graphql/issues/1053#issuecomment-740739410
|
|
224
|
-
formatError: (
|
|
225
|
-
formattedError: GraphQLFormattedError,
|
|
226
|
-
error: unknown,
|
|
227
|
-
) => {
|
|
228
|
-
const graphQLError =
|
|
229
|
-
error instanceof GraphQLError ? error : undefined;
|
|
230
|
-
const exception: any = graphQLError?.extensions?.exception;
|
|
231
|
-
const message =
|
|
232
|
-
exception?.response?.message ||
|
|
233
|
-
graphQLError?.message ||
|
|
234
|
-
formattedError.message;
|
|
235
|
-
|
|
236
|
-
if (conf && (conf.isDev || conf.isTest)) {
|
|
237
|
-
return {
|
|
238
|
-
...formattedError,
|
|
239
|
-
message, // override the message with meaningful info
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// throw only essential error info in production
|
|
244
|
-
const productionError: GraphQLFormattedError = {
|
|
245
|
-
message,
|
|
246
|
-
extensions: {
|
|
247
|
-
path: formattedError.path,
|
|
248
|
-
code: formattedError.extensions?.code,
|
|
249
|
-
exception: graphQLError?.originalError, // error without stacktrace
|
|
250
|
-
},
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
return productionError;
|
|
254
|
-
},
|
|
255
|
-
playground:
|
|
256
|
-
conf?.isDev && !disablePlayground
|
|
257
|
-
? {
|
|
258
|
-
endpoint: `${conf?.apiPrefix}/graphql`,
|
|
259
|
-
settings: { 'request.credentials': 'include' },
|
|
260
|
-
}
|
|
261
|
-
: false,
|
|
262
|
-
debug: conf?.isDev,
|
|
263
|
-
context: async ({
|
|
264
|
-
request,
|
|
265
|
-
reply,
|
|
266
|
-
...rest
|
|
267
|
-
}: {
|
|
268
|
-
request: any;
|
|
269
|
-
reply: any;
|
|
270
|
-
}) => {
|
|
271
|
-
await eventEmitter.emitAsync(
|
|
272
|
-
AppEvents.BEFORE_GQL_CONTEXT_MIDDLEWARE,
|
|
273
|
-
request,
|
|
274
|
-
reply,
|
|
275
|
-
);
|
|
276
|
-
|
|
277
|
-
return {
|
|
278
|
-
request,
|
|
279
|
-
response: reply,
|
|
280
|
-
...rest,
|
|
281
|
-
};
|
|
282
|
-
},
|
|
283
|
-
plugins: [new GqlComplexityPlugin(), ...gqlPluginList],
|
|
284
|
-
};
|
|
285
|
-
},
|
|
286
|
-
inject: [
|
|
287
|
-
ConfigService,
|
|
288
|
-
AppContextService,
|
|
289
|
-
EventEmitter2,
|
|
290
|
-
'APOLLO_PLUGINS',
|
|
291
|
-
],
|
|
292
|
-
}),
|
|
293
|
-
);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (setupEvents) {
|
|
297
|
-
imports.push(EventEmitterModule.forRoot());
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
if (setupJwt) {
|
|
301
|
-
imports.push(
|
|
302
|
-
JwtModule.registerAsync({
|
|
303
|
-
imports: [configModule],
|
|
304
|
-
useFactory: async (configService: ConfigService) => ({
|
|
305
|
-
secret: configService.get<any>(CURAPP_CONF_ALIAS)?.jwtSecretPrivate,
|
|
306
|
-
signOptions: {
|
|
307
|
-
expiresIn: 3600,
|
|
308
|
-
},
|
|
309
|
-
}),
|
|
310
|
-
inject: [ConfigService],
|
|
311
|
-
}),
|
|
312
|
-
);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return {
|
|
316
|
-
imports,
|
|
317
|
-
providers,
|
|
318
|
-
exports: [],
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
|
|
1
|
+
/* istanbul ignore file */
|
|
2
|
+
|
|
3
|
+
import { ImportType } from '@node-yalc/interfaces/nestjs.type.js';
|
|
4
|
+
import { Logger, ModuleMetadata, Provider } from '@nestjs/common';
|
|
5
|
+
import { HttpModule } from '@nestjs/axios';
|
|
6
|
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
7
|
+
import { GraphQLSchema } from 'graphql';
|
|
8
|
+
import {
|
|
9
|
+
wrapSchema,
|
|
10
|
+
RenameTypes,
|
|
11
|
+
RenameRootFields,
|
|
12
|
+
RenameRootTypes,
|
|
13
|
+
RenameInterfaceFields,
|
|
14
|
+
RenameInputObjectFields,
|
|
15
|
+
} from '@graphql-tools/wrap';
|
|
16
|
+
import { AppContextModule } from './app-context.module.js';
|
|
17
|
+
import { AppContextService } from './app-context.service.js';
|
|
18
|
+
import { IDbConfType } from '@nest-yalc-2/database/conf.interface.js';
|
|
19
|
+
import { getConfNameByConnection } from '@nest-yalc-2/database/conn.helper.js';
|
|
20
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
21
|
+
import { BuildSchemaOptions, GraphQLModule } from '@nestjs/graphql';
|
|
22
|
+
import { JwtModule } from '@nestjs/jwt';
|
|
23
|
+
import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter';
|
|
24
|
+
import { IServiceConf } from './conf.type.js';
|
|
25
|
+
import { GraphQLError } from 'graphql/error';
|
|
26
|
+
import { GraphQLFormattedError } from 'graphql/error';
|
|
27
|
+
// import { AppLoggerModule } from '@nest-yalc-2/logger/app-logger.module.js';
|
|
28
|
+
import { TypeORMLogger } from '@nest-yalc-2/logger/typeorm-logger.js';
|
|
29
|
+
import { CURAPP_CONF_ALIAS } from './def.const.js';
|
|
30
|
+
import { AppEvents } from './app.events.js';
|
|
31
|
+
import { GqlComplexityPlugin } from '@nest-yalc-2/graphql/plugins/gql-complexity.plugin.js';
|
|
32
|
+
import { ClassType } from '@node-yalc/types/globals.js';
|
|
33
|
+
import { isClass } from '@node-yalc/utils/class.helper.js';
|
|
34
|
+
import {
|
|
35
|
+
ApolloFederationDriver,
|
|
36
|
+
ApolloFederationDriverConfig,
|
|
37
|
+
} from '@nestjs/apollo';
|
|
38
|
+
|
|
39
|
+
import * as dotenv from 'dotenv';
|
|
40
|
+
dotenv.config(); // preload .env root file before all the others
|
|
41
|
+
|
|
42
|
+
type ApolloServerPlugin = NonNullable<
|
|
43
|
+
ApolloFederationDriverConfig['plugins']
|
|
44
|
+
>[number];
|
|
45
|
+
|
|
46
|
+
export interface IAppImportsFactory {
|
|
47
|
+
envPath?: string | string[];
|
|
48
|
+
setupEvents?: boolean;
|
|
49
|
+
setupJwt?: boolean;
|
|
50
|
+
keepDBConnectionAlive?: boolean;
|
|
51
|
+
operationPrefix?: string;
|
|
52
|
+
buildSchemaOptions?: BuildSchemaOptions;
|
|
53
|
+
gqlPlugin?: ClassType<ApolloServerPlugin>[];
|
|
54
|
+
disablePlayground?: boolean;
|
|
55
|
+
}
|
|
56
|
+
type PickModuleMetadata = 'imports' | 'providers' | 'exports';
|
|
57
|
+
export type AppModuleMetadata = Required<
|
|
58
|
+
Pick<ModuleMetadata, PickModuleMetadata>
|
|
59
|
+
>;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* This factory function can be reused within tests to
|
|
63
|
+
* create a test app module with the same imports of the actual app
|
|
64
|
+
*/
|
|
65
|
+
export function AppDependencyFactory(
|
|
66
|
+
_context: string,
|
|
67
|
+
confs: any[],
|
|
68
|
+
dbConnNames: string[],
|
|
69
|
+
gqlModules: any[],
|
|
70
|
+
options?: IAppImportsFactory,
|
|
71
|
+
): AppModuleMetadata {
|
|
72
|
+
const {
|
|
73
|
+
setupEvents = true,
|
|
74
|
+
setupJwt = true,
|
|
75
|
+
keepDBConnectionAlive = false,
|
|
76
|
+
buildSchemaOptions = {},
|
|
77
|
+
disablePlayground = false,
|
|
78
|
+
} = options ?? {};
|
|
79
|
+
|
|
80
|
+
const envPath: string[] = [];
|
|
81
|
+
|
|
82
|
+
if (!options?.envPath) {
|
|
83
|
+
envPath.push('.env'); // user-defined env (git-ignored)
|
|
84
|
+
|
|
85
|
+
if (process.env.NODE_ENV) {
|
|
86
|
+
envPath.push(`.env.${process.env.NODE_ENV}`); // user-defined env (git-ignored)
|
|
87
|
+
|
|
88
|
+
// .env.shared is always loaded except in production
|
|
89
|
+
if (process.env.NODE_ENV !== 'production')
|
|
90
|
+
envPath.push('conf/dist/.env.shared');
|
|
91
|
+
|
|
92
|
+
if (process.env.DOCKER_CONTAINER === '1')
|
|
93
|
+
envPath.push(`conf/dist/.env.docker.${process.env.NODE_ENV}`);
|
|
94
|
+
else {
|
|
95
|
+
envPath.push(`conf/dist/.env.local.${process.env.NODE_ENV}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
Logger.debug?.(`Using ${envPath}, for environment: ${process.env.NODE_ENV}`);
|
|
101
|
+
|
|
102
|
+
const configModule = ConfigModule.forRoot({
|
|
103
|
+
load: confs,
|
|
104
|
+
envFilePath: envPath,
|
|
105
|
+
// isGlobal: true,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// configModule and AppContextModule must be global to avoid
|
|
109
|
+
// import (re-instantiating) them when they are required by other modules
|
|
110
|
+
const imports: ImportType[] = [
|
|
111
|
+
configModule,
|
|
112
|
+
// AppLoggerModule.forRoot(context, configModule),
|
|
113
|
+
AppContextModule,
|
|
114
|
+
HttpModule,
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
if (dbConnNames.length) {
|
|
118
|
+
dbConnNames.forEach((connName) => {
|
|
119
|
+
imports.push(
|
|
120
|
+
TypeOrmModule.forRootAsync({
|
|
121
|
+
inject: [ConfigService, TypeORMLogger],
|
|
122
|
+
name: connName,
|
|
123
|
+
useFactory: async (
|
|
124
|
+
configService: ConfigService,
|
|
125
|
+
logger: TypeORMLogger,
|
|
126
|
+
) => {
|
|
127
|
+
const conf = configService.get<IDbConfType>(
|
|
128
|
+
getConfNameByConnection(connName),
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
logger.log?.(
|
|
132
|
+
'log',
|
|
133
|
+
`Connecting DB ${connName} to host: ${conf?.host}`,
|
|
134
|
+
);
|
|
135
|
+
return {
|
|
136
|
+
...conf,
|
|
137
|
+
logger: conf?.logging ? logger : undefined,
|
|
138
|
+
maxQueryExecutionTime: 1000,
|
|
139
|
+
keepConnectionAlive: keepDBConnectionAlive,
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
}),
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const providers: Provider<any>[] = [];
|
|
148
|
+
|
|
149
|
+
class ApolloPluginsModule {
|
|
150
|
+
static forRoot() {
|
|
151
|
+
return {
|
|
152
|
+
module: ApolloPluginsModule,
|
|
153
|
+
providers: [
|
|
154
|
+
{
|
|
155
|
+
provide: 'APOLLO_PLUGINS',
|
|
156
|
+
useFactory: (...gqlPlugins: ApolloServerPlugin[]) => {
|
|
157
|
+
return gqlPlugins;
|
|
158
|
+
},
|
|
159
|
+
inject: [...(options?.gqlPlugin ?? [])],
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
exports: ['APOLLO_PLUGINS'],
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (gqlModules.length) {
|
|
168
|
+
imports.push(
|
|
169
|
+
GraphQLModule.forRootAsync<ApolloFederationDriverConfig>({
|
|
170
|
+
driver: ApolloFederationDriver,
|
|
171
|
+
imports: [ApolloPluginsModule.forRoot()],
|
|
172
|
+
useFactory: async (
|
|
173
|
+
configService: ConfigService,
|
|
174
|
+
appContext: AppContextService,
|
|
175
|
+
eventEmitter: EventEmitter2,
|
|
176
|
+
gqlPluginList: ApolloServerPlugin[],
|
|
177
|
+
) => {
|
|
178
|
+
const conf = configService.get<IServiceConf>(CURAPP_CONF_ALIAS);
|
|
179
|
+
return {
|
|
180
|
+
autoSchemaFile: true,
|
|
181
|
+
buildSchemaOptions,
|
|
182
|
+
// can cause performance issues (https://docs.nestjs.com/graphql/other-features#execute-enhancers-at-the-field-resolver-level)
|
|
183
|
+
// be careful
|
|
184
|
+
fieldResolverEnhancers: ['interceptors', 'guards', 'filters'],
|
|
185
|
+
transformAutoSchemaFile: true,
|
|
186
|
+
transformSchema: (graphQLSchema: GraphQLSchema) => {
|
|
187
|
+
const opPrefix =
|
|
188
|
+
options?.operationPrefix ?? conf?.operationPrefix;
|
|
189
|
+
|
|
190
|
+
const rename = (name: string) => {
|
|
191
|
+
// _service, _entities etc. are used by federation and must not be renamed
|
|
192
|
+
if (!opPrefix || name.startsWith('_')) return name;
|
|
193
|
+
|
|
194
|
+
// orphaned types must keep their non-prefixed name
|
|
195
|
+
if (
|
|
196
|
+
options?.buildSchemaOptions?.orphanedTypes?.some(
|
|
197
|
+
(cl) => isClass(cl) && cl.name === name,
|
|
198
|
+
)
|
|
199
|
+
)
|
|
200
|
+
return name;
|
|
201
|
+
|
|
202
|
+
return `${opPrefix}_${name}`;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const transformed = wrapSchema({
|
|
206
|
+
schema: graphQLSchema,
|
|
207
|
+
transforms: [
|
|
208
|
+
new RenameInputObjectFields((_, name) => rename(name)),
|
|
209
|
+
new RenameInterfaceFields((_, name) => rename(name)),
|
|
210
|
+
new RenameRootTypes((name) => rename(name)),
|
|
211
|
+
new RenameTypes((name) => rename(name)),
|
|
212
|
+
new RenameRootFields((_operationName, name) => rename(name)),
|
|
213
|
+
],
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
appContext.setSchema(transformed);
|
|
217
|
+
|
|
218
|
+
return transformed;
|
|
219
|
+
},
|
|
220
|
+
include: gqlModules,
|
|
221
|
+
useGlobalPrefix: true,
|
|
222
|
+
// Makes sure not too much info. is revealed when reporting errors in non-dev stages
|
|
223
|
+
// @url: https://github.com/nestjs/graphql/issues/1053#issuecomment-740739410
|
|
224
|
+
formatError: (
|
|
225
|
+
formattedError: GraphQLFormattedError,
|
|
226
|
+
error: unknown,
|
|
227
|
+
) => {
|
|
228
|
+
const graphQLError =
|
|
229
|
+
error instanceof GraphQLError ? error : undefined;
|
|
230
|
+
const exception: any = graphQLError?.extensions?.exception;
|
|
231
|
+
const message =
|
|
232
|
+
exception?.response?.message ||
|
|
233
|
+
graphQLError?.message ||
|
|
234
|
+
formattedError.message;
|
|
235
|
+
|
|
236
|
+
if (conf && (conf.isDev || conf.isTest)) {
|
|
237
|
+
return {
|
|
238
|
+
...formattedError,
|
|
239
|
+
message, // override the message with meaningful info
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// throw only essential error info in production
|
|
244
|
+
const productionError: GraphQLFormattedError = {
|
|
245
|
+
message,
|
|
246
|
+
extensions: {
|
|
247
|
+
path: formattedError.path,
|
|
248
|
+
code: formattedError.extensions?.code,
|
|
249
|
+
exception: graphQLError?.originalError, // error without stacktrace
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
return productionError;
|
|
254
|
+
},
|
|
255
|
+
playground:
|
|
256
|
+
(conf?.isDev && !disablePlayground
|
|
257
|
+
? {
|
|
258
|
+
endpoint: `${conf?.apiPrefix}/graphql`,
|
|
259
|
+
settings: { 'request.credentials': 'include' },
|
|
260
|
+
}
|
|
261
|
+
: false) as any,
|
|
262
|
+
debug: conf?.isDev,
|
|
263
|
+
context: async ({
|
|
264
|
+
request,
|
|
265
|
+
reply,
|
|
266
|
+
...rest
|
|
267
|
+
}: {
|
|
268
|
+
request: any;
|
|
269
|
+
reply: any;
|
|
270
|
+
}) => {
|
|
271
|
+
await eventEmitter.emitAsync(
|
|
272
|
+
AppEvents.BEFORE_GQL_CONTEXT_MIDDLEWARE,
|
|
273
|
+
request,
|
|
274
|
+
reply,
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
request,
|
|
279
|
+
response: reply,
|
|
280
|
+
...rest,
|
|
281
|
+
};
|
|
282
|
+
},
|
|
283
|
+
plugins: [new GqlComplexityPlugin(), ...gqlPluginList],
|
|
284
|
+
};
|
|
285
|
+
},
|
|
286
|
+
inject: [
|
|
287
|
+
ConfigService,
|
|
288
|
+
AppContextService,
|
|
289
|
+
EventEmitter2,
|
|
290
|
+
'APOLLO_PLUGINS',
|
|
291
|
+
],
|
|
292
|
+
}),
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (setupEvents) {
|
|
297
|
+
imports.push(EventEmitterModule.forRoot());
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (setupJwt) {
|
|
301
|
+
imports.push(
|
|
302
|
+
JwtModule.registerAsync({
|
|
303
|
+
imports: [configModule],
|
|
304
|
+
useFactory: async (configService: ConfigService) => ({
|
|
305
|
+
secret: configService.get<any>(CURAPP_CONF_ALIAS)?.jwtSecretPrivate,
|
|
306
|
+
signOptions: {
|
|
307
|
+
expiresIn: 3600,
|
|
308
|
+
},
|
|
309
|
+
}),
|
|
310
|
+
inject: [ConfigService],
|
|
311
|
+
}),
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
imports,
|
|
317
|
+
providers,
|
|
318
|
+
exports: [],
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
package/src/app.events.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This enum contains the list of app-level events
|
|
3
|
-
*/
|
|
4
|
-
export enum AppEvents {
|
|
5
|
-
BEFORE_ALL_ROUTES = 'app.before_all_routes',
|
|
6
|
-
AFTER_ALL_ROUTES = 'app.after_all_routes',
|
|
7
|
-
BEFORE_GQL_CONTEXT_MIDDLEWARE = 'app.before_gql_context_middleware',
|
|
8
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* This enum contains the list of app-level events
|
|
3
|
+
*/
|
|
4
|
+
export enum AppEvents {
|
|
5
|
+
BEFORE_ALL_ROUTES = 'app.before_all_routes',
|
|
6
|
+
AFTER_ALL_ROUTES = 'app.after_all_routes',
|
|
7
|
+
BEFORE_GQL_CONTEXT_MIDDLEWARE = 'app.before_gql_context_middleware',
|
|
8
|
+
}
|