Skip to content

Commit

Permalink
Return graphql errors when exists (twentyhq#5389)
Browse files Browse the repository at this point in the history
- throw badRequest with graphql error messages when graphql request
fails
- clean some code

Before
<img width="1470" alt="image"
src="https://github.com/twentyhq/twenty/assets/29927851/0b700d9a-2bbe-41f7-84a9-981dc7dd5344">

After

![image](https://github.com/twentyhq/twenty/assets/29927851/6bbaaf7c-1244-473d-9ae5-4fefc6a1b994)
  • Loading branch information
martmull committed May 14, 2024
1 parent 1bc9b78 commit ffdd3a7
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ export class WorkspaceQueryRunnerService {
const { workspaceId, userId, objectMetadataItem } = options;

assertMutationNotOnRemoteObject(objectMetadataItem);
assertIsValidUuid(args.id);

const query = await this.workspaceQueryBuilderFactory.deleteOne(
args,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export class ApiRestQueryBuilderFactory {
};
}

async create(request): Promise<ApiRestQuery> {
async create(request: Request): Promise<ApiRestQuery> {
const objectMetadata = await this.getObjectMetadata(request);

const depth = computeDepth(request);
Expand All @@ -109,7 +109,7 @@ export class ApiRestQueryBuilderFactory {
};
}

async update(request): Promise<ApiRestQuery> {
async update(request: Request): Promise<ApiRestQuery> {
const objectMetadata = await this.getObjectMetadata(request);

const depth = computeDepth(request);
Expand All @@ -128,7 +128,7 @@ export class ApiRestQueryBuilderFactory {
};
}

async get(request): Promise<ApiRestQuery> {
async get(request: Request): Promise<ApiRestQuery> {
const objectMetadata = await this.getObjectMetadata(request);

const depth = computeDepth(request);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Injectable } from '@nestjs/common';

import { Request } from 'express';

import { ApiRestQueryVariables } from 'src/engine/api/rest/types/api-rest-query-variables.type';

@Injectable()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Injectable } from '@nestjs/common';

import { Request } from 'express';

import { ApiRestQueryVariables } from 'src/engine/api/rest/types/api-rest-query-variables.type';

@Injectable()
Expand Down
18 changes: 13 additions & 5 deletions packages/twenty-server/src/engine/api/rest/api-rest.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,37 @@ import { Controller, Delete, Get, Post, Put, Req, Res } from '@nestjs/common';
import { Request, Response } from 'express';

import { ApiRestService } from 'src/engine/api/rest/api-rest.service';
import { handleResult } from 'src/engine/api/rest/api-rest.controller.utils';
import { cleanGraphQLResponse } from 'src/engine/api/rest/api-rest.controller.utils';

@Controller('rest/*')
export class ApiRestController {
constructor(private readonly apiRestService: ApiRestService) {}

@Get()
async handleApiGet(@Req() request: Request, @Res() res: Response) {
handleResult(res, await this.apiRestService.get(request));
const result = await this.apiRestService.get(request);

res.send(cleanGraphQLResponse(result.data));
}

@Delete()
async handleApiDelete(@Req() request: Request, @Res() res: Response) {
handleResult(res, await this.apiRestService.delete(request));
const result = await this.apiRestService.delete(request);

res.send(cleanGraphQLResponse(result.data));
}

@Post()
async handleApiPost(@Req() request: Request, @Res() res: Response) {
handleResult(res, await this.apiRestService.create(request));
const result = await this.apiRestService.create(request);

res.send(cleanGraphQLResponse(result.data));
}

@Put()
async handleApiPut(@Req() request: Request, @Res() res: Response) {
handleResult(res, await this.apiRestService.update(request));
const result = await this.apiRestService.update(request);

res.send(cleanGraphQLResponse(result.data));
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import { Response } from 'express';

import { ApiRestResponse } from 'src/engine/api/rest/types/api-rest-response.type';

// https://gist.github.com/ManUtopiK/469aec75b655d6a4d912aeb3b75af3c9
export const cleanGraphQLResponse = (input) => {
export const cleanGraphQLResponse = (input: any) => {
if (!input) return null;
const output = {};
const isObject = (obj) => {
const isObject = (obj: any) => {
return obj !== null && typeof obj === 'object' && !Array.isArray(obj);
};

Expand All @@ -24,13 +20,3 @@ export const cleanGraphQLResponse = (input) => {

return output;
};

export const handleResult = (res: Response, result: ApiRestResponse) => {
if (result.data.error) {
res
.status(result.data.status || 400)
.send({ error: `${result.data.error}` });
} else {
res.send(cleanGraphQLResponse(result.data));
}
};
79 changes: 32 additions & 47 deletions packages/twenty-server/src/engine/api/rest/api-rest.service.ts
Original file line number Diff line number Diff line change
@@ -1,87 +1,72 @@
import { Injectable } from '@nestjs/common';
import { BadRequestException, Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';

import { Request } from 'express';
import { AxiosResponse } from 'axios';

import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { ApiRestQueryBuilderFactory } from 'src/engine/api/rest/api-rest-query-builder/api-rest-query-builder.factory';
import { TokenService } from 'src/engine/core-modules/auth/services/token.service';
import { ApiRestResponse } from 'src/engine/api/rest/types/api-rest-response.type';
import { ApiRestQuery } from 'src/engine/api/rest/types/api-rest-query.type';
import { getServerUrl } from 'src/utils/get-server-url';

@Injectable()
export class ApiRestService {
constructor(
private readonly tokenService: TokenService,
private readonly environmentService: EnvironmentService,
private readonly apiRestQueryBuilderFactory: ApiRestQueryBuilderFactory,
private readonly httpService: HttpService,
) {}

async callGraphql(
request: Request,
data: ApiRestQuery,
): Promise<ApiRestResponse> {
async callGraphql(request: Request, data: ApiRestQuery) {
const baseUrl = getServerUrl(
request,
this.environmentService.get('SERVER_URL'),
);
let response: AxiosResponse;

try {
return await this.httpService.axiosRef.post(`${baseUrl}/graphql`, data, {
headers: {
'Content-Type': 'application/json',
Authorization: request.headers.authorization,
response = await this.httpService.axiosRef.post(
`${baseUrl}/graphql`,
data,
{
headers: {
'Content-Type': 'application/json',
Authorization: request.headers.authorization,
},
},
});
);
} catch (err) {
return {
data: {
error: `${err}. Please check your query.`,
status: err.response.status,
},
};
throw new BadRequestException(err.response.data);
}

if (response.data.errors?.length) {
throw new BadRequestException(response.data);
}

return response;
}

async get(request: Request): Promise<ApiRestResponse> {
try {
const data = await this.apiRestQueryBuilderFactory.get(request);
async get(request: Request) {
const data = await this.apiRestQueryBuilderFactory.get(request);

return await this.callGraphql(request, data);
} catch (err) {
return { data: { error: err, status: err.status } };
}
return await this.callGraphql(request, data);
}

async delete(request: Request): Promise<ApiRestResponse> {
try {
const data = await this.apiRestQueryBuilderFactory.delete(request);
async delete(request: Request) {
const data = await this.apiRestQueryBuilderFactory.delete(request);

return await this.callGraphql(request, data);
} catch (err) {
return { data: { error: err, status: err.status } };
}
return await this.callGraphql(request, data);
}

async create(request: Request): Promise<ApiRestResponse> {
try {
const data = await this.apiRestQueryBuilderFactory.create(request);
async create(request: Request) {
const data = await this.apiRestQueryBuilderFactory.create(request);

return await this.callGraphql(request, data);
} catch (err) {
return { data: { error: err, status: err.status } };
}
return await this.callGraphql(request, data);
}

async update(request: Request): Promise<ApiRestResponse> {
try {
const data = await this.apiRestQueryBuilderFactory.update(request);
async update(request: Request) {
const data = await this.apiRestQueryBuilderFactory.update(request);

return await this.callGraphql(request, data);
} catch (err) {
return { data: { error: err, status: err.status } };
}
return await this.callGraphql(request, data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,38 @@ import { Controller, Get, Delete, Post, Put, Req, Res } from '@nestjs/common';

import { Request, Response } from 'express';

import { handleResult } from 'src/engine/api/rest/api-rest.controller.utils';
import { ApiRestMetadataService } from 'src/engine/api/rest/metadata-rest.service';
import { cleanGraphQLResponse } from 'src/engine/api/rest/api-rest.controller.utils';

@Controller('rest/metadata/*')
export class ApiRestMetadataController {
constructor(private readonly apiRestService: ApiRestMetadataService) {}

@Get()
async handleApiGet(@Req() request: Request, @Res() res: Response) {
handleResult(res, await this.apiRestService.get(request));
const result = await this.apiRestService.get(request);

res.send(cleanGraphQLResponse(result.data));
}

@Delete()
async handleApiDelete(@Req() request: Request, @Res() res: Response) {
handleResult(res, await this.apiRestService.delete(request));
const result = await this.apiRestService.delete(request);

res.send(cleanGraphQLResponse(result.data));
}

@Post()
async handleApiPost(@Req() request: Request, @Res() res: Response) {
handleResult(res, await this.apiRestService.create(request));
const result = await this.apiRestService.create(request);

res.send(cleanGraphQLResponse(result.data));
}

@Put()
async handleApiPut(@Req() request: Request, @Res() res: Response) {
handleResult(res, await this.apiRestService.update(request));
const result = await this.apiRestService.update(request);

res.send(cleanGraphQLResponse(result.data));
}
}

This file was deleted.

0 comments on commit ffdd3a7

Please sign in to comment.