feat: exceptions handlers (#2855)
* feat: wip exception handlers * feat: exception capturer * fix: rename exception-capturer into exception-handler * fix: remove unused variable
This commit is contained in:
@ -10,6 +10,9 @@ import { ExtractJwt } from 'passport-jwt';
|
||||
import { TokenExpiredError, JsonWebTokenError, verify } from 'jsonwebtoken';
|
||||
|
||||
import { WorkspaceFactory } from 'src/workspace/workspace.factory';
|
||||
import { TypeOrmExceptionFilter } from 'src/filters/typeorm-exception.filter';
|
||||
import { HttpExceptionFilter } from 'src/filters/http-exception.filter';
|
||||
import { GlobalExceptionFilter } from 'src/filters/global-exception.filter';
|
||||
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@ -22,7 +25,6 @@ import {
|
||||
JwtAuthStrategy,
|
||||
JwtPayload,
|
||||
} from './core/auth/strategies/jwt.auth.strategy';
|
||||
import { ExceptionFilter } from './filters/exception.filter';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -111,9 +113,20 @@ import { ExceptionFilter } from './filters/exception.filter';
|
||||
],
|
||||
providers: [
|
||||
AppService,
|
||||
// Exceptions filters must be ordered from the least specific to the most specific
|
||||
// If TypeOrmExceptionFilter handle something, HttpExceptionFilter will not handle it
|
||||
// GlobalExceptionFilter will handle the rest of the exceptions
|
||||
{
|
||||
provide: APP_FILTER,
|
||||
useClass: ExceptionFilter,
|
||||
useClass: GlobalExceptionFilter,
|
||||
},
|
||||
{
|
||||
provide: APP_FILTER,
|
||||
useClass: HttpExceptionFilter,
|
||||
},
|
||||
{
|
||||
provide: APP_FILTER,
|
||||
useClass: TypeOrmExceptionFilter,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
import { ArgumentsHost, Catch, HttpException } from '@nestjs/common';
|
||||
import { GqlContextType, GqlExceptionFilter } from '@nestjs/graphql';
|
||||
|
||||
import { TypeORMError } from 'typeorm';
|
||||
|
||||
import {
|
||||
AuthenticationError,
|
||||
BaseGraphQLError,
|
||||
ForbiddenError,
|
||||
} from 'src/filters/utils/graphql-errors.util';
|
||||
|
||||
const graphQLPredefinedExceptions = {
|
||||
401: AuthenticationError,
|
||||
403: ForbiddenError,
|
||||
};
|
||||
|
||||
@Catch()
|
||||
export class ExceptionFilter implements GqlExceptionFilter {
|
||||
catch(exception: HttpException | TypeORMError, host: ArgumentsHost) {
|
||||
if (host.getType<GqlContextType>() !== 'graphql') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (exception instanceof TypeORMError) {
|
||||
const error = new BaseGraphQLError(
|
||||
exception.name,
|
||||
'INTERNAL_SERVER_ERROR',
|
||||
);
|
||||
|
||||
error.stack = exception.stack;
|
||||
error.extensions['response'] = exception.message;
|
||||
|
||||
return error;
|
||||
} else if (exception instanceof HttpException) {
|
||||
let error: BaseGraphQLError;
|
||||
|
||||
if (exception.getStatus() in graphQLPredefinedExceptions) {
|
||||
error = new graphQLPredefinedExceptions[exception.getStatus()](
|
||||
exception.message,
|
||||
);
|
||||
} else {
|
||||
error = new BaseGraphQLError(
|
||||
exception.message,
|
||||
exception.getStatus().toString(),
|
||||
);
|
||||
}
|
||||
|
||||
error.stack = exception.stack;
|
||||
error.extensions['response'] = exception.getResponse();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
return exception;
|
||||
}
|
||||
}
|
||||
18
server/src/filters/global-exception.filter.ts
Normal file
18
server/src/filters/global-exception.filter.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { Catch, Injectable } from '@nestjs/common';
|
||||
import { GqlExceptionFilter } from '@nestjs/graphql';
|
||||
|
||||
import { ExceptionHandlerService } from 'src/integrations/exception-handler/exception-handler.service';
|
||||
|
||||
@Catch()
|
||||
@Injectable()
|
||||
export class GlobalExceptionFilter implements GqlExceptionFilter {
|
||||
constructor(
|
||||
private readonly exceptionHandlerService: ExceptionHandlerService,
|
||||
) {}
|
||||
|
||||
catch(exception: unknown) {
|
||||
this.exceptionHandlerService.captureException(exception);
|
||||
|
||||
return exception;
|
||||
}
|
||||
}
|
||||
42
server/src/filters/http-exception.filter.ts
Normal file
42
server/src/filters/http-exception.filter.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { ArgumentsHost, Catch, HttpException } from '@nestjs/common';
|
||||
import { GqlContextType, GqlExceptionFilter } from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
AuthenticationError,
|
||||
BaseGraphQLError,
|
||||
ForbiddenError,
|
||||
} from 'src/filters/utils/graphql-errors.util';
|
||||
|
||||
const graphQLPredefinedExceptions = {
|
||||
401: AuthenticationError,
|
||||
403: ForbiddenError,
|
||||
};
|
||||
|
||||
@Catch(HttpException)
|
||||
export class HttpExceptionFilter
|
||||
implements GqlExceptionFilter<HttpException, BaseGraphQLError | null>
|
||||
{
|
||||
catch(exception: HttpException, host: ArgumentsHost) {
|
||||
if (host.getType<GqlContextType>() !== 'graphql') {
|
||||
return null;
|
||||
}
|
||||
|
||||
let error: BaseGraphQLError;
|
||||
|
||||
if (exception.getStatus() in graphQLPredefinedExceptions) {
|
||||
error = new graphQLPredefinedExceptions[exception.getStatus()](
|
||||
exception.message,
|
||||
);
|
||||
} else {
|
||||
error = new BaseGraphQLError(
|
||||
exception.message,
|
||||
exception.getStatus().toString(),
|
||||
);
|
||||
}
|
||||
|
||||
error.stack = exception.stack;
|
||||
error.extensions['response'] = exception.getResponse();
|
||||
|
||||
return error;
|
||||
}
|
||||
}
|
||||
24
server/src/filters/typeorm-exception.filter.ts
Normal file
24
server/src/filters/typeorm-exception.filter.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { ArgumentsHost, Catch } from '@nestjs/common';
|
||||
import { GqlContextType, GqlExceptionFilter } from '@nestjs/graphql';
|
||||
|
||||
import { TypeORMError } from 'typeorm';
|
||||
|
||||
import { BaseGraphQLError } from 'src/filters/utils/graphql-errors.util';
|
||||
|
||||
@Catch(TypeORMError)
|
||||
export class TypeOrmExceptionFilter
|
||||
implements GqlExceptionFilter<TypeORMError, BaseGraphQLError | null>
|
||||
{
|
||||
catch(exception: TypeORMError, host: ArgumentsHost) {
|
||||
if (host.getType<GqlContextType>() !== 'graphql') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const error = new BaseGraphQLError(exception.name, 'INTERNAL_SERVER_ERROR');
|
||||
|
||||
error.stack = exception.stack;
|
||||
error.extensions['response'] = exception.message;
|
||||
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@ -2,11 +2,13 @@
|
||||
import { Injectable, LogLevel } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
import { LoggerDriverType } from 'src/integrations/logger/interfaces';
|
||||
import { ExceptionHandlerDriver } from 'src/integrations/exception-handler/interfaces';
|
||||
import { StorageDriverType } from 'src/integrations/file-storage/interfaces';
|
||||
import { MessageQueueDriverType } from 'src/integrations/message-queue/interfaces';
|
||||
|
||||
import { AwsRegion } from './interfaces/aws-region.interface';
|
||||
import { StorageType } from './interfaces/storage.interface';
|
||||
import { SupportDriver } from './interfaces/support.interface';
|
||||
import { LoggerDriver } from './interfaces/logger.interface';
|
||||
import { MessageQueueType } from './interfaces/message-queue.interface';
|
||||
|
||||
@Injectable()
|
||||
export class EnvironmentService {
|
||||
@ -109,16 +111,17 @@ export class EnvironmentService {
|
||||
return this.configService.get<string>('AUTH_GOOGLE_CALLBACK_URL');
|
||||
}
|
||||
|
||||
getStorageType(): StorageType {
|
||||
getStorageDriverType(): StorageDriverType {
|
||||
return (
|
||||
this.configService.get<StorageType>('STORAGE_TYPE') ?? StorageType.Local
|
||||
this.configService.get<StorageDriverType>('STORAGE_TYPE') ??
|
||||
StorageDriverType.Local
|
||||
);
|
||||
}
|
||||
|
||||
getMessageQueueType(): MessageQueueType {
|
||||
getMessageQueueDriverType(): MessageQueueDriverType {
|
||||
return (
|
||||
this.configService.get<MessageQueueType>('MESSAGE_QUEUE_TYPE') ??
|
||||
MessageQueueType.PgBoss
|
||||
this.configService.get<MessageQueueDriverType>('MESSAGE_QUEUE_TYPE') ??
|
||||
MessageQueueDriverType.PgBoss
|
||||
);
|
||||
}
|
||||
|
||||
@ -154,9 +157,18 @@ export class EnvironmentService {
|
||||
return this.configService.get<string>('SUPPORT_FRONT_HMAC_KEY');
|
||||
}
|
||||
|
||||
getLoggerDriver(): string {
|
||||
getLoggerDriverType(): LoggerDriverType {
|
||||
return (
|
||||
this.configService.get<string>('LOGGER_DRIVER') ?? LoggerDriver.Console
|
||||
this.configService.get<LoggerDriverType>('LOGGER_DRIVER') ??
|
||||
LoggerDriverType.Console
|
||||
);
|
||||
}
|
||||
|
||||
getExceptionHandlerDriverType(): ExceptionHandlerDriver {
|
||||
return (
|
||||
this.configService.get<ExceptionHandlerDriver>(
|
||||
'EXCEPTION_HANDLER_DRIVER',
|
||||
) ?? ExceptionHandlerDriver.Console
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -14,15 +14,16 @@ import {
|
||||
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { CastToStringArray } from 'src/integrations/environment/decorators/cast-to-string-array.decorator';
|
||||
import { ExceptionHandlerDriver } from 'src/integrations/exception-handler/interfaces';
|
||||
import { StorageDriverType } from 'src/integrations/file-storage/interfaces';
|
||||
import { LoggerDriverType } from 'src/integrations/logger/interfaces';
|
||||
|
||||
import { IsDuration } from './decorators/is-duration.decorator';
|
||||
import { StorageType } from './interfaces/storage.interface';
|
||||
import { AwsRegion } from './interfaces/aws-region.interface';
|
||||
import { IsAWSRegion } from './decorators/is-aws-region.decorator';
|
||||
import { CastToBoolean } from './decorators/cast-to-boolean.decorator';
|
||||
import { SupportDriver } from './interfaces/support.interface';
|
||||
import { CastToPositiveNumber } from './decorators/cast-to-positive-number.decorator';
|
||||
import { LoggerDriver } from './interfaces/logger.interface';
|
||||
import { CastToLogLevelArray } from './decorators/cast-to-log-level-array.decorator';
|
||||
|
||||
export class EnvironmentVariables {
|
||||
@ -110,20 +111,20 @@ export class EnvironmentVariables {
|
||||
AUTH_GOOGLE_CALLBACK_URL?: string;
|
||||
|
||||
// Storage
|
||||
@IsEnum(StorageType)
|
||||
@IsEnum(StorageDriverType)
|
||||
@IsOptional()
|
||||
STORAGE_TYPE?: StorageType;
|
||||
STORAGE_TYPE?: StorageDriverType;
|
||||
|
||||
@ValidateIf((env) => env.STORAGE_TYPE === StorageType.S3)
|
||||
@ValidateIf((env) => env.STORAGE_TYPE === StorageDriverType.S3)
|
||||
@IsAWSRegion()
|
||||
STORAGE_S3_REGION?: AwsRegion;
|
||||
|
||||
@ValidateIf((env) => env.STORAGE_TYPE === StorageType.S3)
|
||||
@ValidateIf((env) => env.STORAGE_TYPE === StorageDriverType.S3)
|
||||
@IsString()
|
||||
STORAGE_S3_NAME?: string;
|
||||
|
||||
@IsString()
|
||||
@ValidateIf((env) => env.STORAGE_TYPE === StorageType.Local)
|
||||
@ValidateIf((env) => env.STORAGE_TYPE === StorageDriverType.Local)
|
||||
STORAGE_LOCAL_PATH?: string;
|
||||
|
||||
// Support
|
||||
@ -139,9 +140,13 @@ export class EnvironmentVariables {
|
||||
@IsString()
|
||||
SUPPORT_FRONT_HMAC_KEY?: string;
|
||||
|
||||
@IsEnum(LoggerDriver)
|
||||
@IsEnum(LoggerDriverType)
|
||||
@IsOptional()
|
||||
LOGGER_DRIVER?: LoggerDriver;
|
||||
LOGGER_DRIVER?: LoggerDriverType;
|
||||
|
||||
@IsEnum(ExceptionHandlerDriver)
|
||||
@IsOptional()
|
||||
EXCEPTION_HANDLER_DRIVER?: ExceptionHandlerDriver;
|
||||
|
||||
@CastToLogLevelArray()
|
||||
@IsOptional()
|
||||
@ -151,7 +156,9 @@ export class EnvironmentVariables {
|
||||
@IsOptional()
|
||||
DEMO_WORKSPACE_IDS?: string[];
|
||||
|
||||
@ValidateIf((env) => env.LOGGER_DRIVER === LoggerDriver.Sentry)
|
||||
@ValidateIf(
|
||||
(env) => env.EXCEPTION_HANDLER_DRIVER === ExceptionHandlerDriver.Sentry,
|
||||
)
|
||||
@IsString()
|
||||
SENTRY_DSN?: string;
|
||||
}
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
export enum LoggerDriver {
|
||||
Console = 'console',
|
||||
Sentry = 'sentry',
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
export enum MemoryStorageType {
|
||||
Local = 'local',
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
export enum MessageQueueType {
|
||||
PgBoss = 'pg-boss',
|
||||
BullMQ = 'bull-mq',
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
export enum StorageType {
|
||||
S3 = 's3',
|
||||
Local = 'local',
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
import { ExceptionHandlerDriverInterface } from 'src/integrations/exception-handler/interfaces';
|
||||
|
||||
export class ExceptionHandlerConsoleDriver
|
||||
implements ExceptionHandlerDriverInterface
|
||||
{
|
||||
captureException(exception: unknown) {
|
||||
console.group('Exception Captured');
|
||||
console.error(exception);
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
captureMessage(message: string): void {
|
||||
console.group('Message Captured');
|
||||
console.info(message);
|
||||
console.groupEnd();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
import * as Sentry from '@sentry/node';
|
||||
import { ProfilingIntegration } from '@sentry/profiling-node';
|
||||
|
||||
import {
|
||||
ExceptionHandlerDriverInterface,
|
||||
ExceptionHandlerSentryDriverFactoryOptions,
|
||||
} from 'src/integrations/exception-handler/interfaces';
|
||||
|
||||
export class ExceptionHandlerSentryDriver
|
||||
implements ExceptionHandlerDriverInterface
|
||||
{
|
||||
constructor(options: ExceptionHandlerSentryDriverFactoryOptions['options']) {
|
||||
Sentry.init({
|
||||
dsn: options.dns,
|
||||
integrations: [
|
||||
// enable HTTP calls tracing
|
||||
new Sentry.Integrations.Http({ tracing: true }),
|
||||
// enable Express.js middleware tracing
|
||||
new Sentry.Integrations.Express({ app: options.serverInstance }),
|
||||
new Sentry.Integrations.GraphQL(),
|
||||
new Sentry.Integrations.Postgres({
|
||||
usePgNative: true,
|
||||
}),
|
||||
new ProfilingIntegration(),
|
||||
],
|
||||
tracesSampleRate: 1.0,
|
||||
profilesSampleRate: 1.0,
|
||||
environment: options.debug ? 'development' : 'production',
|
||||
debug: options.debug,
|
||||
});
|
||||
}
|
||||
|
||||
captureException(exception: Error) {
|
||||
Sentry.captureException(exception);
|
||||
}
|
||||
|
||||
captureMessage(message: string) {
|
||||
Sentry.captureMessage(message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
export const EXCEPTION_HANDLER_DRIVER = Symbol('EXCEPTION_HANDLER_DRIVER');
|
||||
@ -0,0 +1,25 @@
|
||||
import {
|
||||
ConfigurableModuleBuilder,
|
||||
FactoryProvider,
|
||||
ModuleMetadata,
|
||||
} from '@nestjs/common';
|
||||
|
||||
import { ExceptionHandlerModuleOptions } from './interfaces';
|
||||
|
||||
export const {
|
||||
ConfigurableModuleClass,
|
||||
MODULE_OPTIONS_TOKEN,
|
||||
OPTIONS_TYPE,
|
||||
ASYNC_OPTIONS_TYPE,
|
||||
} = new ConfigurableModuleBuilder<ExceptionHandlerModuleOptions>({
|
||||
moduleName: 'ExceptionHandlerModule',
|
||||
})
|
||||
.setClassMethodName('forRoot')
|
||||
.build();
|
||||
|
||||
export type ExceptionHandlerModuleAsyncOptions = {
|
||||
useFactory: (
|
||||
...args: any[]
|
||||
) => ExceptionHandlerModuleOptions | Promise<ExceptionHandlerModuleOptions>;
|
||||
} & Pick<ModuleMetadata, 'imports'> &
|
||||
Pick<FactoryProvider, 'inject'>;
|
||||
@ -0,0 +1,39 @@
|
||||
import { HttpAdapterHost } from '@nestjs/core';
|
||||
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import { OPTIONS_TYPE } from 'src/integrations/exception-handler/exception-handler.module-definition';
|
||||
import { ExceptionHandlerDriver } from 'src/integrations/exception-handler/interfaces';
|
||||
|
||||
/**
|
||||
* ExceptionHandler Module factory
|
||||
* @param environment
|
||||
* @returns ExceptionHandlerModuleOptions
|
||||
*/
|
||||
export const exceptionHandlerModuleFactory = async (
|
||||
environmentService: EnvironmentService,
|
||||
adapterHost: HttpAdapterHost,
|
||||
): Promise<typeof OPTIONS_TYPE> => {
|
||||
const driverType = environmentService.getExceptionHandlerDriverType();
|
||||
|
||||
switch (driverType) {
|
||||
case ExceptionHandlerDriver.Console: {
|
||||
return {
|
||||
type: ExceptionHandlerDriver.Console,
|
||||
};
|
||||
}
|
||||
case ExceptionHandlerDriver.Sentry: {
|
||||
return {
|
||||
type: ExceptionHandlerDriver.Sentry,
|
||||
options: {
|
||||
dns: environmentService.getSentryDSN() ?? '',
|
||||
serverInstance: adapterHost.httpAdapter.getInstance(),
|
||||
debug: environmentService.isDebugMode(),
|
||||
},
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw new Error(
|
||||
`Invalid exception capturer driver type (${driverType}), check your .env file`,
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,60 @@
|
||||
import { DynamicModule, Global, Module } from '@nestjs/common';
|
||||
|
||||
import { ExceptionHandlerSentryDriver } from 'src/integrations/exception-handler/drivers/sentry.driver';
|
||||
import { ExceptionHandlerConsoleDriver } from 'src/integrations/exception-handler/drivers/console.driver';
|
||||
|
||||
import { ExceptionHandlerService } from './exception-handler.service';
|
||||
import { ExceptionHandlerDriver } from './interfaces';
|
||||
import { EXCEPTION_HANDLER_DRIVER } from './exception-handler.constants';
|
||||
import {
|
||||
ConfigurableModuleClass,
|
||||
OPTIONS_TYPE,
|
||||
ASYNC_OPTIONS_TYPE,
|
||||
} from './exception-handler.module-definition';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [ExceptionHandlerService],
|
||||
exports: [ExceptionHandlerService],
|
||||
})
|
||||
export class ExceptionHandlerModule extends ConfigurableModuleClass {
|
||||
static forRoot(options: typeof OPTIONS_TYPE): DynamicModule {
|
||||
const provider = {
|
||||
provide: EXCEPTION_HANDLER_DRIVER,
|
||||
useValue:
|
||||
options.type === ExceptionHandlerDriver.Console
|
||||
? new ExceptionHandlerConsoleDriver()
|
||||
: new ExceptionHandlerSentryDriver(options.options),
|
||||
};
|
||||
const dynamicModule = super.forRoot(options);
|
||||
|
||||
return {
|
||||
...dynamicModule,
|
||||
providers: [...(dynamicModule.providers ?? []), provider],
|
||||
};
|
||||
}
|
||||
|
||||
static forRootAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule {
|
||||
const provider = {
|
||||
provide: EXCEPTION_HANDLER_DRIVER,
|
||||
useFactory: async (...args: any[]) => {
|
||||
const config = await options?.useFactory?.(...args);
|
||||
|
||||
if (!config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return config.type === ExceptionHandlerDriver.Console
|
||||
? new ExceptionHandlerConsoleDriver()
|
||||
: new ExceptionHandlerSentryDriver(config.options);
|
||||
},
|
||||
inject: options.inject || [],
|
||||
};
|
||||
const dynamicModule = super.forRootAsync(options);
|
||||
|
||||
return {
|
||||
...dynamicModule,
|
||||
providers: [...(dynamicModule.providers ?? []), provider],
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { ExceptionHandlerService } from 'src/integrations/exception-handler/exception-handler.service';
|
||||
|
||||
import { EXCEPTION_HANDLER_DRIVER } from './exception-handler.constants';
|
||||
|
||||
describe('ExceptionHandlerService', () => {
|
||||
let service: ExceptionHandlerService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
ExceptionHandlerService,
|
||||
{
|
||||
provide: EXCEPTION_HANDLER_DRIVER,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<ExceptionHandlerService>(ExceptionHandlerService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,17 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
|
||||
import { ExceptionHandlerDriverInterface } from 'src/integrations/exception-handler/interfaces';
|
||||
|
||||
import { EXCEPTION_HANDLER_DRIVER } from './exception-handler.constants';
|
||||
|
||||
@Injectable()
|
||||
export class ExceptionHandlerService {
|
||||
constructor(
|
||||
@Inject(EXCEPTION_HANDLER_DRIVER)
|
||||
private driver: ExceptionHandlerDriverInterface,
|
||||
) {}
|
||||
|
||||
captureException(exception: unknown) {
|
||||
this.driver.captureException(exception);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
export interface ExceptionHandlerDriverInterface {
|
||||
captureException(exception: unknown): void;
|
||||
captureMessage(message: string): void;
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { Router } from 'express';
|
||||
|
||||
export enum ExceptionHandlerDriver {
|
||||
Sentry = 'sentry',
|
||||
Console = 'console',
|
||||
}
|
||||
|
||||
export interface ExceptionHandlerSentryDriverFactoryOptions {
|
||||
type: ExceptionHandlerDriver.Sentry;
|
||||
options: {
|
||||
dns: string;
|
||||
serverInstance: Router;
|
||||
debug?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ExceptionHandlerDriverFactoryOptions {
|
||||
type: ExceptionHandlerDriver.Console;
|
||||
}
|
||||
|
||||
export type ExceptionHandlerModuleOptions =
|
||||
| ExceptionHandlerSentryDriverFactoryOptions
|
||||
| ExceptionHandlerDriverFactoryOptions;
|
||||
@ -0,0 +1,2 @@
|
||||
export * from './exception-handler.interface';
|
||||
export * from './exception-handler-driver.interface';
|
||||
@ -0,0 +1,53 @@
|
||||
import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
|
||||
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import {
|
||||
FileStorageModuleOptions,
|
||||
StorageDriverType,
|
||||
} from 'src/integrations/file-storage/interfaces';
|
||||
|
||||
/**
|
||||
* FileStorage Module factory
|
||||
* @param environment
|
||||
* @returns FileStorageModuleOptions
|
||||
*/
|
||||
export const fileStorageModuleFactory = async (
|
||||
environmentService: EnvironmentService,
|
||||
): Promise<FileStorageModuleOptions> => {
|
||||
const driverType = environmentService.getStorageDriverType();
|
||||
|
||||
switch (driverType) {
|
||||
case StorageDriverType.Local: {
|
||||
const storagePath = environmentService.getStorageLocalPath();
|
||||
|
||||
return {
|
||||
type: StorageDriverType.Local,
|
||||
options: {
|
||||
storagePath: process.cwd() + '/' + storagePath,
|
||||
},
|
||||
};
|
||||
}
|
||||
case StorageDriverType.S3: {
|
||||
const bucketName = environmentService.getStorageS3Name();
|
||||
const endpoint = environmentService.getStorageS3Endpoint();
|
||||
const region = environmentService.getStorageS3Region();
|
||||
|
||||
return {
|
||||
type: StorageDriverType.S3,
|
||||
options: {
|
||||
bucketName: bucketName ?? '',
|
||||
endpoint: endpoint,
|
||||
credentials: fromNodeProviderChain({
|
||||
clientConfig: { region },
|
||||
}),
|
||||
forcePathStyle: true,
|
||||
region: region ?? '',
|
||||
},
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw new Error(
|
||||
`Invalid storage driver type (${driverType}), check your .env file`,
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -1,17 +1,20 @@
|
||||
import { FactoryProvider, ModuleMetadata } from '@nestjs/common';
|
||||
|
||||
import { StorageType } from 'src/integrations/environment/interfaces/storage.interface';
|
||||
|
||||
import { S3DriverOptions } from 'src/integrations/file-storage/drivers/s3.driver';
|
||||
import { LocalDriverOptions } from 'src/integrations/file-storage/drivers/local.driver';
|
||||
|
||||
export enum StorageDriverType {
|
||||
S3 = 's3',
|
||||
Local = 'local',
|
||||
}
|
||||
|
||||
export interface S3DriverFactoryOptions {
|
||||
type: StorageType.S3;
|
||||
type: StorageDriverType.S3;
|
||||
options: S3DriverOptions;
|
||||
}
|
||||
|
||||
export interface LocalDriverFactoryOptions {
|
||||
type: StorageType.Local;
|
||||
type: StorageDriverType.Local;
|
||||
options: LocalDriverOptions;
|
||||
}
|
||||
|
||||
|
||||
@ -1,134 +1,17 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { HttpAdapterHost } from '@nestjs/core';
|
||||
|
||||
import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
|
||||
import { ExceptionHandlerModule } from 'src/integrations/exception-handler/exception-handler.module';
|
||||
import { exceptionHandlerModuleFactory } from 'src/integrations/exception-handler/exception-handler.module-factory';
|
||||
import { fileStorageModuleFactory } from 'src/integrations/file-storage/file-storage.module-factory';
|
||||
import { loggerModuleFactory } from 'src/integrations/logger/logger.module-factory';
|
||||
import { messageQueueModuleFactory } from 'src/integrations/message-queue/message-queue.module-factory';
|
||||
|
||||
import { EnvironmentModule } from './environment/environment.module';
|
||||
import { EnvironmentService } from './environment/environment.service';
|
||||
import { FileStorageModule } from './file-storage/file-storage.module';
|
||||
import { FileStorageModuleOptions } from './file-storage/interfaces';
|
||||
import { StorageType } from './environment/interfaces/storage.interface';
|
||||
import { LoggerModule } from './logger/logger.module';
|
||||
import { LoggerModuleOptions } from './logger/interfaces';
|
||||
import { LoggerDriver } from './environment/interfaces/logger.interface';
|
||||
import { MessageQueueModule } from './message-queue/message-queue.module';
|
||||
import { MessageQueueModuleOptions } from './message-queue/interfaces';
|
||||
import { MessageQueueType } from './environment/interfaces/message-queue.interface';
|
||||
|
||||
/**
|
||||
* FileStorage Module factory
|
||||
* @param environment
|
||||
* @returns FileStorageModuleOptions
|
||||
*/
|
||||
const fileStorageModuleFactory = async (
|
||||
environmentService: EnvironmentService,
|
||||
): Promise<FileStorageModuleOptions> => {
|
||||
const type = environmentService.getStorageType();
|
||||
|
||||
switch (type) {
|
||||
case StorageType.Local: {
|
||||
const storagePath = environmentService.getStorageLocalPath();
|
||||
|
||||
return {
|
||||
type: StorageType.Local,
|
||||
options: {
|
||||
storagePath: process.cwd() + '/' + storagePath,
|
||||
},
|
||||
};
|
||||
}
|
||||
case StorageType.S3: {
|
||||
const bucketName = environmentService.getStorageS3Name();
|
||||
const endpoint = environmentService.getStorageS3Endpoint();
|
||||
const region = environmentService.getStorageS3Region();
|
||||
|
||||
return {
|
||||
type: StorageType.S3,
|
||||
options: {
|
||||
bucketName: bucketName ?? '',
|
||||
endpoint: endpoint,
|
||||
credentials: fromNodeProviderChain({
|
||||
clientConfig: { region },
|
||||
}),
|
||||
forcePathStyle: true,
|
||||
region: region ?? '',
|
||||
},
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw new Error(`Invalid storage type (${type}), check your .env file`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Logger Module factory
|
||||
* @param environment
|
||||
* @returns LoggerModuleOptions
|
||||
*/
|
||||
const loggerModuleFactory = async (
|
||||
environmentService: EnvironmentService,
|
||||
): Promise<LoggerModuleOptions> => {
|
||||
const type = environmentService.getLoggerDriver();
|
||||
|
||||
switch (type) {
|
||||
case LoggerDriver.Console: {
|
||||
return {
|
||||
type: LoggerDriver.Console,
|
||||
options: null,
|
||||
};
|
||||
}
|
||||
case LoggerDriver.Sentry: {
|
||||
return {
|
||||
type: LoggerDriver.Sentry,
|
||||
options: {
|
||||
sentryDNS: environmentService.getSentryDSN() ?? '',
|
||||
},
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw new Error(`Invalid logger type (${type}), check your .env file`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* MessageQueue Module factory
|
||||
* @param environment
|
||||
* @returns MessageQueueModuleOptions
|
||||
*/
|
||||
const messageQueueModuleFactory = async (
|
||||
environmentService: EnvironmentService,
|
||||
): Promise<MessageQueueModuleOptions> => {
|
||||
const type = environmentService.getMessageQueueType();
|
||||
|
||||
switch (type) {
|
||||
case MessageQueueType.PgBoss: {
|
||||
const connectionString = environmentService.getPGDatabaseUrl();
|
||||
|
||||
return {
|
||||
type: MessageQueueType.PgBoss,
|
||||
options: {
|
||||
connectionString,
|
||||
},
|
||||
};
|
||||
}
|
||||
case MessageQueueType.BullMQ: {
|
||||
const host = environmentService.getRedisHost();
|
||||
const port = environmentService.getRedisPort();
|
||||
|
||||
return {
|
||||
type: MessageQueueType.BullMQ,
|
||||
options: {
|
||||
connection: {
|
||||
host,
|
||||
port,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw new Error(
|
||||
`Invalid message queue type (${type}), check your .env file`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -145,6 +28,10 @@ const messageQueueModuleFactory = async (
|
||||
useFactory: messageQueueModuleFactory,
|
||||
inject: [EnvironmentService],
|
||||
}),
|
||||
ExceptionHandlerModule.forRootAsync({
|
||||
useFactory: exceptionHandlerModuleFactory,
|
||||
inject: [EnvironmentService, HttpAdapterHost],
|
||||
}),
|
||||
],
|
||||
exports: [],
|
||||
providers: [],
|
||||
|
||||
@ -1,53 +0,0 @@
|
||||
import { LoggerService } from '@nestjs/common';
|
||||
|
||||
import * as Sentry from '@sentry/node';
|
||||
|
||||
export interface SentryDriverOptions {
|
||||
sentryDNS: string;
|
||||
}
|
||||
|
||||
export class SentryDriver implements LoggerService {
|
||||
constructor(options: SentryDriverOptions) {
|
||||
Sentry.init({
|
||||
dsn: options.sentryDNS,
|
||||
tracesSampleRate: 1.0,
|
||||
profilesSampleRate: 1.0,
|
||||
});
|
||||
}
|
||||
|
||||
private logLevels = ['log', 'error', 'warning', 'debug', 'info'];
|
||||
|
||||
setLogLevels(levels: string[]) {
|
||||
this.logLevels = levels;
|
||||
}
|
||||
|
||||
log(message: any) {
|
||||
if (this.logLevels.includes('log')) {
|
||||
Sentry.captureMessage(message, { level: 'log' });
|
||||
}
|
||||
}
|
||||
|
||||
error(message: any) {
|
||||
if (this.logLevels.includes('error')) {
|
||||
Sentry.captureMessage(message, { level: 'error' });
|
||||
}
|
||||
}
|
||||
|
||||
warn(message: any) {
|
||||
if (this.logLevels.includes('warn')) {
|
||||
Sentry.captureMessage(message, { level: 'warning' });
|
||||
}
|
||||
}
|
||||
|
||||
debug?(message: any) {
|
||||
if (this.logLevels.includes('debug')) {
|
||||
Sentry.captureMessage(message, { level: 'debug' });
|
||||
}
|
||||
}
|
||||
|
||||
verbose?(message: any) {
|
||||
if (this.logLevels.includes('verbose')) {
|
||||
Sentry.captureMessage(message, { level: 'info' });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,17 +1,9 @@
|
||||
import { LoggerDriver } from 'src/integrations/environment/interfaces/logger.interface';
|
||||
|
||||
export interface SentryDriverFactoryOptions {
|
||||
type: LoggerDriver.Sentry;
|
||||
options: {
|
||||
sentryDNS: string;
|
||||
};
|
||||
export enum LoggerDriverType {
|
||||
Console = 'console',
|
||||
}
|
||||
|
||||
export interface ConsoleDriverFactoryOptions {
|
||||
type: LoggerDriver.Console;
|
||||
options: null;
|
||||
type: LoggerDriverType.Console;
|
||||
}
|
||||
|
||||
export type LoggerModuleOptions =
|
||||
| SentryDriverFactoryOptions
|
||||
| ConsoleDriverFactoryOptions;
|
||||
export type LoggerModuleOptions = ConsoleDriverFactoryOptions;
|
||||
|
||||
28
server/src/integrations/logger/logger.module-factory.ts
Normal file
28
server/src/integrations/logger/logger.module-factory.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import {
|
||||
LoggerModuleOptions,
|
||||
LoggerDriverType,
|
||||
} from 'src/integrations/logger/interfaces';
|
||||
|
||||
/**
|
||||
* Logger Module factory
|
||||
* @param environment
|
||||
* @returns LoggerModuleOptions
|
||||
*/
|
||||
export const loggerModuleFactory = async (
|
||||
environmentService: EnvironmentService,
|
||||
): Promise<LoggerModuleOptions> => {
|
||||
const driverType = environmentService.getLoggerDriverType();
|
||||
|
||||
switch (driverType) {
|
||||
case LoggerDriverType.Console: {
|
||||
return {
|
||||
type: LoggerDriverType.Console,
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw new Error(
|
||||
`Invalid logger driver type (${driverType}), check your .env file`,
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -1,50 +1,58 @@
|
||||
import { DynamicModule, Global, ConsoleLogger } from '@nestjs/common';
|
||||
import { DynamicModule, Global, ConsoleLogger, Module } from '@nestjs/common';
|
||||
|
||||
import { LoggerDriver } from 'src/integrations/environment/interfaces/logger.interface';
|
||||
import { LoggerDriverType } from 'src/integrations/logger/interfaces';
|
||||
|
||||
import { LoggerService } from './logger.service';
|
||||
import { LoggerModuleOptions } from './interfaces';
|
||||
import { LOGGER_DRIVER } from './logger.constants';
|
||||
import { LoggerModuleAsyncOptions } from './logger.module-definition';
|
||||
|
||||
import { SentryDriver } from './drivers/sentry.driver';
|
||||
import {
|
||||
ASYNC_OPTIONS_TYPE,
|
||||
ConfigurableModuleClass,
|
||||
OPTIONS_TYPE,
|
||||
} from './logger.module-definition';
|
||||
|
||||
@Global()
|
||||
export class LoggerModule {
|
||||
static forRoot(options: LoggerModuleOptions): DynamicModule {
|
||||
@Module({
|
||||
providers: [LoggerService],
|
||||
exports: [LoggerService],
|
||||
})
|
||||
export class LoggerModule extends ConfigurableModuleClass {
|
||||
static forRoot(options: typeof OPTIONS_TYPE): DynamicModule {
|
||||
const provider = {
|
||||
provide: LOGGER_DRIVER,
|
||||
useValue:
|
||||
options.type === LoggerDriver.Console
|
||||
options.type === LoggerDriverType.Console
|
||||
? new ConsoleLogger()
|
||||
: new SentryDriver(options.options),
|
||||
: undefined,
|
||||
};
|
||||
const dynamicModule = super.forRoot(options);
|
||||
|
||||
return {
|
||||
module: LoggerModule,
|
||||
providers: [LoggerService, provider],
|
||||
exports: [LoggerService],
|
||||
...dynamicModule,
|
||||
providers: [...(dynamicModule.providers ?? []), provider],
|
||||
};
|
||||
}
|
||||
|
||||
static forRootAsync(options: LoggerModuleAsyncOptions): DynamicModule {
|
||||
static forRootAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule {
|
||||
const provider = {
|
||||
provide: LOGGER_DRIVER,
|
||||
useFactory: async (...args: any[]) => {
|
||||
const config = await options.useFactory(...args);
|
||||
const config = await options?.useFactory?.(...args);
|
||||
|
||||
return config?.type === LoggerDriver.Console
|
||||
if (!config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return config?.type === LoggerDriverType.Console
|
||||
? new ConsoleLogger()
|
||||
: new SentryDriver(config.options);
|
||||
: undefined;
|
||||
},
|
||||
inject: options.inject || [],
|
||||
};
|
||||
const dynamicModule = super.forRootAsync(options);
|
||||
|
||||
return {
|
||||
module: LoggerModule,
|
||||
imports: options.imports || [],
|
||||
providers: [LoggerService, provider],
|
||||
exports: [LoggerService],
|
||||
...dynamicModule,
|
||||
providers: [...(dynamicModule.providers ?? []), provider],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
import { FactoryProvider, ModuleMetadata } from '@nestjs/common';
|
||||
|
||||
import { MemoryStorageType } from 'src/integrations/environment/interfaces/memory-storage.interface';
|
||||
import { MemoryStorageSerializer } from 'src/integrations/memory-storage/serializers/interfaces/memory-storage-serializer.interface';
|
||||
|
||||
import { LocalMemoryDriverOptions } from 'src/integrations/memory-storage/drivers/local.driver';
|
||||
|
||||
export enum MemoryStorageDriverType {
|
||||
Local = 'local',
|
||||
}
|
||||
|
||||
export interface LocalMemoryDriverFactoryOptions {
|
||||
type: MemoryStorageType.Local;
|
||||
type: MemoryStorageDriverType.Local;
|
||||
options: LocalMemoryDriverOptions;
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import { DynamicModule, Global } from '@nestjs/common';
|
||||
|
||||
import { MemoryStorageType } from 'src/integrations/environment/interfaces/memory-storage.interface';
|
||||
|
||||
import { MemoryStorageDefaultSerializer } from 'src/integrations/memory-storage/serializers/default.serializer';
|
||||
import { createMemoryStorageInjectionToken } from 'src/integrations/memory-storage/memory-storage.util';
|
||||
|
||||
import {
|
||||
MemoryStorageDriverType,
|
||||
MemoryStorageModuleAsyncOptions,
|
||||
MemoryStorageModuleOptions,
|
||||
} from './interfaces';
|
||||
@ -59,7 +58,7 @@ export class MemoryStorageModule {
|
||||
|
||||
private static createStorageDriver(options: MemoryStorageModuleOptions) {
|
||||
switch (options.type) {
|
||||
case MemoryStorageType.Local:
|
||||
case MemoryStorageDriverType.Local:
|
||||
return new LocalMemoryDriver(
|
||||
options.identifier,
|
||||
options.options,
|
||||
|
||||
@ -1,17 +1,20 @@
|
||||
import { FactoryProvider, ModuleMetadata } from '@nestjs/common';
|
||||
|
||||
import { MessageQueueType } from 'src/integrations/environment/interfaces/message-queue.interface';
|
||||
|
||||
import { BullMQDriverOptions } from 'src/integrations/message-queue/drivers/bullmq.driver';
|
||||
import { PgBossDriverOptions } from 'src/integrations/message-queue/drivers/pg-boss.driver';
|
||||
|
||||
export enum MessageQueueDriverType {
|
||||
PgBoss = 'pg-boss',
|
||||
BullMQ = 'bull-mq',
|
||||
}
|
||||
|
||||
export interface PgBossDriverFactoryOptions {
|
||||
type: MessageQueueType.PgBoss;
|
||||
type: MessageQueueDriverType.PgBoss;
|
||||
options: PgBossDriverOptions;
|
||||
}
|
||||
|
||||
export interface BullMQDriverFactoryOptions {
|
||||
type: MessageQueueType.BullMQ;
|
||||
type: MessageQueueDriverType.BullMQ;
|
||||
options: BullMQDriverOptions;
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import {
|
||||
MessageQueueDriverType,
|
||||
MessageQueueModuleOptions,
|
||||
} from 'src/integrations/message-queue/interfaces';
|
||||
|
||||
/**
|
||||
* MessageQueue Module factory
|
||||
* @param environment
|
||||
* @returns MessageQueueModuleOptions
|
||||
*/
|
||||
export const messageQueueModuleFactory = async (
|
||||
environmentService: EnvironmentService,
|
||||
): Promise<MessageQueueModuleOptions> => {
|
||||
const driverType = environmentService.getMessageQueueDriverType();
|
||||
|
||||
switch (driverType) {
|
||||
case MessageQueueDriverType.PgBoss: {
|
||||
const connectionString = environmentService.getPGDatabaseUrl();
|
||||
|
||||
return {
|
||||
type: MessageQueueDriverType.PgBoss,
|
||||
options: {
|
||||
connectionString,
|
||||
},
|
||||
};
|
||||
}
|
||||
case MessageQueueDriverType.BullMQ: {
|
||||
const host = environmentService.getRedisHost();
|
||||
const port = environmentService.getRedisPort();
|
||||
|
||||
return {
|
||||
type: MessageQueueDriverType.BullMQ,
|
||||
options: {
|
||||
connection: {
|
||||
host,
|
||||
port,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw new Error(
|
||||
`Invalid message queue driver type (${driverType}), check your .env file`,
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -1,9 +1,11 @@
|
||||
import { DynamicModule, Global } from '@nestjs/common';
|
||||
|
||||
import { MessageQueueDriver } from 'src/integrations/message-queue/drivers/interfaces/message-queue-driver.interface';
|
||||
import { MessageQueueType } from 'src/integrations/environment/interfaces/message-queue.interface';
|
||||
|
||||
import { MessageQueueModuleAsyncOptions } from 'src/integrations/message-queue/interfaces';
|
||||
import {
|
||||
MessageQueueDriverType,
|
||||
MessageQueueModuleAsyncOptions,
|
||||
} from 'src/integrations/message-queue/interfaces';
|
||||
import {
|
||||
QUEUE_DRIVER,
|
||||
MessageQueues,
|
||||
@ -31,7 +33,7 @@ export class MessageQueueModule {
|
||||
useFactory: async (...args: any[]) => {
|
||||
const config = await options.useFactory(...args);
|
||||
|
||||
if (config.type === MessageQueueType.PgBoss) {
|
||||
if (config.type === MessageQueueDriverType.PgBoss) {
|
||||
const boss = new PgBossDriver(config.options);
|
||||
|
||||
await boss.init();
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { MemoryStorageType } from 'src/integrations/environment/interfaces/memory-storage.interface';
|
||||
|
||||
import { MemoryStorageDriverType } from 'src/integrations/memory-storage/interfaces';
|
||||
import { MemoryStorageModule } from 'src/integrations/memory-storage/memory-storage.module';
|
||||
import { MemoryStorageJsonSerializer } from 'src/integrations/memory-storage/serializers/json.serializer';
|
||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||
@ -15,24 +14,24 @@ import { WorkspaceSchemaStorageService } from 'src/workspace/workspace-schema-st
|
||||
WorkspaceCacheVersionModule,
|
||||
MemoryStorageModule.forRoot({
|
||||
identifier: 'objectMetadataCollection',
|
||||
type: MemoryStorageType.Local,
|
||||
type: MemoryStorageDriverType.Local,
|
||||
options: {},
|
||||
serializer: new MemoryStorageJsonSerializer<ObjectMetadataEntity[]>(),
|
||||
}),
|
||||
MemoryStorageModule.forRoot({
|
||||
identifier: 'typeDefs',
|
||||
type: MemoryStorageType.Local,
|
||||
type: MemoryStorageDriverType.Local,
|
||||
options: {},
|
||||
}),
|
||||
MemoryStorageModule.forRoot({
|
||||
identifier: 'usedScalarNames',
|
||||
type: MemoryStorageType.Local,
|
||||
type: MemoryStorageDriverType.Local,
|
||||
options: {},
|
||||
serializer: new MemoryStorageJsonSerializer<string[]>(),
|
||||
}),
|
||||
MemoryStorageModule.forRoot({
|
||||
identifier: 'cacheVersion',
|
||||
type: MemoryStorageType.Local,
|
||||
type: MemoryStorageDriverType.Local,
|
||||
options: {},
|
||||
}),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user