Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1f12178ad2 | |||
| ffe9fff33c |
@@ -194,7 +194,7 @@ export const useAuth = () => {
|
||||
// TODO: Get intellisense for graphql error extensions code (codegen?)
|
||||
if (
|
||||
error instanceof ApolloError &&
|
||||
error.graphQLErrors[0]?.extensions?.code === 'EMAIL_NOT_VERIFIED'
|
||||
error.graphQLErrors[0]?.extensions?.subCode === 'EMAIL_NOT_VERIFIED'
|
||||
) {
|
||||
setSearchParams({ email });
|
||||
setSignInUpStep(SignInUpStep.EmailVerification);
|
||||
|
||||
+1
-2
@@ -1,6 +1,7 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class GraphqlQueryRunnerException extends CustomException {
|
||||
declare code: GraphqlQueryRunnerExceptionCode;
|
||||
constructor(message: string, code: GraphqlQueryRunnerExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
@@ -18,8 +19,6 @@ export enum GraphqlQueryRunnerExceptionCode {
|
||||
RECORD_NOT_FOUND = 'RECORD_NOT_FOUND',
|
||||
INVALID_ARGS_FIRST = 'INVALID_ARGS_FIRST',
|
||||
INVALID_ARGS_LAST = 'INVALID_ARGS_LAST',
|
||||
METADATA_CACHE_VERSION_NOT_FOUND = 'METADATA_CACHE_VERSION_NOT_FOUND',
|
||||
METADATA_CACHE_FEATURE_FLAG_RECOMPUTATION_REQUIRED = 'METADATA_CACHE_FEATURE_FLAG_RECOMPUTATION_REQUIRED',
|
||||
RELATION_SETTINGS_NOT_FOUND = 'RELATION_SETTINGS_NOT_FOUND',
|
||||
RELATION_TARGET_OBJECT_METADATA_NOT_FOUND = 'RELATION_TARGET_OBJECT_METADATA_NOT_FOUND',
|
||||
NOT_IMPLEMENTED = 'NOT_IMPLEMENTED',
|
||||
|
||||
+10
-3
@@ -3,7 +3,6 @@ import {
|
||||
GraphqlQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||
import {
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
@@ -26,7 +25,15 @@ export const graphqlQueryRunnerExceptionHandler = (
|
||||
throw new UserInputError(error.message);
|
||||
case GraphqlQueryRunnerExceptionCode.RECORD_NOT_FOUND:
|
||||
throw new NotFoundError(error.message);
|
||||
default:
|
||||
throw new InternalServerError(error.message);
|
||||
case GraphqlQueryRunnerExceptionCode.RELATION_SETTINGS_NOT_FOUND:
|
||||
case GraphqlQueryRunnerExceptionCode.RELATION_TARGET_OBJECT_METADATA_NOT_FOUND:
|
||||
case GraphqlQueryRunnerExceptionCode.OBJECT_METADATA_COLLECTION_NOT_FOUND:
|
||||
case GraphqlQueryRunnerExceptionCode.INVALID_POST_HOOK_PAYLOAD:
|
||||
throw error;
|
||||
default: {
|
||||
const _exhaustiveCheck: never = error.code;
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+6
-3
@@ -4,7 +4,6 @@ import {
|
||||
} from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception';
|
||||
import {
|
||||
ForbiddenError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
TimeoutError,
|
||||
UserInputError,
|
||||
@@ -26,7 +25,11 @@ export const workspaceExceptionHandler = (
|
||||
case WorkspaceQueryRunnerExceptionCode.QUERY_TIMEOUT:
|
||||
throw new TimeoutError(error.message);
|
||||
case WorkspaceQueryRunnerExceptionCode.INTERNAL_SERVER_ERROR:
|
||||
default:
|
||||
throw new InternalServerError(error.message);
|
||||
throw error;
|
||||
default: {
|
||||
const _exhaustiveCheck: never = error.code;
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+2
@@ -1,8 +1,10 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class WorkspaceQueryRunnerException extends CustomException {
|
||||
code: WorkspaceQueryRunnerExceptionCode;
|
||||
constructor(message: string, code: WorkspaceQueryRunnerExceptionCode) {
|
||||
super(message, code);
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@ import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twent
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
import {
|
||||
WorkspaceMetadataVersionException,
|
||||
WorkspaceMetadataVersionExceptionCode,
|
||||
} from 'src/engine/metadata-modules/workspace-metadata-version/exceptions/workspace-metadata-version.exception';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
@Injectable()
|
||||
@@ -89,9 +93,9 @@ export class WorkspaceSchemaFactory {
|
||||
}
|
||||
|
||||
if (!currentCacheVersion) {
|
||||
throw new GraphqlQueryRunnerException(
|
||||
throw new WorkspaceMetadataVersionException(
|
||||
'Metadata cache version not found',
|
||||
GraphqlQueryRunnerExceptionCode.METADATA_CACHE_VERSION_NOT_FOUND,
|
||||
WorkspaceMetadataVersionExceptionCode.METADATA_VERSION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class AuthException extends CustomException {
|
||||
declare code: AuthExceptionCode;
|
||||
constructor(message: string, code: AuthExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
|
||||
+19
-6
@@ -6,9 +6,7 @@ import {
|
||||
} from 'src/engine/core-modules/auth/auth.exception';
|
||||
import {
|
||||
AuthenticationError,
|
||||
EmailNotVerifiedError,
|
||||
ForbiddenError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
@@ -21,18 +19,33 @@ export class AuthGraphqlApiExceptionFilter implements ExceptionFilter {
|
||||
throw new NotFoundError(exception.message);
|
||||
case AuthExceptionCode.INVALID_INPUT:
|
||||
throw new UserInputError(exception.message);
|
||||
case AuthExceptionCode.EMAIL_NOT_VERIFIED:
|
||||
throw new EmailNotVerifiedError(exception.message);
|
||||
case AuthExceptionCode.FORBIDDEN_EXCEPTION:
|
||||
throw new ForbiddenError(exception.message);
|
||||
case AuthExceptionCode.EMAIL_NOT_VERIFIED:
|
||||
throw new ForbiddenError(exception.message, {
|
||||
subCode: AuthExceptionCode.EMAIL_NOT_VERIFIED,
|
||||
});
|
||||
case AuthExceptionCode.UNAUTHENTICATED:
|
||||
case AuthExceptionCode.USER_NOT_FOUND:
|
||||
case AuthExceptionCode.WORKSPACE_NOT_FOUND:
|
||||
throw new AuthenticationError(exception.message);
|
||||
case AuthExceptionCode.INVALID_DATA:
|
||||
case AuthExceptionCode.INTERNAL_SERVER_ERROR:
|
||||
default:
|
||||
throw new InternalServerError(exception.message);
|
||||
case AuthExceptionCode.USER_WORKSPACE_NOT_FOUND:
|
||||
case AuthExceptionCode.INSUFFICIENT_SCOPES:
|
||||
case AuthExceptionCode.OAUTH_ACCESS_DENIED:
|
||||
case AuthExceptionCode.SSO_AUTH_FAILED:
|
||||
case AuthExceptionCode.USE_SSO_AUTH:
|
||||
case AuthExceptionCode.SIGNUP_DISABLED:
|
||||
case AuthExceptionCode.GOOGLE_API_AUTH_DISABLED:
|
||||
case AuthExceptionCode.MICROSOFT_API_AUTH_DISABLED:
|
||||
case AuthExceptionCode.MISSING_ENVIRONMENT_VARIABLE:
|
||||
throw exception;
|
||||
default: {
|
||||
const _exhaustiveCheck: never = exception.code;
|
||||
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import {
|
||||
EmailVerificationException,
|
||||
EmailVerificationExceptionCode,
|
||||
} from 'src/engine/core-modules/email-verification/email-verification.exception';
|
||||
import {
|
||||
ForbiddenError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
|
||||
export const emailVerificationGraphqlApiExceptionHandler = (
|
||||
error: EmailVerificationException,
|
||||
) => {
|
||||
switch (error.code) {
|
||||
case EmailVerificationExceptionCode.INVALID_TOKEN:
|
||||
case EmailVerificationExceptionCode.INVALID_APP_TOKEN_TYPE:
|
||||
case EmailVerificationExceptionCode.TOKEN_EXPIRED:
|
||||
case EmailVerificationExceptionCode.RATE_LIMIT_EXCEEDED:
|
||||
throw new ForbiddenError(error.message);
|
||||
case EmailVerificationExceptionCode.EMAIL_MISSING:
|
||||
case EmailVerificationExceptionCode.EMAIL_ALREADY_VERIFIED:
|
||||
case EmailVerificationExceptionCode.EMAIL_VERIFICATION_NOT_REQUIRED:
|
||||
throw new UserInputError(error.message);
|
||||
default: {
|
||||
const _exhaustiveCheck: never = error.code;
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class EmailVerificationException extends CustomException {
|
||||
declare code: EmailVerificationExceptionCode;
|
||||
constructor(message: string, code: EmailVerificationExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
|
||||
+18
-9
@@ -5,6 +5,8 @@ import { SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
|
||||
import { ResendEmailVerificationTokenInput } from 'src/engine/core-modules/email-verification/dtos/resend-email-verification-token.input';
|
||||
import { ResendEmailVerificationTokenOutput } from 'src/engine/core-modules/email-verification/dtos/resend-email-verification-token.output';
|
||||
import { emailVerificationGraphqlApiExceptionHandler } from 'src/engine/core-modules/email-verification/email-verification-exception-handler.util';
|
||||
import { EmailVerificationException } from 'src/engine/core-modules/email-verification/email-verification.exception';
|
||||
import { EmailVerificationService } from 'src/engine/core-modules/email-verification/services/email-verification.service';
|
||||
import { I18nContext } from 'src/engine/core-modules/i18n/types/i18n-context.type';
|
||||
import { OriginHeader } from 'src/engine/decorators/auth/origin-header.decorator';
|
||||
@@ -23,15 +25,22 @@ export class EmailVerificationResolver {
|
||||
@OriginHeader() origin: string,
|
||||
@Context() context: I18nContext,
|
||||
): Promise<ResendEmailVerificationTokenOutput> {
|
||||
const workspace =
|
||||
await this.domainManagerService.getWorkspaceByOriginOrDefaultWorkspace(
|
||||
origin,
|
||||
);
|
||||
try {
|
||||
const workspace =
|
||||
await this.domainManagerService.getWorkspaceByOriginOrDefaultWorkspace(
|
||||
origin,
|
||||
);
|
||||
|
||||
return await this.emailVerificationService.resendEmailVerificationToken(
|
||||
resendEmailVerificationTokenInput.email,
|
||||
workspace,
|
||||
context.req.headers['x-locale'] ?? SOURCE_LOCALE,
|
||||
);
|
||||
return await this.emailVerificationService.resendEmailVerificationToken(
|
||||
resendEmailVerificationTokenInput.email,
|
||||
workspace,
|
||||
context.req.headers['x-locale'] ?? SOURCE_LOCALE,
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof EmailVerificationException) {
|
||||
return emailVerificationGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-2
@@ -3,6 +3,7 @@ import * as Sentry from '@sentry/node';
|
||||
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 { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class ExceptionHandlerSentryDriver
|
||||
implements ExceptionHandlerDriverInterface
|
||||
@@ -15,8 +16,7 @@ export class ExceptionHandlerSentryDriver
|
||||
|
||||
Sentry.withScope((scope) => {
|
||||
if (options?.operation) {
|
||||
scope.setTag('operation', options.operation.name);
|
||||
scope.setTag('operationName', options.operation.name);
|
||||
scope.setExtra('operation', options.operation.name);
|
||||
}
|
||||
|
||||
if (options?.document) {
|
||||
@@ -49,6 +49,11 @@ export class ExceptionHandlerSentryDriver
|
||||
});
|
||||
}
|
||||
|
||||
if (exception instanceof CustomException) {
|
||||
scope.setTag('customExceptionCode', exception.code);
|
||||
scope.setFingerprint([exception.code]);
|
||||
}
|
||||
|
||||
const eventId = Sentry.captureException(exception, {
|
||||
contexts: {
|
||||
GraphQL: {
|
||||
|
||||
+4
-13
@@ -24,7 +24,6 @@ export enum ErrorCode {
|
||||
PERSISTED_QUERY_NOT_SUPPORTED = 'PERSISTED_QUERY_NOT_SUPPORTED',
|
||||
BAD_USER_INPUT = 'BAD_USER_INPUT',
|
||||
NOT_FOUND = 'NOT_FOUND',
|
||||
EMAIL_NOT_VERIFIED = 'EMAIL_NOT_VERIFIED',
|
||||
METHOD_NOT_ALLOWED = 'METHOD_NOT_ALLOWED',
|
||||
CONFLICT = 'CONFLICT',
|
||||
TIMEOUT = 'TIMEOUT',
|
||||
@@ -107,16 +106,16 @@ export class ValidationError extends BaseGraphQLError {
|
||||
}
|
||||
|
||||
export class AuthenticationError extends BaseGraphQLError {
|
||||
constructor(message: string) {
|
||||
super(message, ErrorCode.UNAUTHENTICATED);
|
||||
constructor(message: string, extensions?: Record<string, any>) {
|
||||
super(message, ErrorCode.UNAUTHENTICATED, extensions);
|
||||
|
||||
Object.defineProperty(this, 'name', { value: 'AuthenticationError' });
|
||||
}
|
||||
}
|
||||
|
||||
export class ForbiddenError extends BaseGraphQLError {
|
||||
constructor(message: string) {
|
||||
super(message, ErrorCode.FORBIDDEN);
|
||||
constructor(message: string, extensions?: Record<string, any>) {
|
||||
super(message, ErrorCode.FORBIDDEN, extensions);
|
||||
|
||||
Object.defineProperty(this, 'name', { value: 'ForbiddenError' });
|
||||
}
|
||||
@@ -161,14 +160,6 @@ export class NotFoundError extends BaseGraphQLError {
|
||||
}
|
||||
}
|
||||
|
||||
export class EmailNotVerifiedError extends BaseGraphQLError {
|
||||
constructor(message: string) {
|
||||
super(message, ErrorCode.EMAIL_NOT_VERIFIED);
|
||||
|
||||
Object.defineProperty(this, 'name', { value: 'EmailNotVerifiedError' });
|
||||
}
|
||||
}
|
||||
|
||||
export class MethodNotAllowedError extends BaseGraphQLError {
|
||||
constructor(message: string) {
|
||||
super(message, ErrorCode.METHOD_NOT_ALLOWED);
|
||||
|
||||
+10
-1
@@ -1,3 +1,5 @@
|
||||
import { HttpException } from '@nestjs/common';
|
||||
|
||||
import {
|
||||
BaseGraphQLError,
|
||||
ErrorCode,
|
||||
@@ -12,7 +14,6 @@ export const graphQLErrorCodesToFilterOut = [
|
||||
ErrorCode.TIMEOUT,
|
||||
ErrorCode.CONFLICT,
|
||||
ErrorCode.BAD_USER_INPUT,
|
||||
ErrorCode.EMAIL_NOT_VERIFIED,
|
||||
];
|
||||
|
||||
export const shouldCaptureException = (exception: Error): boolean => {
|
||||
@@ -23,5 +24,13 @@ export const shouldCaptureException = (exception: Error): boolean => {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
exception instanceof HttpException &&
|
||||
exception.getStatus() >= 400 &&
|
||||
exception.getStatus() < 500
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
+1
-2
@@ -1,13 +1,12 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class SearchException extends CustomException {
|
||||
declare code: SearchExceptionCode;
|
||||
constructor(message: string, code: SearchExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
}
|
||||
|
||||
export enum SearchExceptionCode {
|
||||
METADATA_CACHE_VERSION_NOT_FOUND = 'METADATA_CACHE_VERSION_NOT_FOUND',
|
||||
LABEL_IDENTIFIER_FIELD_NOT_FOUND = 'LABEL_IDENTIFIER_FIELD_NOT_FOUND',
|
||||
OBJECT_METADATA_MAP_NOT_FOUND = 'OBJECT_METADATA_MAP_NOT_FOUND',
|
||||
}
|
||||
|
||||
+11
-4
@@ -1,7 +1,9 @@
|
||||
import { Catch, ExceptionFilter } from '@nestjs/common';
|
||||
|
||||
import { InternalServerError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { SearchException } from 'src/engine/core-modules/search/exceptions/search.exception';
|
||||
import {
|
||||
SearchException,
|
||||
SearchExceptionCode,
|
||||
} from 'src/engine/core-modules/search/exceptions/search.exception';
|
||||
|
||||
@Catch(SearchException)
|
||||
export class SearchApiExceptionFilter implements ExceptionFilter {
|
||||
@@ -9,8 +11,13 @@ export class SearchApiExceptionFilter implements ExceptionFilter {
|
||||
|
||||
catch(exception: SearchException) {
|
||||
switch (exception.code) {
|
||||
default:
|
||||
throw new InternalServerError(exception.message);
|
||||
case SearchExceptionCode.LABEL_IDENTIFIER_FIELD_NOT_FOUND:
|
||||
throw exception;
|
||||
default: {
|
||||
const _exhaustiveCheck: never = exception.code;
|
||||
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,16 +7,20 @@ import { ObjectRecordFilter } from 'src/engine/api/graphql/workspace-query-build
|
||||
|
||||
import { SearchArgs } from 'src/engine/core-modules/search/dtos/search-args';
|
||||
import { SearchRecordDTO } from 'src/engine/core-modules/search/dtos/search-record-dto';
|
||||
import {
|
||||
SearchException,
|
||||
SearchExceptionCode,
|
||||
} from 'src/engine/core-modules/search/exceptions/search.exception';
|
||||
import { SearchApiExceptionFilter } from 'src/engine/core-modules/search/filters/search-api-exception.filter';
|
||||
import { SearchService } from 'src/engine/core-modules/search/services/search.service';
|
||||
import { RecordsWithObjectMetadataItem } from 'src/engine/core-modules/search/types/records-with-object-metadata-item';
|
||||
import { formatSearchTerms } from 'src/engine/core-modules/search/utils/format-search-terms';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import {
|
||||
WorkspaceMetadataCacheException,
|
||||
WorkspaceMetadataCacheExceptionCode,
|
||||
} from 'src/engine/metadata-modules/workspace-metadata-cache/exceptions/workspace-metadata-cache.exception';
|
||||
import {
|
||||
WorkspaceMetadataVersionException,
|
||||
WorkspaceMetadataVersionExceptionCode,
|
||||
} from 'src/engine/metadata-modules/workspace-metadata-version/exceptions/workspace-metadata-version.exception';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
@@ -47,9 +51,9 @@ export class SearchResolver {
|
||||
await this.workspaceCacheStorageService.getMetadataVersion(workspace.id);
|
||||
|
||||
if (currentCacheVersion === undefined) {
|
||||
throw new SearchException(
|
||||
'Metadata cache version not found',
|
||||
SearchExceptionCode.METADATA_CACHE_VERSION_NOT_FOUND,
|
||||
throw new WorkspaceMetadataVersionException(
|
||||
`Metadata version not found for workspace ${workspace.id}`,
|
||||
WorkspaceMetadataVersionExceptionCode.METADATA_VERSION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -60,9 +64,9 @@ export class SearchResolver {
|
||||
);
|
||||
|
||||
if (!objectMetadataMaps) {
|
||||
throw new SearchException(
|
||||
throw new WorkspaceMetadataCacheException(
|
||||
`Object metadata map not found for workspace ${workspace.id} and metadata version ${currentCacheVersion}`,
|
||||
SearchExceptionCode.OBJECT_METADATA_MAP_NOT_FOUND,
|
||||
WorkspaceMetadataCacheExceptionCode.OBJECT_METADATA_MAP_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+7
-3
@@ -2,7 +2,6 @@ import { Catch, ExceptionFilter } from '@nestjs/common';
|
||||
|
||||
import {
|
||||
ForbiddenError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
@@ -25,8 +24,13 @@ export class ConfigVariableGraphqlApiExceptionFilter
|
||||
case ConfigVariableExceptionCode.VALIDATION_FAILED:
|
||||
throw new UserInputError(exception.message);
|
||||
case ConfigVariableExceptionCode.INTERNAL_ERROR:
|
||||
default:
|
||||
throw new InternalServerError(exception.message);
|
||||
case ConfigVariableExceptionCode.UNSUPPORTED_CONFIG_TYPE:
|
||||
throw exception;
|
||||
default: {
|
||||
const _exhaustiveCheck: never = exception.code;
|
||||
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,7 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class ConfigVariableException extends CustomException {
|
||||
declare code: ConfigVariableExceptionCode;
|
||||
constructor(message: string, code: ConfigVariableExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
@@ -12,6 +13,5 @@ export enum ConfigVariableExceptionCode {
|
||||
VARIABLE_NOT_FOUND = 'VARIABLE_NOT_FOUND',
|
||||
VALIDATION_FAILED = 'VALIDATION_FAILED',
|
||||
UNSUPPORTED_CONFIG_TYPE = 'UNSUPPORTED_CONFIG_TYPE',
|
||||
METADATA_NOT_FOUND = 'METADATA_NOT_FOUND',
|
||||
INTERNAL_ERROR = 'INTERNAL_ERROR',
|
||||
}
|
||||
|
||||
+7
-3
@@ -1,7 +1,6 @@
|
||||
import { Catch, ExceptionFilter } from '@nestjs/common';
|
||||
|
||||
import {
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
@@ -25,8 +24,13 @@ export class WorkflowTriggerGraphqlApiExceptionFilter
|
||||
throw new UserInputError(exception.message);
|
||||
case WorkflowTriggerExceptionCode.NOT_FOUND:
|
||||
throw new NotFoundError(exception.message);
|
||||
default:
|
||||
throw new InternalServerError(exception.message);
|
||||
case WorkflowTriggerExceptionCode.INTERNAL_ERROR:
|
||||
throw exception;
|
||||
default: {
|
||||
const _exhaustiveCheck: never = exception.code;
|
||||
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
ConflictError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { workspaceGraphqlApiExceptionHandler } from 'src/engine/core-modules/workspace/utils/workspace-graphql-api-exception-handler.util';
|
||||
@@ -8,6 +7,7 @@ import {
|
||||
WorkspaceException,
|
||||
WorkspaceExceptionCode,
|
||||
} from 'src/engine/core-modules/workspace/workspace.exception';
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
describe('workspaceGraphqlApiExceptionHandler', () => {
|
||||
it('should throw NotFoundError when WorkspaceExceptionCode is SUBDOMAIN_NOT_FOUND', () => {
|
||||
@@ -48,7 +48,7 @@ describe('workspaceGraphqlApiExceptionHandler', () => {
|
||||
const error = new WorkspaceException('Unknown error', 'UNKNOWN_CODE');
|
||||
|
||||
expect(() => workspaceGraphqlApiExceptionHandler(error)).toThrow(
|
||||
InternalServerError,
|
||||
CustomException,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
+7
-3
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
ConflictError,
|
||||
ForbiddenError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import {
|
||||
@@ -15,12 +14,17 @@ export const workspaceGraphqlApiExceptionHandler = (error: Error) => {
|
||||
case WorkspaceExceptionCode.SUBDOMAIN_NOT_FOUND:
|
||||
case WorkspaceExceptionCode.WORKSPACE_NOT_FOUND:
|
||||
throw new NotFoundError(error.message);
|
||||
case WorkspaceExceptionCode.DOMAIN_ALREADY_TAKEN:
|
||||
case WorkspaceExceptionCode.SUBDOMAIN_ALREADY_TAKEN:
|
||||
throw new ConflictError(error.message);
|
||||
case WorkspaceExceptionCode.ENVIRONMENT_VAR_NOT_ENABLED:
|
||||
case WorkspaceExceptionCode.WORKSPACE_CUSTOM_DOMAIN_DISABLED:
|
||||
throw new ForbiddenError(error.message);
|
||||
default:
|
||||
throw new InternalServerError(error.message);
|
||||
default: {
|
||||
const _exhaustiveCheck: never = error.code;
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class WorkspaceException extends CustomException {
|
||||
declare code: WorkspaceExceptionCode;
|
||||
constructor(message: string, code: WorkspaceExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class FieldMetadataException extends CustomException {
|
||||
declare code: FieldMetadataExceptionCode;
|
||||
constructor(message: string, code: FieldMetadataExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
|
||||
+3
-3
@@ -46,7 +46,7 @@ import {
|
||||
RelationMetadataEntity,
|
||||
RelationMetadataType,
|
||||
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception';
|
||||
import { InvalidMetadataException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception';
|
||||
import { exceedsDatabaseIdentifierMaximumLength } from 'src/engine/metadata-modules/utils/validate-database-identifier-length.utils';
|
||||
import { validateFieldNameAvailabilityOrThrow } from 'src/engine/metadata-modules/utils/validate-field-name-availability.utils';
|
||||
import { validateMetadataNameOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name.utils';
|
||||
@@ -614,7 +614,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
try {
|
||||
validateMetadataNameOrThrow(fieldMetadataInput.name);
|
||||
} catch (error) {
|
||||
if (error instanceof InvalidMetadataNameException) {
|
||||
if (error instanceof InvalidMetadataException) {
|
||||
throw new FieldMetadataException(
|
||||
error.message,
|
||||
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||
@@ -630,7 +630,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
objectMetadata,
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof InvalidMetadataNameException) {
|
||||
if (error instanceof InvalidMetadataException) {
|
||||
throw new FieldMetadataException(
|
||||
`Name "${fieldMetadataInput.name}" is not available, check that it is not duplicating another field's name.`,
|
||||
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||
|
||||
+12
-4
@@ -11,6 +11,14 @@ import {
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { removeFieldMapsFromObjectMetadata } from 'src/engine/metadata-modules/utils/remove-field-maps-from-object-metadata.util';
|
||||
import {
|
||||
WorkspaceMetadataCacheException,
|
||||
WorkspaceMetadataCacheExceptionCode,
|
||||
} from 'src/engine/metadata-modules/workspace-metadata-cache/exceptions/workspace-metadata-cache.exception';
|
||||
import {
|
||||
WorkspaceMetadataVersionException,
|
||||
WorkspaceMetadataVersionExceptionCode,
|
||||
} from 'src/engine/metadata-modules/workspace-metadata-version/exceptions/workspace-metadata-version.exception';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
@Injectable()
|
||||
@@ -43,9 +51,9 @@ export class FieldMetadataRelationService {
|
||||
await this.workspaceCacheStorageService.getMetadataVersion(workspaceId);
|
||||
|
||||
if (!isDefined(metadataVersion)) {
|
||||
throw new FieldMetadataException(
|
||||
throw new WorkspaceMetadataVersionException(
|
||||
`Metadata version not found for workspace ${workspaceId}`,
|
||||
FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
WorkspaceMetadataVersionExceptionCode.METADATA_VERSION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -56,9 +64,9 @@ export class FieldMetadataRelationService {
|
||||
);
|
||||
|
||||
if (!objectMetadataMaps) {
|
||||
throw new FieldMetadataException(
|
||||
throw new WorkspaceMetadataCacheException(
|
||||
`Object metadata map not found for workspace ${workspaceId} and metadata version ${metadataVersion}`,
|
||||
FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
WorkspaceMetadataCacheExceptionCode.OBJECT_METADATA_MAP_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
ConflictError,
|
||||
ForbiddenError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
@@ -31,8 +30,12 @@ export const fieldMetadataGraphqlApiExceptionHandler = (error: Error) => {
|
||||
case FieldMetadataExceptionCode.FIELD_METADATA_RELATION_NOT_ENABLED:
|
||||
case FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED:
|
||||
case FieldMetadataExceptionCode.LABEL_IDENTIFIER_FIELD_METADATA_ID_NOT_FOUND:
|
||||
default:
|
||||
throw new InternalServerError(error.message);
|
||||
throw error;
|
||||
default: {
|
||||
const _exhaustiveCheck: never = error.code;
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class ObjectMetadataException extends CustomException {
|
||||
declare code: ObjectMetadataExceptionCode;
|
||||
constructor(message: string, code: ObjectMetadataExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
|
||||
+7
-3
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
ConflictError,
|
||||
ForbiddenError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
@@ -26,8 +25,13 @@ export const objectMetadataGraphqlApiExceptionHandler = (error: Error) => {
|
||||
throw new ForbiddenError(error.message);
|
||||
case ObjectMetadataExceptionCode.OBJECT_ALREADY_EXISTS:
|
||||
throw new ConflictError(error.message);
|
||||
default:
|
||||
throw new InternalServerError(error.message);
|
||||
case ObjectMetadataExceptionCode.MISSING_CUSTOM_OBJECT_DEFAULT_LABEL_IDENTIFIER_FIELD:
|
||||
throw error;
|
||||
default: {
|
||||
const _exhaustiveCheck: never = error.code;
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@ import {
|
||||
ObjectMetadataException,
|
||||
ObjectMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception';
|
||||
import { InvalidMetadataException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception';
|
||||
import { validateMetadataNameIsNotTooLongOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name-is-not-too-long.utils';
|
||||
import { validateMetadataNameIsNotTooShortOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name-is-not-too-short.utils';
|
||||
import { validateMetadataNameOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name.utils';
|
||||
@@ -28,7 +28,7 @@ export const validateObjectMetadataInputNameOrThrow = (name: string): void => {
|
||||
try {
|
||||
validateMetadataNameOrThrow(name);
|
||||
} catch (error) {
|
||||
if (error instanceof InvalidMetadataNameException) {
|
||||
if (error instanceof InvalidMetadataException) {
|
||||
throw new ObjectMetadataException(
|
||||
error.message,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
@@ -58,7 +58,7 @@ const validateObjectMetadataInputLabelOrThrow = (name: string): void => {
|
||||
try {
|
||||
validators.forEach((validator) => validator(name.trim()));
|
||||
} catch (error) {
|
||||
if (error instanceof InvalidMetadataNameException) {
|
||||
if (error instanceof InvalidMetadataException) {
|
||||
throw new ObjectMetadataException(
|
||||
error.message,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class PermissionsException extends CustomException {
|
||||
declare code: PermissionsExceptionCode;
|
||||
constructor(message: string, code: PermissionsExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
|
||||
+15
-3
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
ForbiddenError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
@@ -29,7 +28,20 @@ export const permissionGraphqlApiExceptionHandler = (
|
||||
case PermissionsExceptionCode.OBJECT_METADATA_NOT_FOUND:
|
||||
throw new NotFoundError(error.message);
|
||||
case PermissionsExceptionCode.DEFAULT_ROLE_NOT_FOUND:
|
||||
default:
|
||||
throw new InternalServerError(error.message);
|
||||
case PermissionsExceptionCode.WORKSPACE_ID_ROLE_USER_WORKSPACE_MISMATCH:
|
||||
case PermissionsExceptionCode.TOO_MANY_ADMIN_CANDIDATES:
|
||||
case PermissionsExceptionCode.USER_WORKSPACE_ALREADY_HAS_ROLE:
|
||||
case PermissionsExceptionCode.ADMIN_ROLE_NOT_FOUND:
|
||||
case PermissionsExceptionCode.DEFAULT_ROLE_CANNOT_BE_DELETED:
|
||||
case PermissionsExceptionCode.WORKSPACE_MEMBER_NOT_FOUND:
|
||||
case PermissionsExceptionCode.UNKNOWN_OPERATION_NAME:
|
||||
case PermissionsExceptionCode.UNKNOWN_REQUIRED_PERMISSION:
|
||||
case PermissionsExceptionCode.NO_ROLE_FOUND_FOR_USER_WORKSPACE:
|
||||
throw error;
|
||||
default: {
|
||||
const _exhaustiveCheck: never = error.code;
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class RelationMetadataException extends CustomException {
|
||||
declare code: RelationMetadataExceptionCode;
|
||||
constructor(message: string, code: RelationMetadataExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
|
||||
+14
-4
@@ -22,9 +22,17 @@ import {
|
||||
RelationMetadataException,
|
||||
RelationMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.exception';
|
||||
import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception';
|
||||
import { InvalidMetadataException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception';
|
||||
import { validateFieldNameAvailabilityOrThrow } from 'src/engine/metadata-modules/utils/validate-field-name-availability.utils';
|
||||
import { validateMetadataNameOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name.utils';
|
||||
import {
|
||||
WorkspaceMetadataCacheException,
|
||||
WorkspaceMetadataCacheExceptionCode,
|
||||
} from 'src/engine/metadata-modules/workspace-metadata-cache/exceptions/workspace-metadata-cache.exception';
|
||||
import {
|
||||
WorkspaceMetadataVersionException,
|
||||
WorkspaceMetadataVersionExceptionCode,
|
||||
} from 'src/engine/metadata-modules/workspace-metadata-version/exceptions/workspace-metadata-version.exception';
|
||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
||||
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
|
||||
import {
|
||||
@@ -73,7 +81,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
validateMetadataNameOrThrow(relationMetadataInput.fromName);
|
||||
validateMetadataNameOrThrow(relationMetadataInput.toName);
|
||||
} catch (error) {
|
||||
if (error instanceof InvalidMetadataNameException)
|
||||
if (error instanceof InvalidMetadataException)
|
||||
throw new RelationMetadataException(
|
||||
error.message,
|
||||
RelationMetadataExceptionCode.INVALID_RELATION_INPUT,
|
||||
@@ -497,8 +505,9 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
await this.workspaceCacheStorageService.getMetadataVersion(workspaceId);
|
||||
|
||||
if (!isDefined(metadataVersion)) {
|
||||
throw new NotFoundException(
|
||||
throw new WorkspaceMetadataVersionException(
|
||||
`Metadata version not found for workspace ${workspaceId}`,
|
||||
WorkspaceMetadataVersionExceptionCode.METADATA_VERSION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -509,8 +518,9 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
);
|
||||
|
||||
if (!objectMetadataMaps) {
|
||||
throw new NotFoundException(
|
||||
throw new WorkspaceMetadataCacheException(
|
||||
`Object metadata map not found for workspace ${workspaceId} and metadata version ${metadataVersion}`,
|
||||
WorkspaceMetadataCacheExceptionCode.OBJECT_METADATA_MAP_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+7
-6
@@ -1,7 +1,5 @@
|
||||
import {
|
||||
ConflictError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import {
|
||||
@@ -17,15 +15,18 @@ export const relationMetadataGraphqlApiExceptionHandler = (error: Error) => {
|
||||
|
||||
if (error instanceof RelationMetadataException) {
|
||||
switch (error.code) {
|
||||
case RelationMetadataExceptionCode.RELATION_METADATA_NOT_FOUND:
|
||||
throw new NotFoundError(error.message);
|
||||
case RelationMetadataExceptionCode.INVALID_RELATION_INPUT:
|
||||
throw new UserInputError(error.message);
|
||||
case RelationMetadataExceptionCode.RELATION_ALREADY_EXISTS:
|
||||
throw new ConflictError(error.message);
|
||||
case RelationMetadataExceptionCode.FOREIGN_KEY_NOT_FOUND:
|
||||
default:
|
||||
throw new InternalServerError(error.message);
|
||||
case RelationMetadataExceptionCode.RELATION_METADATA_NOT_FOUND:
|
||||
throw error;
|
||||
default: {
|
||||
const _exhaustiveCheck: never = error.code;
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class RemoteServerException extends CustomException {
|
||||
declare code: RemoteServerExceptionCode;
|
||||
constructor(message: string, code: RemoteServerExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class RemoteTableException extends CustomException {
|
||||
declare code: RemoteTableExceptionCode;
|
||||
constructor(message: string, code: RemoteTableExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
|
||||
+5
-3
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
ConflictError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
@@ -21,8 +20,11 @@ export const remoteTableGraphqlApiExceptionHandler = (error: Error) => {
|
||||
throw new UserInputError(error.message);
|
||||
case RemoteTableExceptionCode.REMOTE_TABLE_ALREADY_EXISTS:
|
||||
throw new ConflictError(error.message);
|
||||
default:
|
||||
throw new InternalServerError(error.message);
|
||||
default: {
|
||||
const _exhaustiveCheck: never = error.code;
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-3
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
ConflictError,
|
||||
ForbiddenError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
@@ -21,8 +20,13 @@ export const remoteServerGraphqlApiExceptionHandler = (error: any) => {
|
||||
throw new ForbiddenError(error.message);
|
||||
case RemoteServerExceptionCode.REMOTE_SERVER_ALREADY_EXISTS:
|
||||
throw new ConflictError(error.message);
|
||||
default:
|
||||
throw new InternalServerError(error.message);
|
||||
case RemoteServerExceptionCode.REMOTE_SERVER_CONNECTION_ERROR:
|
||||
throw error;
|
||||
default: {
|
||||
const _exhaustiveCheck: never = error.code;
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class ServerlessFunctionException extends CustomException {
|
||||
declare code: ServerlessFunctionExceptionCode;
|
||||
constructor(message: string, code: ServerlessFunctionExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
|
||||
+8
-3
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
ConflictError,
|
||||
ForbiddenError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import {
|
||||
@@ -20,9 +19,15 @@ export const serverlessFunctionGraphQLApiExceptionHandler = (error: any) => {
|
||||
case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_NOT_READY:
|
||||
case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_BUILDING:
|
||||
case ServerlessFunctionExceptionCode.FEATURE_FLAG_INVALID:
|
||||
case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_EXECUTION_LIMIT_REACHED:
|
||||
throw new ForbiddenError(error.message);
|
||||
default:
|
||||
throw new InternalServerError(error.message);
|
||||
case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_CODE_UNCHANGED:
|
||||
throw error;
|
||||
default: {
|
||||
const _exhaustiveCheck: never = error.code;
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
|
||||
+12
-3
@@ -2,11 +2,17 @@ import camelCase from 'lodash.camelcase';
|
||||
import { slugify } from 'transliteration';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { InvalidMetadataException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception';
|
||||
import {
|
||||
InvalidMetadataException,
|
||||
InvalidMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception';
|
||||
|
||||
export const computeMetadataNameFromLabel = (label: string): string => {
|
||||
if (!isDefined(label)) {
|
||||
throw new InvalidMetadataException('Label is required');
|
||||
throw new InvalidMetadataException(
|
||||
'Label is required',
|
||||
InvalidMetadataExceptionCode.LABEL_REQUIRED,
|
||||
);
|
||||
}
|
||||
|
||||
const prefixedLabel = /^\d/.test(label) ? `n${label}` : label;
|
||||
@@ -22,7 +28,10 @@ export const computeMetadataNameFromLabel = (label: string): string => {
|
||||
});
|
||||
|
||||
if (formattedString === '') {
|
||||
throw new InvalidMetadataException(`Invalid label: "${label}"`);
|
||||
throw new InvalidMetadataException(
|
||||
`Invalid label: "${label}"`,
|
||||
InvalidMetadataExceptionCode.INVALID_LABEL,
|
||||
);
|
||||
}
|
||||
|
||||
return camelCase(formattedString);
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
export class InvalidMetadataNameException extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
+17
-3
@@ -1,5 +1,19 @@
|
||||
export class InvalidMetadataException extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class InvalidMetadataException extends CustomException {
|
||||
constructor(message: string, code: InvalidMetadataExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
}
|
||||
|
||||
export enum InvalidMetadataExceptionCode {
|
||||
LABEL_REQUIRED = 'Label required',
|
||||
INPUT_TOO_SHORT = 'Input too short',
|
||||
EXCEEDS_MAX_LENGTH = 'Exceeds max length',
|
||||
RESERVED_KEYWORD = 'Reserved keyword',
|
||||
NOT_CAMEL_CASE = 'Not camel case',
|
||||
INVALID_LABEL = 'Invalid label',
|
||||
NAME_NOT_SYNCED_WITH_LABEL = 'Name not synced with label',
|
||||
INVALID_STRING = 'Invalid string',
|
||||
NOT_AVAILABLE = 'Name not available',
|
||||
}
|
||||
|
||||
+12
-3
@@ -2,7 +2,10 @@ import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-meta
|
||||
import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception';
|
||||
import {
|
||||
InvalidMetadataException,
|
||||
InvalidMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception';
|
||||
|
||||
const getReservedCompositeFieldNames = (
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
@@ -33,10 +36,16 @@ export const validateFieldNameAvailabilityOrThrow = (
|
||||
getReservedCompositeFieldNames(objectMetadata);
|
||||
|
||||
if (objectMetadata.fields.some((field) => field.name === name)) {
|
||||
throw new InvalidMetadataNameException(`Name "${name}" is not available`);
|
||||
throw new InvalidMetadataException(
|
||||
`Name "${name}" is not available`,
|
||||
InvalidMetadataExceptionCode.NOT_AVAILABLE,
|
||||
);
|
||||
}
|
||||
|
||||
if (reservedCompositeFieldsNames.includes(name)) {
|
||||
throw new InvalidMetadataNameException(`Name "${name}" is not available`);
|
||||
throw new InvalidMetadataException(
|
||||
`Name "${name}" is not available`,
|
||||
InvalidMetadataExceptionCode.RESERVED_KEYWORD,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
+6
-2
@@ -1,11 +1,15 @@
|
||||
import camelCase from 'lodash.camelcase';
|
||||
|
||||
import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception';
|
||||
import {
|
||||
InvalidMetadataException,
|
||||
InvalidMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception';
|
||||
|
||||
export const validateMetadataNameIsCamelCaseOrThrow = (name: string) => {
|
||||
if (name !== camelCase(name)) {
|
||||
throw new InvalidMetadataNameException(
|
||||
throw new InvalidMetadataException(
|
||||
`Name should be in camelCase: ${name}`,
|
||||
InvalidMetadataExceptionCode.NOT_CAMEL_CASE,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
+6
-2
@@ -1,4 +1,7 @@
|
||||
import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception';
|
||||
import {
|
||||
InvalidMetadataException,
|
||||
InvalidMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception';
|
||||
|
||||
const coreObjectNames = [
|
||||
'approvedAccessDomain',
|
||||
@@ -64,8 +67,9 @@ export const validateMetadataNameIsNotReservedKeywordOrThrow = (
|
||||
name: string,
|
||||
) => {
|
||||
if (reservedKeywords.includes(name)) {
|
||||
throw new InvalidMetadataNameException(
|
||||
throw new InvalidMetadataException(
|
||||
`The name "${name}" is not available`,
|
||||
InvalidMetadataExceptionCode.RESERVED_KEYWORD,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
+6
-2
@@ -1,10 +1,14 @@
|
||||
import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception';
|
||||
import {
|
||||
InvalidMetadataException,
|
||||
InvalidMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception';
|
||||
import { exceedsDatabaseIdentifierMaximumLength } from 'src/engine/metadata-modules/utils/validate-database-identifier-length.utils';
|
||||
|
||||
export const validateMetadataNameIsNotTooLongOrThrow = (name: string) => {
|
||||
if (exceedsDatabaseIdentifierMaximumLength(name)) {
|
||||
throw new InvalidMetadataNameException(
|
||||
throw new InvalidMetadataException(
|
||||
`String "${name}" exceeds 63 characters limit`,
|
||||
InvalidMetadataExceptionCode.EXCEEDS_MAX_LENGTH,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
+8
-2
@@ -1,8 +1,14 @@
|
||||
import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception';
|
||||
import {
|
||||
InvalidMetadataException,
|
||||
InvalidMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception';
|
||||
import { beneathDatabaseIdentifierMinimumLength } from 'src/engine/metadata-modules/utils/validate-database-identifier-length.utils';
|
||||
|
||||
export const validateMetadataNameIsNotTooShortOrThrow = (name: string) => {
|
||||
if (beneathDatabaseIdentifierMinimumLength(name)) {
|
||||
throw new InvalidMetadataNameException(`Input is too short: "${name}"`);
|
||||
throw new InvalidMetadataException(
|
||||
`Input is too short: "${name}"`,
|
||||
InvalidMetadataExceptionCode.INPUT_TOO_SHORT,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
+6
-2
@@ -1,4 +1,7 @@
|
||||
import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception';
|
||||
import {
|
||||
InvalidMetadataException,
|
||||
InvalidMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception';
|
||||
|
||||
const STARTS_WITH_LOWER_CASE_AND_CONTAINS_ONLY_CAPS_AND_LOWER_LETTERS_AND_NUMBER_STRING_REGEX =
|
||||
/^[a-z][a-zA-Z0-9]*$/;
|
||||
@@ -10,8 +13,9 @@ export const validateMetadataNameStartWithLowercaseLetterAndContainDigitsNorLett
|
||||
STARTS_WITH_LOWER_CASE_AND_CONTAINS_ONLY_CAPS_AND_LOWER_LETTERS_AND_NUMBER_STRING_REGEX,
|
||||
)
|
||||
) {
|
||||
throw new InvalidMetadataNameException(
|
||||
throw new InvalidMetadataException(
|
||||
`String "${name}" is not valid: must start with lowercase letter and contain only alphanumeric letters`,
|
||||
InvalidMetadataExceptionCode.INVALID_STRING,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
+13
-3
@@ -2,7 +2,10 @@ import camelCase from 'lodash.camelcase';
|
||||
import { slugify } from 'transliteration';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { InvalidMetadataException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception';
|
||||
import {
|
||||
InvalidMetadataException,
|
||||
InvalidMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception';
|
||||
|
||||
export const validateNameAndLabelAreSyncOrThrow = (
|
||||
label: string,
|
||||
@@ -13,13 +16,17 @@ export const validateNameAndLabelAreSyncOrThrow = (
|
||||
if (name !== computedName) {
|
||||
throw new InvalidMetadataException(
|
||||
`Name is not synced with label. Expected name: "${computedName}", got ${name}`,
|
||||
InvalidMetadataExceptionCode.NAME_NOT_SYNCED_WITH_LABEL,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const computeMetadataNameFromLabel = (label: string): string => {
|
||||
if (!isDefined(label)) {
|
||||
throw new InvalidMetadataException('Label is required');
|
||||
throw new InvalidMetadataException(
|
||||
'Label is required',
|
||||
InvalidMetadataExceptionCode.LABEL_REQUIRED,
|
||||
);
|
||||
}
|
||||
|
||||
const prefixedLabel = /^\d/.test(label) ? `n${label}` : label;
|
||||
@@ -35,7 +42,10 @@ export const computeMetadataNameFromLabel = (label: string): string => {
|
||||
});
|
||||
|
||||
if (formattedString === '') {
|
||||
throw new InvalidMetadataException(`Invalid label: "${label}"`);
|
||||
throw new InvalidMetadataException(
|
||||
`Invalid label: "${label}"`,
|
||||
InvalidMetadataExceptionCode.INVALID_LABEL,
|
||||
);
|
||||
}
|
||||
|
||||
return camelCase(formattedString);
|
||||
|
||||
+1
-1
@@ -7,5 +7,5 @@ export class WorkspaceMetadataCacheException extends CustomException {
|
||||
}
|
||||
|
||||
export enum WorkspaceMetadataCacheExceptionCode {
|
||||
METADATA_VERSION_NOT_FOUND = 'METADATA_VERSION_NOT_FOUND',
|
||||
OBJECT_METADATA_MAP_NOT_FOUND = 'Object Metadata map not found',
|
||||
}
|
||||
|
||||
+6
-6
@@ -1,17 +1,17 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { generateObjectMetadataMaps } from 'src/engine/metadata-modules/utils/generate-object-metadata-maps.util';
|
||||
import {
|
||||
WorkspaceMetadataCacheException,
|
||||
WorkspaceMetadataCacheExceptionCode,
|
||||
} from 'src/engine/metadata-modules/workspace-metadata-cache/exceptions/workspace-metadata-cache.exception';
|
||||
WorkspaceMetadataVersionException,
|
||||
WorkspaceMetadataVersionExceptionCode,
|
||||
} from 'src/engine/metadata-modules/workspace-metadata-version/exceptions/workspace-metadata-version.exception';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
@Injectable()
|
||||
@@ -46,9 +46,9 @@ export class WorkspaceMetadataCacheService {
|
||||
await this.getMetadataVersionFromDatabase(workspaceId);
|
||||
|
||||
if (!isDefined(currentDatabaseVersion)) {
|
||||
throw new WorkspaceMetadataCacheException(
|
||||
throw new WorkspaceMetadataVersionException(
|
||||
'Metadata version not found in the database',
|
||||
WorkspaceMetadataCacheExceptionCode.METADATA_VERSION_NOT_FOUND,
|
||||
WorkspaceMetadataVersionExceptionCode.METADATA_VERSION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+7
-3
@@ -11,6 +11,10 @@ import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twent
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { WorkspaceFeatureFlagsMapCacheService } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.service';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
import {
|
||||
WorkspaceMetadataVersionException,
|
||||
WorkspaceMetadataVersionExceptionCode,
|
||||
} from 'src/engine/metadata-modules/workspace-metadata-version/exceptions/workspace-metadata-version.exception';
|
||||
import { WorkspacePermissionsCacheStorageService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache-storage.service';
|
||||
import {
|
||||
ROLES_PERMISSIONS,
|
||||
@@ -312,9 +316,9 @@ export class WorkspaceDatasourceFactory {
|
||||
|
||||
if (!isDefined(latestWorkspaceMetadataVersion)) {
|
||||
if (shouldFailIfMetadataNotFound) {
|
||||
throw new TwentyORMException(
|
||||
throw new WorkspaceMetadataVersionException(
|
||||
`Metadata version not found for workspace ${workspaceId}`,
|
||||
TwentyORMExceptionCode.METADATA_VERSION_NOT_FOUND,
|
||||
WorkspaceMetadataVersionExceptionCode.METADATA_VERSION_NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
await this.workspaceMetadataCacheService.recomputeMetadataCache({
|
||||
@@ -330,7 +334,7 @@ export class WorkspaceDatasourceFactory {
|
||||
|
||||
if (!isDefined(latestWorkspaceMetadataVersion)) {
|
||||
throw new TwentyORMException(
|
||||
`Metadata version not found after recompute for workspace ${workspaceId}`,
|
||||
`Metadata version not found after recompute`,
|
||||
TwentyORMExceptionCode.METADATA_VERSION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
+12
-7
@@ -148,16 +148,21 @@ export class CalendarEventImportErrorHandlerService {
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
this.exceptionHandlerService.captureExceptions([exception], {
|
||||
workspace: {
|
||||
id: workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
throw new CalendarEventImportException(
|
||||
const calendarEventImportException = new CalendarEventImportException(
|
||||
`Unknown error importing calendar events for calendar channel ${calendarChannel.id} in workspace ${workspaceId}: ${exception.message}`,
|
||||
CalendarEventImportExceptionCode.UNKNOWN,
|
||||
);
|
||||
|
||||
this.exceptionHandlerService.captureExceptions(
|
||||
[calendarEventImportException],
|
||||
{
|
||||
workspace: {
|
||||
id: workspaceId,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
throw calendarEventImportException;
|
||||
}
|
||||
|
||||
private async handleNotFoundException(
|
||||
|
||||
+8
-4
@@ -1,9 +1,11 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkspacePostQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
|
||||
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { ForbiddenError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { ApplyCalendarEventsVisibilityRestrictionsService } from 'src/modules/calendar/common/query-hooks/calendar-event/services/apply-calendar-events-visibility-restrictions.service';
|
||||
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
|
||||
|
||||
@@ -23,13 +25,15 @@ export class CalendarEventFindManyPostQueryHook
|
||||
_objectName: string,
|
||||
payload: CalendarEventWorkspaceEntity[],
|
||||
): Promise<void> {
|
||||
if (!authContext.workspaceMemberId) {
|
||||
throw new UserInputError('Workspace member id is required');
|
||||
const { user, apiKey } = authContext;
|
||||
|
||||
if (!isDefined(user) && !isDefined(apiKey)) {
|
||||
throw new ForbiddenError('User is required');
|
||||
}
|
||||
|
||||
await this.applyCalendarEventsVisibilityRestrictionsService.applyCalendarEventsVisibilityRestrictions(
|
||||
authContext.workspaceMemberId,
|
||||
payload,
|
||||
user?.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -1,9 +1,11 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkspacePostQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
|
||||
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { ForbiddenError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { ApplyCalendarEventsVisibilityRestrictionsService } from 'src/modules/calendar/common/query-hooks/calendar-event/services/apply-calendar-events-visibility-restrictions.service';
|
||||
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
|
||||
|
||||
@@ -23,13 +25,15 @@ export class CalendarEventFindOnePostQueryHook
|
||||
_objectName: string,
|
||||
payload: CalendarEventWorkspaceEntity[],
|
||||
): Promise<void> {
|
||||
if (!authContext.workspaceMemberId) {
|
||||
throw new UserInputError('Workspace member id is required');
|
||||
const { user, apiKey } = authContext;
|
||||
|
||||
if (!isDefined(user) && !isDefined(apiKey)) {
|
||||
throw new ForbiddenError('User is required');
|
||||
}
|
||||
|
||||
await this.applyCalendarEventsVisibilityRestrictionsService.applyCalendarEventsVisibilityRestrictions(
|
||||
authContext.workspaceMemberId,
|
||||
payload,
|
||||
user?.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+87
-5
@@ -48,6 +48,10 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
find: jest.fn(),
|
||||
};
|
||||
|
||||
const mockWorkspaceMemberRepository = {
|
||||
findOneByOrFail: jest.fn(),
|
||||
};
|
||||
|
||||
const mockTwentyORMManager = {
|
||||
getRepository: jest.fn().mockImplementation((name) => {
|
||||
if (name === 'calendarChannelEventAssociation') {
|
||||
@@ -56,6 +60,9 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
if (name === 'connectedAccount') {
|
||||
return mockConnectedAccountRepository;
|
||||
}
|
||||
if (name === 'workspaceMember') {
|
||||
return mockWorkspaceMemberRepository;
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -83,6 +90,10 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
createMockCalendarEvent('1', 'Test Event', 'Test Description'),
|
||||
];
|
||||
|
||||
mockWorkspaceMemberRepository.findOneByOrFail.mockResolvedValue({
|
||||
id: 'workspace-member-id',
|
||||
});
|
||||
|
||||
mockCalendarEventAssociationRepository.find.mockResolvedValue([
|
||||
{
|
||||
calendarEventId: '1',
|
||||
@@ -94,8 +105,8 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
]);
|
||||
|
||||
const result = await service.applyCalendarEventsVisibilityRestrictions(
|
||||
'workspace-member-id',
|
||||
calendarEvents,
|
||||
'user-id',
|
||||
);
|
||||
|
||||
expect(result).toEqual(calendarEvents);
|
||||
@@ -124,11 +135,15 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
},
|
||||
]);
|
||||
|
||||
mockWorkspaceMemberRepository.findOneByOrFail.mockResolvedValue({
|
||||
id: 'workspace-member-id',
|
||||
});
|
||||
|
||||
mockConnectedAccountRepository.find.mockResolvedValue([]);
|
||||
|
||||
const result = await service.applyCalendarEventsVisibilityRestrictions(
|
||||
'workspace-member-id',
|
||||
calendarEvents,
|
||||
'user-id',
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
@@ -155,11 +170,15 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
},
|
||||
]);
|
||||
|
||||
mockWorkspaceMemberRepository.findOneByOrFail.mockResolvedValue({
|
||||
id: 'workspace-member-account-owner-id',
|
||||
});
|
||||
|
||||
mockConnectedAccountRepository.find.mockResolvedValue([{ id: '1' }]);
|
||||
|
||||
const result = await service.applyCalendarEventsVisibilityRestrictions(
|
||||
'workspace-member-id',
|
||||
calendarEvents,
|
||||
'user-id',
|
||||
);
|
||||
|
||||
expect(result).toEqual(calendarEvents);
|
||||
@@ -186,11 +205,15 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
},
|
||||
]);
|
||||
|
||||
mockWorkspaceMemberRepository.findOneByOrFail.mockResolvedValue({
|
||||
id: 'workspace-member-not-account-owner-id',
|
||||
});
|
||||
|
||||
mockConnectedAccountRepository.find.mockResolvedValue([]);
|
||||
|
||||
const result = await service.applyCalendarEventsVisibilityRestrictions(
|
||||
'workspace-member-id',
|
||||
calendarEvents,
|
||||
'user-id',
|
||||
);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
@@ -203,6 +226,10 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
createMockCalendarEvent('3', 'Event 3', 'Description 3'),
|
||||
];
|
||||
|
||||
mockWorkspaceMemberRepository.findOneByOrFail.mockResolvedValue({
|
||||
id: 'workspace-member-id',
|
||||
});
|
||||
|
||||
mockCalendarEventAssociationRepository.find.mockResolvedValue([
|
||||
{
|
||||
calendarEventId: '1',
|
||||
@@ -232,8 +259,63 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
.mockResolvedValueOnce([{ id: '1' }]); // request for calendar event 2
|
||||
|
||||
const result = await service.applyCalendarEventsVisibilityRestrictions(
|
||||
'workspace-member-id',
|
||||
calendarEvents,
|
||||
'user-id',
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
calendarEvents[0],
|
||||
calendarEvents[1],
|
||||
{
|
||||
...calendarEvents[2],
|
||||
title: FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED,
|
||||
description: FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return all calendar events with the right visibility when userId is undefined (api key request)', async () => {
|
||||
const calendarEvents = [
|
||||
createMockCalendarEvent('1', 'Event 1', 'Description 1'),
|
||||
createMockCalendarEvent('2', 'Event 2', 'Description 2'),
|
||||
createMockCalendarEvent('3', 'Event 3', 'Description 3'),
|
||||
];
|
||||
|
||||
mockWorkspaceMemberRepository.findOneByOrFail.mockResolvedValue({
|
||||
id: 'workspace-member-id',
|
||||
});
|
||||
|
||||
mockCalendarEventAssociationRepository.find.mockResolvedValue([
|
||||
{
|
||||
calendarEventId: '1',
|
||||
calendarChannel: {
|
||||
id: '1',
|
||||
visibility: CalendarChannelVisibility.SHARE_EVERYTHING,
|
||||
},
|
||||
},
|
||||
{
|
||||
calendarEventId: '2',
|
||||
calendarChannel: {
|
||||
id: '2',
|
||||
visibility: CalendarChannelVisibility.METADATA,
|
||||
},
|
||||
},
|
||||
{
|
||||
calendarEventId: '3',
|
||||
calendarChannel: {
|
||||
id: '3',
|
||||
visibility: CalendarChannelVisibility.METADATA,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
mockConnectedAccountRepository.find
|
||||
.mockResolvedValueOnce([]) // request for calendar event 3
|
||||
.mockResolvedValueOnce([{ id: '1' }]); // request for calendar event 2
|
||||
|
||||
const result = await service.applyCalendarEventsVisibilityRestrictions(
|
||||
calendarEvents,
|
||||
undefined,
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
|
||||
+26
-11
@@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import groupBy from 'lodash.groupby';
|
||||
import { FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED } from 'twenty-shared/constants';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { In } from 'typeorm';
|
||||
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
@@ -9,14 +10,15 @@ import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/cale
|
||||
import { CalendarChannelVisibility } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class ApplyCalendarEventsVisibilityRestrictionsService {
|
||||
constructor(private readonly twentyORMManager: TwentyORMManager) {}
|
||||
|
||||
public async applyCalendarEventsVisibilityRestrictions(
|
||||
workspaceMemberId: string,
|
||||
calendarEvents: CalendarEventWorkspaceEntity[],
|
||||
userId?: string, // undefined when request is made with api key
|
||||
) {
|
||||
const calendarChannelEventAssociationRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarChannelEventAssociationWorkspaceEntity>(
|
||||
@@ -36,6 +38,11 @@ export class ApplyCalendarEventsVisibilityRestrictionsService {
|
||||
'connectedAccount',
|
||||
);
|
||||
|
||||
const workspaceMemberRepository =
|
||||
await this.twentyORMManager.getRepository<WorkspaceMemberWorkspaceEntity>(
|
||||
'workspaceMember',
|
||||
);
|
||||
|
||||
for (let i = calendarEvents.length - 1; i >= 0; i--) {
|
||||
const calendarChannelCalendarEventAssociations =
|
||||
calendarChannelCalendarEventsAssociations.filter(
|
||||
@@ -59,18 +66,26 @@ export class ApplyCalendarEventsVisibilityRestrictionsService {
|
||||
continue;
|
||||
}
|
||||
|
||||
const connectedAccounts = await connectedAccountRepository.find({
|
||||
select: ['id'],
|
||||
where: {
|
||||
calendarChannels: {
|
||||
id: In(calendarChannels.map((channel) => channel.id)),
|
||||
if (isDefined(userId)) {
|
||||
const workspaceMember = await workspaceMemberRepository.findOneByOrFail(
|
||||
{
|
||||
userId: userId,
|
||||
},
|
||||
accountOwnerId: workspaceMemberId,
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
if (connectedAccounts.length > 0) {
|
||||
continue;
|
||||
const connectedAccounts = await connectedAccountRepository.find({
|
||||
select: ['id'],
|
||||
where: {
|
||||
calendarChannels: {
|
||||
id: In(calendarChannels.map((channel) => channel.id)),
|
||||
},
|
||||
accountOwnerId: workspaceMember.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (connectedAccounts.length > 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
|
||||
+91
-6
@@ -38,6 +38,10 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
find: jest.fn(),
|
||||
};
|
||||
|
||||
const mockWorkspaceMemberRepository = {
|
||||
findOneByOrFail: jest.fn(),
|
||||
};
|
||||
|
||||
const mockTwentyORMManager = {
|
||||
getRepository: jest.fn().mockImplementation((name) => {
|
||||
if (name === 'messageChannelMessageAssociation') {
|
||||
@@ -46,6 +50,9 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
if (name === 'connectedAccount') {
|
||||
return mockConnectedAccountRepository;
|
||||
}
|
||||
if (name === 'workspaceMember') {
|
||||
return mockWorkspaceMemberRepository;
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -83,8 +90,8 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
]);
|
||||
|
||||
const result = await service.applyMessagesVisibilityRestrictions(
|
||||
'workspace-member-id',
|
||||
messages,
|
||||
'user-id',
|
||||
);
|
||||
|
||||
expect(result).toEqual(messages);
|
||||
@@ -114,9 +121,13 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
|
||||
mockConnectedAccountRepository.find.mockResolvedValue([]);
|
||||
|
||||
mockWorkspaceMemberRepository.findOneByOrFail.mockResolvedValue({
|
||||
id: 'workspace-member-id',
|
||||
});
|
||||
|
||||
const result = await service.applyMessagesVisibilityRestrictions(
|
||||
'workspace-member-id',
|
||||
messages,
|
||||
'user-id',
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
@@ -144,9 +155,13 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
|
||||
mockConnectedAccountRepository.find.mockResolvedValue([]);
|
||||
|
||||
mockWorkspaceMemberRepository.findOneByOrFail.mockResolvedValue({
|
||||
id: 'workspace-member-id',
|
||||
});
|
||||
|
||||
const result = await service.applyMessagesVisibilityRestrictions(
|
||||
'workspace-member-id',
|
||||
messages,
|
||||
'user-id',
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
@@ -173,11 +188,15 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
},
|
||||
]);
|
||||
|
||||
mockWorkspaceMemberRepository.findOneByOrFail.mockResolvedValue({
|
||||
id: 'workspace-member-account-owner-id',
|
||||
});
|
||||
|
||||
mockConnectedAccountRepository.find.mockResolvedValue([{ id: '1' }]);
|
||||
|
||||
const result = await service.applyMessagesVisibilityRestrictions(
|
||||
'workspace-member-id',
|
||||
messages,
|
||||
'user-id',
|
||||
);
|
||||
|
||||
expect(result).toEqual(messages);
|
||||
@@ -203,11 +222,15 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
},
|
||||
]);
|
||||
|
||||
mockWorkspaceMemberRepository.findOneByOrFail.mockResolvedValue({
|
||||
id: 'workspace-member-not-account-owner-id',
|
||||
});
|
||||
|
||||
mockConnectedAccountRepository.find.mockResolvedValue([]);
|
||||
|
||||
const result = await service.applyMessagesVisibilityRestrictions(
|
||||
'workspace-member-id',
|
||||
messages,
|
||||
'user-id',
|
||||
);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
@@ -244,13 +267,75 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
},
|
||||
]);
|
||||
|
||||
mockWorkspaceMemberRepository.findOneByOrFail.mockResolvedValue({
|
||||
id: 'workspace-member-id',
|
||||
});
|
||||
|
||||
mockConnectedAccountRepository.find
|
||||
.mockResolvedValueOnce([]) // request for message 3
|
||||
.mockResolvedValueOnce([]); // request for message 2
|
||||
|
||||
const result = await service.applyMessagesVisibilityRestrictions(
|
||||
'workspace-member-id',
|
||||
messages,
|
||||
'user-id',
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
messages[0],
|
||||
{
|
||||
...messages[1],
|
||||
text: FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED,
|
||||
},
|
||||
{
|
||||
...messages[2],
|
||||
subject: FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED,
|
||||
text: FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return all messages with the right visibility when userId is undefined (api key request)', async () => {
|
||||
const messages = [
|
||||
createMockMessage('1', 'Subject 1', 'Message 1'),
|
||||
createMockMessage('2', 'Subject 2', 'Message 2'),
|
||||
createMockMessage('3', 'Subject 3', 'Message 3'),
|
||||
];
|
||||
|
||||
mockMessageChannelMessageAssociationRepository.find.mockResolvedValue([
|
||||
{
|
||||
messageId: '1',
|
||||
messageChannel: {
|
||||
id: '1',
|
||||
visibility: MessageChannelVisibility.SHARE_EVERYTHING,
|
||||
},
|
||||
},
|
||||
{
|
||||
messageId: '2',
|
||||
messageChannel: {
|
||||
id: '2',
|
||||
visibility: MessageChannelVisibility.SUBJECT,
|
||||
},
|
||||
},
|
||||
{
|
||||
messageId: '3',
|
||||
messageChannel: {
|
||||
id: '3',
|
||||
visibility: MessageChannelVisibility.METADATA,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
mockWorkspaceMemberRepository.findOneByOrFail.mockResolvedValue({
|
||||
id: 'workspace-member-id',
|
||||
});
|
||||
|
||||
mockConnectedAccountRepository.find
|
||||
.mockResolvedValueOnce([]) // request for message 3
|
||||
.mockResolvedValueOnce([]); // request for message 2
|
||||
|
||||
const result = await service.applyMessagesVisibilityRestrictions(
|
||||
messages,
|
||||
undefined,
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
|
||||
+27
-11
@@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import groupBy from 'lodash.groupby';
|
||||
import { FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED } from 'twenty-shared/constants';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { In } from 'typeorm';
|
||||
|
||||
import { NotFoundError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
@@ -10,14 +11,15 @@ import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/s
|
||||
import { MessageChannelMessageAssociationWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity';
|
||||
import { MessageChannelVisibility } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
|
||||
import { MessageWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message.workspace-entity';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class ApplyMessagesVisibilityRestrictionsService {
|
||||
constructor(private readonly twentyORMManager: TwentyORMManager) {}
|
||||
|
||||
public async applyMessagesVisibilityRestrictions(
|
||||
workspaceMemberId: string,
|
||||
messages: MessageWorkspaceEntity[],
|
||||
userId?: string, // undefined when request is made with api key
|
||||
) {
|
||||
const messageChannelMessageAssociationRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelMessageAssociationWorkspaceEntity>(
|
||||
@@ -37,6 +39,11 @@ export class ApplyMessagesVisibilityRestrictionsService {
|
||||
'connectedAccount',
|
||||
);
|
||||
|
||||
const workspaceMemberRepository =
|
||||
await this.twentyORMManager.getRepository<WorkspaceMemberWorkspaceEntity>(
|
||||
'workspaceMember',
|
||||
);
|
||||
|
||||
for (let i = messages.length - 1; i >= 0; i--) {
|
||||
const messageChannelMessageAssociations =
|
||||
messageChannelMessagesAssociations.filter(
|
||||
@@ -66,19 +73,28 @@ export class ApplyMessagesVisibilityRestrictionsService {
|
||||
continue;
|
||||
}
|
||||
|
||||
const connectedAccounts = await connectedAccountRepository.find({
|
||||
select: ['id'],
|
||||
where: {
|
||||
messageChannels: {
|
||||
id: In(messageChannels.map((channel) => channel.id)),
|
||||
if (isDefined(userId)) {
|
||||
const workspaceMember = await workspaceMemberRepository.findOneByOrFail(
|
||||
{
|
||||
userId,
|
||||
},
|
||||
accountOwnerId: workspaceMemberId,
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
if (connectedAccounts.length > 0) {
|
||||
continue;
|
||||
const connectedAccounts = await connectedAccountRepository.find({
|
||||
select: ['id'],
|
||||
where: {
|
||||
messageChannels: {
|
||||
id: In(messageChannels.map((channel) => channel.id)),
|
||||
},
|
||||
accountOwnerId: workspaceMember.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (connectedAccounts.length > 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (messageChannelsGroupByVisibility[MessageChannelVisibility.SUBJECT]) {
|
||||
messages[i].text = FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED;
|
||||
continue;
|
||||
|
||||
+8
-4
@@ -1,9 +1,11 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkspacePostQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
|
||||
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { ForbiddenError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { ApplyMessagesVisibilityRestrictionsService } from 'src/modules/messaging/common/query-hooks/message/apply-messages-visibility-restrictions.service';
|
||||
import { MessageWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message.workspace-entity';
|
||||
|
||||
@@ -23,13 +25,15 @@ export class MessageFindManyPostQueryHook
|
||||
_objectName: string,
|
||||
payload: MessageWorkspaceEntity[],
|
||||
): Promise<void> {
|
||||
if (!authContext.workspaceMemberId) {
|
||||
throw new UserInputError('Workspace member id is required');
|
||||
const { user, apiKey } = authContext;
|
||||
|
||||
if (!isDefined(user) && !isDefined(apiKey)) {
|
||||
throw new ForbiddenError('User is required');
|
||||
}
|
||||
|
||||
await this.applyMessagesVisibilityRestrictionsService.applyMessagesVisibilityRestrictions(
|
||||
authContext.workspaceMemberId,
|
||||
payload,
|
||||
user?.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -1,9 +1,11 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkspacePostQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
|
||||
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { ForbiddenError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { ApplyMessagesVisibilityRestrictionsService } from 'src/modules/messaging/common/query-hooks/message/apply-messages-visibility-restrictions.service';
|
||||
import { MessageWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message.workspace-entity';
|
||||
|
||||
@@ -23,13 +25,15 @@ export class MessageFindOnePostQueryHook
|
||||
_objectName: string,
|
||||
payload: MessageWorkspaceEntity[],
|
||||
): Promise<void> {
|
||||
if (!authContext.workspaceMemberId) {
|
||||
throw new UserInputError('Workspace member id is required');
|
||||
const { user, apiKey } = authContext;
|
||||
|
||||
if (!isDefined(user) && !isDefined(apiKey)) {
|
||||
throw new ForbiddenError('User is required');
|
||||
}
|
||||
|
||||
await this.applyMessagesVisibilityRestrictionsService.applyMessagesVisibilityRestrictions(
|
||||
authContext.workspaceMemberId,
|
||||
payload,
|
||||
user?.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -162,7 +162,10 @@ export class MessageImportExceptionHandlerService {
|
||||
);
|
||||
|
||||
this.exceptionHandlerService.captureExceptions([
|
||||
`Unknown error importing messages for message channel ${messageChannel.id} in workspace ${workspaceId}: ${exception.message}`,
|
||||
new MessageImportException(
|
||||
`Unknown error importing messages for message channel ${messageChannel.id} in workspace ${workspaceId}: ${exception.message}`,
|
||||
MessageImportExceptionCode.UNKNOWN,
|
||||
),
|
||||
]);
|
||||
|
||||
throw new MessageImportException(
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class WorkflowTriggerException extends CustomException {
|
||||
declare code: WorkflowTriggerExceptionCode;
|
||||
constructor(message: string, code: WorkflowTriggerExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user