fix: FieldMetadata default value and options better validation (#2785)
* fix: wip better field metadata validation * fix: remove files * fix: default value and options validation * fix: small fix * fix: try to limit patch * fix: tests * Update server/src/metadata/field-metadata/validators/is-field-metadata-options.validator.ts Co-authored-by: Weiko <corentin@twenty.com> * fix: lint * fix: standard fields update security --------- Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
@ -1,17 +1,54 @@
|
||||
import { Catch, UnauthorizedException } from '@nestjs/common';
|
||||
import { GqlExceptionFilter } from '@nestjs/graphql';
|
||||
import { ArgumentsHost, Catch, HttpException } from '@nestjs/common';
|
||||
import { GqlContextType, GqlExceptionFilter } from '@nestjs/graphql';
|
||||
|
||||
import { GraphQLError } from 'graphql';
|
||||
import { TypeORMError } from 'typeorm';
|
||||
|
||||
import {
|
||||
AuthenticationError,
|
||||
BaseGraphQLError,
|
||||
ForbiddenError,
|
||||
} from 'src/filters/utils/graphql-errors.util';
|
||||
|
||||
const graphQLPredefinedExceptions = {
|
||||
401: AuthenticationError,
|
||||
403: ForbiddenError,
|
||||
};
|
||||
|
||||
@Catch()
|
||||
export class ExceptionFilter implements GqlExceptionFilter {
|
||||
catch(exception: Error) {
|
||||
if (exception instanceof UnauthorizedException) {
|
||||
throw new GraphQLError('Unauthorized', {
|
||||
extensions: {
|
||||
code: 'UNAUTHENTICATED',
|
||||
},
|
||||
});
|
||||
catch(exception: HttpException | TypeORMError, host: ArgumentsHost) {
|
||||
if (host.getType<GqlContextType>() !== 'graphql') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (exception instanceof TypeORMError) {
|
||||
const error = new BaseGraphQLError(
|
||||
exception.name,
|
||||
'INTERNAL_SERVER_ERROR',
|
||||
);
|
||||
|
||||
error.stack = exception.stack;
|
||||
error.extensions['response'] = exception.message;
|
||||
|
||||
return error;
|
||||
} else if (exception instanceof HttpException) {
|
||||
let error: BaseGraphQLError;
|
||||
|
||||
if (exception.getStatus() in graphQLPredefinedExceptions) {
|
||||
error = new graphQLPredefinedExceptions[exception.getStatus()](
|
||||
exception.message,
|
||||
);
|
||||
} else {
|
||||
error = new BaseGraphQLError(
|
||||
exception.message,
|
||||
exception.getStatus().toString(),
|
||||
);
|
||||
}
|
||||
|
||||
error.stack = exception.stack;
|
||||
error.extensions['response'] = exception.getResponse();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
return exception;
|
||||
|
||||
135
server/src/filters/utils/graphql-errors.util.ts
Normal file
135
server/src/filters/utils/graphql-errors.util.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import {
|
||||
ASTNode,
|
||||
GraphQLError,
|
||||
GraphQLFormattedError,
|
||||
Source,
|
||||
SourceLocation,
|
||||
} from 'graphql';
|
||||
|
||||
declare module 'graphql' {
|
||||
export interface GraphQLErrorExtensions {
|
||||
exception?: {
|
||||
code?: string;
|
||||
stacktrace?: ReadonlyArray<string>;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class BaseGraphQLError extends Error implements GraphQLError {
|
||||
public extensions: Record<string, any>;
|
||||
override readonly name!: string;
|
||||
readonly locations: ReadonlyArray<SourceLocation> | undefined;
|
||||
readonly path: ReadonlyArray<string | number> | undefined;
|
||||
readonly source: Source | undefined;
|
||||
readonly positions: ReadonlyArray<number> | undefined;
|
||||
readonly nodes: ReadonlyArray<ASTNode> | undefined;
|
||||
public originalError: Error | undefined;
|
||||
|
||||
[key: string]: any;
|
||||
|
||||
constructor(
|
||||
message: string,
|
||||
code?: string,
|
||||
extensions?: Record<string, any>,
|
||||
) {
|
||||
super(message);
|
||||
|
||||
// if no name provided, use the default. defineProperty ensures that it stays non-enumerable
|
||||
if (!this.name) {
|
||||
Object.defineProperty(this, 'name', { value: 'ApolloError' });
|
||||
}
|
||||
|
||||
if (extensions?.extensions) {
|
||||
throw Error(
|
||||
'Pass extensions directly as the third argument of the ApolloError constructor: `new ' +
|
||||
'ApolloError(message, code, {myExt: value})`, not `new ApolloError(message, code, ' +
|
||||
'{extensions: {myExt: value}})`',
|
||||
);
|
||||
}
|
||||
|
||||
this.extensions = { ...extensions, code };
|
||||
}
|
||||
|
||||
toJSON(): GraphQLFormattedError {
|
||||
return toGraphQLError(this).toJSON();
|
||||
}
|
||||
|
||||
override toString(): string {
|
||||
return toGraphQLError(this).toString();
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag](): string {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
function toGraphQLError(error: BaseGraphQLError): GraphQLError {
|
||||
return new GraphQLError(error.message, {
|
||||
nodes: error.nodes,
|
||||
source: error.source,
|
||||
positions: error.positions,
|
||||
path: error.path,
|
||||
originalError: error.originalError,
|
||||
extensions: error.extensions,
|
||||
});
|
||||
}
|
||||
|
||||
export class SyntaxError extends BaseGraphQLError {
|
||||
constructor(message: string) {
|
||||
super(message, 'GRAPHQL_PARSE_FAILED');
|
||||
|
||||
Object.defineProperty(this, 'name', { value: 'SyntaxError' });
|
||||
}
|
||||
}
|
||||
|
||||
export class ValidationError extends BaseGraphQLError {
|
||||
constructor(message: string) {
|
||||
super(message, 'GRAPHQL_VALIDATION_FAILED');
|
||||
|
||||
Object.defineProperty(this, 'name', { value: 'ValidationError' });
|
||||
}
|
||||
}
|
||||
|
||||
export class AuthenticationError extends BaseGraphQLError {
|
||||
constructor(message: string, extensions?: Record<string, any>) {
|
||||
super(message, 'UNAUTHENTICATED', extensions);
|
||||
|
||||
Object.defineProperty(this, 'name', { value: 'AuthenticationError' });
|
||||
}
|
||||
}
|
||||
|
||||
export class ForbiddenError extends BaseGraphQLError {
|
||||
constructor(message: string, extensions?: Record<string, any>) {
|
||||
super(message, 'FORBIDDEN', extensions);
|
||||
|
||||
Object.defineProperty(this, 'name', { value: 'ForbiddenError' });
|
||||
}
|
||||
}
|
||||
|
||||
export class PersistedQueryNotFoundError extends BaseGraphQLError {
|
||||
constructor() {
|
||||
super('PersistedQueryNotFound', 'PERSISTED_QUERY_NOT_FOUND');
|
||||
|
||||
Object.defineProperty(this, 'name', {
|
||||
value: 'PersistedQueryNotFoundError',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class PersistedQueryNotSupportedError extends BaseGraphQLError {
|
||||
constructor() {
|
||||
super('PersistedQueryNotSupported', 'PERSISTED_QUERY_NOT_SUPPORTED');
|
||||
|
||||
Object.defineProperty(this, 'name', {
|
||||
value: 'PersistedQueryNotSupportedError',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class UserInputError extends BaseGraphQLError {
|
||||
constructor(message: string, extensions?: Record<string, any>) {
|
||||
super(message, 'BAD_USER_INPUT', extensions);
|
||||
|
||||
Object.defineProperty(this, 'name', { value: 'UserInputError' });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user