Better errors handling (#8835)

- [ ] Catch this specific `500` error
- [ ] Make sure catched `500` errors are sent to sentry for the Cloud
version
- [ ] Hide the option to sync email with google in this situation where
the according env var is missing
- [x] Add Worskpace information to all catched errors for better
debugging

fix #8607
This commit is contained in:
Guillim 2024-12-03 12:16:38 +01:00 committed by GitHub
parent 32194a88b3
commit 7e4277fbe4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 120 additions and 104 deletions

View File

@ -114,8 +114,13 @@ export class GraphQLConfigService
email: user.email, email: user.email,
firstName: user.firstName, firstName: user.firstName,
lastName: user.lastName, lastName: user.lastName,
workspaceId: workspace?.id, }
workspaceDisplayName: workspace?.displayName, : undefined,
workspace
? {
id: workspace.id,
displayName: workspace.displayName,
activationStatus: workspace.activationStatus,
} }
: undefined, : undefined,
); );

View File

@ -1,13 +1,13 @@
import { ExecutionContext, Injectable } from '@nestjs/common'; import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import {
AuthException,
AuthExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception';
import { GoogleAPIsOauthExchangeCodeForTokenStrategy } from 'src/engine/core-modules/auth/strategies/google-apis-oauth-exchange-code-for-token.auth.strategy'; import { GoogleAPIsOauthExchangeCodeForTokenStrategy } from 'src/engine/core-modules/auth/strategies/google-apis-oauth-exchange-code-for-token.auth.strategy';
import { TransientTokenService } from 'src/engine/core-modules/auth/token/services/transient-token.service'; import { TransientTokenService } from 'src/engine/core-modules/auth/token/services/transient-token.service';
import { setRequestExtraParams } from 'src/engine/core-modules/auth/utils/google-apis-set-request-extra-params.util'; import { setRequestExtraParams } from 'src/engine/core-modules/auth/utils/google-apis-set-request-extra-params.util';
import {
EnvironmentException,
EnvironmentExceptionCode,
} from 'src/engine/core-modules/environment/environment.exception';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
@ -41,9 +41,9 @@ export class GoogleAPIsOauthExchangeCodeForTokenGuard extends AuthGuard(
!this.environmentService.get('MESSAGING_PROVIDER_GMAIL_ENABLED') && !this.environmentService.get('MESSAGING_PROVIDER_GMAIL_ENABLED') &&
!this.environmentService.get('CALENDAR_PROVIDER_GOOGLE_ENABLED') !this.environmentService.get('CALENDAR_PROVIDER_GOOGLE_ENABLED')
) { ) {
throw new AuthException( throw new EnvironmentException(
'Google apis auth is not enabled', 'Google apis auth is not enabled',
AuthExceptionCode.FORBIDDEN_EXCEPTION, EnvironmentExceptionCode.ENVIRONMENT_VARIABLES_NOT_FOUND,
); );
} }

View File

@ -1,13 +1,13 @@
import { ExecutionContext, Injectable } from '@nestjs/common'; import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import {
AuthException,
AuthExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception';
import { GoogleAPIsOauthRequestCodeStrategy } from 'src/engine/core-modules/auth/strategies/google-apis-oauth-request-code.auth.strategy'; import { GoogleAPIsOauthRequestCodeStrategy } from 'src/engine/core-modules/auth/strategies/google-apis-oauth-request-code.auth.strategy';
import { TransientTokenService } from 'src/engine/core-modules/auth/token/services/transient-token.service'; import { TransientTokenService } from 'src/engine/core-modules/auth/token/services/transient-token.service';
import { setRequestExtraParams } from 'src/engine/core-modules/auth/utils/google-apis-set-request-extra-params.util'; import { setRequestExtraParams } from 'src/engine/core-modules/auth/utils/google-apis-set-request-extra-params.util';
import {
EnvironmentException,
EnvironmentExceptionCode,
} from 'src/engine/core-modules/environment/environment.exception';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
@ -41,9 +41,9 @@ export class GoogleAPIsOauthRequestCodeGuard extends AuthGuard('google-apis') {
!this.environmentService.get('MESSAGING_PROVIDER_GMAIL_ENABLED') && !this.environmentService.get('MESSAGING_PROVIDER_GMAIL_ENABLED') &&
!this.environmentService.get('CALENDAR_PROVIDER_GOOGLE_ENABLED') !this.environmentService.get('CALENDAR_PROVIDER_GOOGLE_ENABLED')
) { ) {
throw new AuthException( throw new EnvironmentException(
'Google apis auth is not enabled', 'Google apis auth is not enabled',
AuthExceptionCode.FORBIDDEN_EXCEPTION, EnvironmentExceptionCode.ENVIRONMENT_VARIABLES_NOT_FOUND,
); );
} }

View File

@ -2,11 +2,11 @@ import { CanActivate, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import {
AuthException,
AuthExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception';
import { GoogleStrategy } from 'src/engine/core-modules/auth/strategies/google.auth.strategy'; import { GoogleStrategy } from 'src/engine/core-modules/auth/strategies/google.auth.strategy';
import {
EnvironmentException,
EnvironmentExceptionCode,
} from 'src/engine/core-modules/environment/environment.exception';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
@Injectable() @Injectable()
@ -15,9 +15,9 @@ export class GoogleProviderEnabledGuard implements CanActivate {
canActivate(): boolean | Promise<boolean> | Observable<boolean> { canActivate(): boolean | Promise<boolean> | Observable<boolean> {
if (!this.environmentService.get('AUTH_GOOGLE_ENABLED')) { if (!this.environmentService.get('AUTH_GOOGLE_ENABLED')) {
throw new AuthException( throw new EnvironmentException(
'Google auth is not enabled', 'Google auth is not enabled',
AuthExceptionCode.FORBIDDEN_EXCEPTION, EnvironmentExceptionCode.ENVIRONMENT_VARIABLES_NOT_FOUND,
); );
} }

View File

@ -2,11 +2,11 @@ import { CanActivate, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import {
AuthException,
AuthExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception';
import { MicrosoftStrategy } from 'src/engine/core-modules/auth/strategies/microsoft.auth.strategy'; import { MicrosoftStrategy } from 'src/engine/core-modules/auth/strategies/microsoft.auth.strategy';
import {
EnvironmentException,
EnvironmentExceptionCode,
} from 'src/engine/core-modules/environment/environment.exception';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
@Injectable() @Injectable()
@ -15,9 +15,9 @@ export class MicrosoftProviderEnabledGuard implements CanActivate {
canActivate(): boolean | Promise<boolean> | Observable<boolean> { canActivate(): boolean | Promise<boolean> | Observable<boolean> {
if (!this.environmentService.get('AUTH_MICROSOFT_ENABLED')) { if (!this.environmentService.get('AUTH_MICROSOFT_ENABLED')) {
throw new AuthException( throw new EnvironmentException(
'Microsoft auth is not enabled', 'Microsoft auth is not enabled',
AuthExceptionCode.FORBIDDEN_EXCEPTION, EnvironmentExceptionCode.ENVIRONMENT_VARIABLES_NOT_FOUND,
); );
} }

View File

@ -5,9 +5,9 @@ import { CanActivate, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { import {
AuthException, EnvironmentException,
AuthExceptionCode, EnvironmentExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception'; } from 'src/engine/core-modules/environment/environment.exception';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
@Injectable() @Injectable()
@ -16,9 +16,9 @@ export class SSOProviderEnabledGuard implements CanActivate {
canActivate(): boolean | Promise<boolean> | Observable<boolean> { canActivate(): boolean | Promise<boolean> | Observable<boolean> {
if (!this.environmentService.get('ENTERPRISE_KEY')) { if (!this.environmentService.get('ENTERPRISE_KEY')) {
throw new AuthException( throw new EnvironmentException(
'Enterprise key must be defined to use SSO', 'Enterprise key must be defined to use SSO',
AuthExceptionCode.FORBIDDEN_EXCEPTION, EnvironmentExceptionCode.ENVIRONMENT_VARIABLES_NOT_FOUND,
); );
} }

View File

@ -2,13 +2,14 @@ import { HttpService } from '@nestjs/axios';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { isDefined } from 'class-validator';
import FileType from 'file-type'; import FileType from 'file-type';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
import { isDefined } from 'class-validator';
import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface'; import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface';
import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity';
import { import {
AuthException, AuthException,
AuthExceptionCode, AuthExceptionCode,
@ -18,6 +19,11 @@ import {
compareHash, compareHash,
hashPassword, hashPassword,
} from 'src/engine/core-modules/auth/auth.util'; } from 'src/engine/core-modules/auth/auth.util';
import {
EnvironmentException,
EnvironmentExceptionCode,
} from 'src/engine/core-modules/environment/environment.exception';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service'; import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service'; import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service';
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
@ -26,9 +32,7 @@ import {
Workspace, Workspace,
WorkspaceActivationStatus, WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity'; } from 'src/engine/core-modules/workspace/workspace.entity';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { getImageBufferFromUrl } from 'src/utils/image'; import { getImageBufferFromUrl } from 'src/utils/image';
import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity';
export type SignInUpServiceInput = { export type SignInUpServiceInput = {
email: string; email: string;
@ -298,9 +302,9 @@ export class SignInUpService {
picture: SignInUpServiceInput['picture']; picture: SignInUpServiceInput['picture'];
}) { }) {
if (this.environmentService.get('IS_SIGN_UP_DISABLED')) { if (this.environmentService.get('IS_SIGN_UP_DISABLED')) {
throw new AuthException( throw new EnvironmentException(
'Sign up is disabled', 'Sign up is disabled',
AuthExceptionCode.FORBIDDEN_EXCEPTION, EnvironmentExceptionCode.ENVIRONMENT_VARIABLES_NOT_FOUND,
); );
} }

View File

@ -17,6 +17,10 @@ import {
AuthContext, AuthContext,
JwtPayload, JwtPayload,
} from 'src/engine/core-modules/auth/types/auth-context.type'; } from 'src/engine/core-modules/auth/types/auth-context.type';
import {
EnvironmentException,
EnvironmentExceptionCode,
} from 'src/engine/core-modules/environment/environment.exception';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service'; import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service';
import { User } from 'src/engine/core-modules/user/user.entity'; import { User } from 'src/engine/core-modules/user/user.entity';
@ -42,9 +46,9 @@ export class AccessTokenService {
const expiresIn = this.environmentService.get('ACCESS_TOKEN_EXPIRES_IN'); const expiresIn = this.environmentService.get('ACCESS_TOKEN_EXPIRES_IN');
if (!expiresIn) { if (!expiresIn) {
throw new AuthException( throw new EnvironmentException(
'Expiration time for access token is not set', 'Expiration time for access token is not set',
AuthExceptionCode.INTERNAL_SERVER_ERROR, EnvironmentExceptionCode.ENVIRONMENT_VARIABLES_NOT_FOUND,
); );
} }

View File

@ -1,6 +1,6 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { AuthException } from 'src/engine/core-modules/auth/auth.exception'; import { EnvironmentException } from 'src/engine/core-modules/environment/environment.exception';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service'; import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service';
@ -76,7 +76,7 @@ describe('LoginTokenService', () => {
await expect( await expect(
service.generateLoginToken('test@example.com'), service.generateLoginToken('test@example.com'),
).rejects.toThrow(AuthException); ).rejects.toThrow(EnvironmentException);
}); });
}); });

View File

@ -3,11 +3,11 @@ import { Injectable } from '@nestjs/common';
import { addMilliseconds } from 'date-fns'; import { addMilliseconds } from 'date-fns';
import ms from 'ms'; import ms from 'ms';
import {
AuthException,
AuthExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception';
import { AuthToken } from 'src/engine/core-modules/auth/dto/token.entity'; import { AuthToken } from 'src/engine/core-modules/auth/dto/token.entity';
import {
EnvironmentException,
EnvironmentExceptionCode,
} from 'src/engine/core-modules/environment/environment.exception';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service'; import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service';
@ -23,9 +23,9 @@ export class LoginTokenService {
const expiresIn = this.environmentService.get('LOGIN_TOKEN_EXPIRES_IN'); const expiresIn = this.environmentService.get('LOGIN_TOKEN_EXPIRES_IN');
if (!expiresIn) { if (!expiresIn) {
throw new AuthException( throw new EnvironmentException(
'Expiration time for access token is not set', 'Expiration time for access token is not set',
AuthExceptionCode.INTERNAL_SERVER_ERROR, EnvironmentExceptionCode.ENVIRONMENT_VARIABLES_NOT_FOUND,
); );
} }

View File

@ -1,6 +1,6 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { AuthException } from 'src/engine/core-modules/auth/auth.exception'; import { EnvironmentException } from 'src/engine/core-modules/environment/environment.exception';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service'; import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service';
@ -88,7 +88,7 @@ describe('TransientTokenService', () => {
await expect( await expect(
service.generateTransientToken('member-id', 'user-id', 'workspace-id'), service.generateTransientToken('member-id', 'user-id', 'workspace-id'),
).rejects.toThrow(AuthException); ).rejects.toThrow(EnvironmentException);
}); });
}); });

View File

@ -3,11 +3,11 @@ import { Injectable } from '@nestjs/common';
import { addMilliseconds } from 'date-fns'; import { addMilliseconds } from 'date-fns';
import ms from 'ms'; import ms from 'ms';
import {
AuthException,
AuthExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception';
import { AuthToken } from 'src/engine/core-modules/auth/dto/token.entity'; import { AuthToken } from 'src/engine/core-modules/auth/dto/token.entity';
import {
EnvironmentException,
EnvironmentExceptionCode,
} from 'src/engine/core-modules/environment/environment.exception';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service'; import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service';
@ -32,9 +32,9 @@ export class TransientTokenService {
); );
if (!expiresIn) { if (!expiresIn) {
throw new AuthException( throw new EnvironmentException(
'Expiration time for access token is not set', 'Expiration time for access token is not set',
AuthExceptionCode.INTERNAL_SERVER_ERROR, EnvironmentExceptionCode.ENVIRONMENT_VARIABLES_NOT_FOUND,
); );
} }

View File

@ -0,0 +1,12 @@
import { CustomException } from 'src/utils/custom-exception';
export class EnvironmentException extends CustomException {
code: EnvironmentExceptionCode;
constructor(message: string, code: EnvironmentExceptionCode) {
super(message, code);
}
}
export enum EnvironmentExceptionCode {
ENVIRONMENT_VARIABLES_NOT_FOUND = 'ENVIRONMENT_VARIABLES_NOT_FOUND',
}

View File

@ -1,5 +1,4 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
import { ExceptionHandlerUser } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-user.interface';
import { ExceptionHandlerOptions } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-options.interface'; import { ExceptionHandlerOptions } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-options.interface';
import { ExceptionHandlerDriverInterface } from 'src/engine/core-modules/exception-handler/interfaces'; import { ExceptionHandlerDriverInterface } from 'src/engine/core-modules/exception-handler/interfaces';
@ -18,11 +17,4 @@ export class ExceptionHandlerConsoleDriver
return []; return [];
} }
captureMessage(message: string, user?: ExceptionHandlerUser): void {
console.group('Message Captured');
console.info(user);
console.info(message);
console.groupEnd();
}
} }

View File

@ -1,7 +1,6 @@
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { ExceptionHandlerOptions } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-options.interface'; import { ExceptionHandlerOptions } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-options.interface';
import { ExceptionHandlerUser } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-user.interface';
import { ExceptionHandlerDriverInterface } from 'src/engine/core-modules/exception-handler/interfaces'; import { ExceptionHandlerDriverInterface } from 'src/engine/core-modules/exception-handler/interfaces';
@ -24,14 +23,16 @@ export class ExceptionHandlerSentryDriver
scope.setExtra('document', options.document); scope.setExtra('document', options.document);
} }
if (options?.workspace) {
scope.setExtra('workspace', options.workspace);
}
if (options?.user) { if (options?.user) {
scope.setUser({ scope.setUser({
id: options.user.id, id: options.user.id,
email: options.user.email, email: options.user.email,
firstName: options.user.firstName, firstName: options.user.firstName,
lastName: options.user.lastName, lastName: options.user.lastName,
workspaceId: options.user.workspaceId,
workspaceDisplayName: options.user.workspaceDisplayName,
}); });
} }
@ -69,21 +70,4 @@ export class ExceptionHandlerSentryDriver
return eventIds; return eventIds;
} }
captureMessage(message: string, user?: ExceptionHandlerUser) {
Sentry.captureMessage(message, (scope) => {
if (user) {
scope.setUser({
id: user.id,
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
workspaceId: user.workspaceId,
workspaceDisplayName: user.workspaceDisplayName,
});
}
return scope;
});
}
} }

View File

@ -1,10 +1,8 @@
import { ExceptionHandlerOptions } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-options.interface'; import { ExceptionHandlerOptions } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-options.interface';
import { ExceptionHandlerUser } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-user.interface';
export interface ExceptionHandlerDriverInterface { export interface ExceptionHandlerDriverInterface {
captureExceptions( captureExceptions(
exceptions: ReadonlyArray<any>, exceptions: ReadonlyArray<any>,
options?: ExceptionHandlerOptions, options?: ExceptionHandlerOptions,
): string[]; ): string[];
captureMessage(message: string, user?: ExceptionHandlerUser): void;
} }

View File

@ -1,6 +1,7 @@
import { OperationTypeNode } from 'graphql'; import { OperationTypeNode } from 'graphql';
import { ExceptionHandlerUser } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-user.interface'; import { ExceptionHandlerUser } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-user.interface';
import { ExceptionHandlerWorkspace } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-workspace.interface';
export interface ExceptionHandlerOptions { export interface ExceptionHandlerOptions {
operation?: { operation?: {
@ -9,4 +10,5 @@ export interface ExceptionHandlerOptions {
}; };
document?: string; document?: string;
user?: ExceptionHandlerUser | null; user?: ExceptionHandlerUser | null;
workspace?: ExceptionHandlerWorkspace | null;
} }

View File

@ -3,6 +3,4 @@ export interface ExceptionHandlerUser {
email?: string; email?: string;
firstName?: string; firstName?: string;
lastName?: string; lastName?: string;
workspaceId?: string;
workspaceDisplayName?: string;
} }

View File

@ -0,0 +1,6 @@
export interface ExceptionHandlerWorkspace {
id?: string;
displayName?: string;
activationStatus?: string;
createdAt?: string;
}

View File

@ -8,10 +8,10 @@ import { GraphQLError, Kind, OperationDefinitionNode, print } from 'graphql';
import { GraphQLContext } from 'src/engine/api/graphql/graphql-config/interfaces/graphql-context.interface'; import { GraphQLContext } from 'src/engine/api/graphql/graphql-config/interfaces/graphql-context.interface';
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
import { generateGraphQLErrorFromError } from 'src/engine/core-modules/graphql/utils/generate-graphql-error-from-error.util'; import { generateGraphQLErrorFromError } from 'src/engine/core-modules/graphql/utils/generate-graphql-error-from-error.util';
import { BaseGraphQLError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; import { BaseGraphQLError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
import { shouldCaptureException } from 'src/engine/core-modules/graphql/utils/should-capture-exception.util'; import { shouldCaptureException } from 'src/engine/core-modules/graphql/utils/should-capture-exception.util';
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
type GraphQLErrorHandlerHookOptions = { type GraphQLErrorHandlerHookOptions = {
/** /**
@ -93,6 +93,14 @@ export const useGraphQLErrorHandlerHook = <
}, },
document, document,
user, user,
workspace: {
id: args.contextValue.req.workspace?.id,
displayName: args.contextValue.req.workspace?.displayName,
createdAt:
args.contextValue.req.workspace?.createdAt.toISOString(),
activationStatus:
args.contextValue.req.workspace?.activationStatus,
},
}, },
); );

View File

@ -3,7 +3,9 @@ import { HttpException } from '@nestjs/common';
import { GraphQLError } from 'graphql'; import { GraphQLError } from 'graphql';
import { ExceptionHandlerUser } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-user.interface'; import { ExceptionHandlerUser } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-user.interface';
import { ExceptionHandlerWorkspace } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-workspace.interface';
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
import { import {
AuthenticationError, AuthenticationError,
BaseGraphQLError, BaseGraphQLError,
@ -15,7 +17,6 @@ import {
TimeoutError, TimeoutError,
ValidationError, ValidationError,
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
const graphQLPredefinedExceptions = { const graphQLPredefinedExceptions = {
400: ValidationError, 400: ValidationError,
@ -42,8 +43,9 @@ export const handleExceptionAndConvertToGraphQLError = (
exception: Error, exception: Error,
exceptionHandlerService: ExceptionHandlerService, exceptionHandlerService: ExceptionHandlerService,
user?: ExceptionHandlerUser, user?: ExceptionHandlerUser,
workspace?: ExceptionHandlerWorkspace,
): BaseGraphQLError => { ): BaseGraphQLError => {
handleException(exception, exceptionHandlerService, user); handleException(exception, exceptionHandlerService, user, workspace);
return convertExceptionToGraphQLError(exception); return convertExceptionToGraphQLError(exception);
}; };
@ -74,12 +76,13 @@ const handleException = (
exception: Error, exception: Error,
exceptionHandlerService: ExceptionHandlerService, exceptionHandlerService: ExceptionHandlerService,
user?: ExceptionHandlerUser, user?: ExceptionHandlerUser,
workspace?: ExceptionHandlerWorkspace,
): void => { ): void => {
if (shouldFilterException(exception)) { if (shouldFilterException(exception)) {
return; return;
} }
exceptionHandlerService.captureExceptions([exception], { user }); exceptionHandlerService.captureExceptions([exception], { user, workspace });
}; };
export const convertExceptionToGraphQLError = ( export const convertExceptionToGraphQLError = (

View File

@ -78,8 +78,8 @@ export class CalendarEventListFetchCronJob {
} }
} catch (error) { } catch (error) {
this.exceptionHandlerService.captureExceptions([error], { this.exceptionHandlerService.captureExceptions([error], {
user: { workspace: {
workspaceId: activeWorkspace.id, id: activeWorkspace.id,
}, },
}); });
} }

View File

@ -75,8 +75,8 @@ export class CalendarEventsImportCronJob {
} }
} catch (error) { } catch (error) {
this.exceptionHandlerService.captureExceptions([error], { this.exceptionHandlerService.captureExceptions([error], {
user: { workspace: {
workspaceId: activeWorkspace.id, id: activeWorkspace.id,
}, },
}); });
} }

View File

@ -52,8 +52,8 @@ export class CalendarOngoingStaleCronJob {
); );
} catch (error) { } catch (error) {
this.exceptionHandlerService.captureExceptions([error], { this.exceptionHandlerService.captureExceptions([error], {
user: { workspace: {
workspaceId: activeWorkspace.id, id: activeWorkspace.id,
}, },
}); });
} }

View File

@ -79,8 +79,8 @@ export class MessagingMessageListFetchCronJob {
} }
} catch (error) { } catch (error) {
this.exceptionHandlerService.captureExceptions([error], { this.exceptionHandlerService.captureExceptions([error], {
user: { workspace: {
workspaceId: activeWorkspace.id, id: activeWorkspace.id,
}, },
}); });
} }

View File

@ -76,8 +76,8 @@ export class MessagingMessagesImportCronJob {
} }
} catch (error) { } catch (error) {
this.exceptionHandlerService.captureExceptions([error], { this.exceptionHandlerService.captureExceptions([error], {
user: { workspace: {
workspaceId: activeWorkspace.id, id: activeWorkspace.id,
}, },
}); });
} }

View File

@ -52,8 +52,8 @@ export class MessagingOngoingStaleCronJob {
); );
} catch (error) { } catch (error) {
this.exceptionHandlerService.captureExceptions([error], { this.exceptionHandlerService.captureExceptions([error], {
user: { workspace: {
workspaceId: activeWorkspace.id, id: activeWorkspace.id,
}, },
}); });
} }

View File

@ -82,8 +82,8 @@ export class MessagingMessageChannelSyncStatusMonitoringCronJob {
} }
} catch (error) { } catch (error) {
this.exceptionHandlerService.captureExceptions([error], { this.exceptionHandlerService.captureExceptions([error], {
user: { workspace: {
workspaceId: activeWorkspace.id, id: activeWorkspace.id,
}, },
}); });
} }