feat: wip server folder structure (#4573)
* feat: wip server folder structure * fix: merge * fix: wrong merge * fix: remove unused file * fix: comment * fix: lint * fix: merge * fix: remove console.log * fix: metadata graphql arguments broken
This commit is contained in:
@ -1,8 +1,8 @@
|
||||
import { FieldMetadataInterface } from 'src/engine-metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
import { RelationMetadataInterface } from 'src/engine-metadata/field-metadata/interfaces/relation-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
import { RelationMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-metadata.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine-metadata/field-metadata/field-metadata.entity';
|
||||
import { RelationMetadataType } from 'src/engine-metadata/relation-metadata/relation-metadata.entity';
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import {
|
||||
deduceRelationDirection,
|
||||
RelationDirection,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ObjectMetadataInterface } from 'src/engine-metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { computeCustomName } from './compute-custom-name.util';
|
||||
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
export const createCustomColumnName = (name: string) => {
|
||||
return `_${name}`;
|
||||
};
|
||||
@ -1,5 +1,5 @@
|
||||
import { FieldMetadataInterface } from 'src/engine-metadata/field-metadata/interfaces/field-metadata.interface';
|
||||
import { RelationMetadataInterface } from 'src/engine-metadata/field-metadata/interfaces/relation-metadata.interface';
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
import { RelationMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-metadata.interface';
|
||||
|
||||
export enum RelationDirection {
|
||||
FROM = 'from',
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { WorkspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { ObjectMetadataInterface } from 'src/engine-metadata/field-metadata/interfaces/object-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { camelCase } from 'src/utils/camel-case';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@ -0,0 +1,94 @@
|
||||
import { HttpException } from '@nestjs/common';
|
||||
|
||||
import { ExceptionHandlerUser } from 'src/engine/integrations/exception-handler/interfaces/exception-handler-user.interface';
|
||||
|
||||
import {
|
||||
AuthenticationError,
|
||||
BaseGraphQLError,
|
||||
ForbiddenError,
|
||||
ValidationError,
|
||||
NotFoundError,
|
||||
ConflictError,
|
||||
} from 'src/engine/utils/graphql-errors.util';
|
||||
import { ExceptionHandlerService } from 'src/engine/integrations/exception-handler/exception-handler.service';
|
||||
|
||||
const graphQLPredefinedExceptions = {
|
||||
400: ValidationError,
|
||||
401: AuthenticationError,
|
||||
403: ForbiddenError,
|
||||
404: NotFoundError,
|
||||
409: ConflictError,
|
||||
};
|
||||
|
||||
export const handleExceptionAndConvertToGraphQLError = (
|
||||
exception: Error,
|
||||
exceptionHandlerService: ExceptionHandlerService,
|
||||
user?: ExceptionHandlerUser,
|
||||
): BaseGraphQLError => {
|
||||
handleException(exception, exceptionHandlerService, user);
|
||||
|
||||
return convertExceptionToGraphQLError(exception);
|
||||
};
|
||||
|
||||
export const filterException = (exception: Error): boolean => {
|
||||
if (exception instanceof HttpException && exception.getStatus() < 500) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const handleException = (
|
||||
exception: Error,
|
||||
exceptionHandlerService: ExceptionHandlerService,
|
||||
user?: ExceptionHandlerUser,
|
||||
): void => {
|
||||
if (filterException(exception)) {
|
||||
return;
|
||||
}
|
||||
|
||||
exceptionHandlerService.captureExceptions([exception], { user });
|
||||
};
|
||||
|
||||
export const convertExceptionToGraphQLError = (
|
||||
exception: Error,
|
||||
): BaseGraphQLError => {
|
||||
if (exception instanceof HttpException) {
|
||||
return convertHttpExceptionToGraphql(exception);
|
||||
}
|
||||
|
||||
return convertExceptionToGraphql(exception);
|
||||
};
|
||||
|
||||
export const convertHttpExceptionToGraphql = (exception: HttpException) => {
|
||||
const status = exception.getStatus();
|
||||
let error: BaseGraphQLError;
|
||||
|
||||
if (status in graphQLPredefinedExceptions) {
|
||||
const message = exception.getResponse()['message'] ?? exception.message;
|
||||
|
||||
error = new graphQLPredefinedExceptions[exception.getStatus()](message);
|
||||
} else {
|
||||
error = new BaseGraphQLError(
|
||||
'Internal Server Error',
|
||||
exception.getStatus().toString(),
|
||||
);
|
||||
}
|
||||
|
||||
// Only show the stack trace in development mode
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
error.stack = exception.stack;
|
||||
error.extensions['response'] = exception.getResponse();
|
||||
}
|
||||
|
||||
return error;
|
||||
};
|
||||
|
||||
export const convertExceptionToGraphql = (exception: Error) => {
|
||||
const error = new BaseGraphQLError(exception.name, 'INTERNAL_SERVER_ERROR');
|
||||
|
||||
error.stack = exception.stack;
|
||||
error.extensions['response'] = exception.message;
|
||||
|
||||
return error;
|
||||
};
|
||||
151
packages/twenty-server/src/engine/utils/graphql-errors.util.ts
Normal file
151
packages/twenty-server/src/engine/utils/graphql-errors.util.ts
Normal file
@ -0,0 +1,151 @@
|
||||
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' });
|
||||
}
|
||||
}
|
||||
|
||||
export class NotFoundError extends BaseGraphQLError {
|
||||
constructor(message: string, extensions?: Record<string, any>) {
|
||||
super(message, 'NOT_FOUND', extensions);
|
||||
|
||||
Object.defineProperty(this, 'name', { value: 'NotFoundError' });
|
||||
}
|
||||
}
|
||||
|
||||
export class ConflictError extends BaseGraphQLError {
|
||||
constructor(message: string, extensions?: Record<string, any>) {
|
||||
super(message, 'CONFLICT', extensions);
|
||||
|
||||
Object.defineProperty(this, 'name', { value: 'ConflictError' });
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import { FieldMetadataType } from 'src/engine-metadata/field-metadata/field-metadata.entity';
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
|
||||
export const isRelationFieldMetadataType = (
|
||||
type: FieldMetadataType,
|
||||
|
||||
Reference in New Issue
Block a user