[1/n]: Migrate deleteOne Rest API to use TwentyORM directly (#9784)
# This PR - Addressing #3644 - Migrates the `DELETE /rest/*` endpoint to use TwentyORM - Factorizes common middleware logic into a common module --------- Co-authored-by: martmull <martmull@hotmail.fr>
This commit is contained in:
@ -7,20 +7,26 @@ import {
|
||||
Put,
|
||||
Req,
|
||||
Res,
|
||||
UseFilters,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
import { RestApiCoreServiceV2 } from 'src/engine/api/rest/core/rest-api-core-v2.service';
|
||||
import { RestApiCoreService } from 'src/engine/api/rest/core/rest-api-core.service';
|
||||
import { cleanGraphQLResponse } from 'src/engine/api/rest/utils/clean-graphql-response.utils';
|
||||
import { JwtAuthGuard } from 'src/engine/guards/jwt-auth.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { RestApiExceptionFilter } from 'src/engine/api/rest/rest-api-exception.filter';
|
||||
|
||||
@Controller('rest/*')
|
||||
@UseGuards(JwtAuthGuard, WorkspaceAuthGuard)
|
||||
export class RestApiCoreController {
|
||||
constructor(private readonly restApiCoreService: RestApiCoreService) {}
|
||||
constructor(
|
||||
private readonly restApiCoreService: RestApiCoreService,
|
||||
private readonly restApiCoreServiceV2: RestApiCoreServiceV2,
|
||||
) {}
|
||||
|
||||
@Post('/duplicates')
|
||||
async handleApiFindDuplicates(@Req() request: Request, @Res() res: Response) {
|
||||
@ -37,10 +43,13 @@ export class RestApiCoreController {
|
||||
}
|
||||
|
||||
@Delete()
|
||||
// We should move this exception filter to RestApiCoreController class level
|
||||
// when all endpoints are migrated to v2
|
||||
@UseFilters(RestApiExceptionFilter)
|
||||
async handleApiDelete(@Req() request: Request, @Res() res: Response) {
|
||||
const result = await this.restApiCoreService.delete(request);
|
||||
const result = await this.restApiCoreServiceV2.delete(request);
|
||||
|
||||
res.status(200).send(cleanGraphQLResponse(result.data.data));
|
||||
res.status(200).send(result);
|
||||
}
|
||||
|
||||
@Post()
|
||||
|
||||
@ -0,0 +1,73 @@
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
|
||||
import { Request } from 'express';
|
||||
import { capitalize } from 'twenty-shared';
|
||||
|
||||
import { CoreQueryBuilderFactory } from 'src/engine/api/rest/core/query-builder/core-query-builder.factory';
|
||||
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
|
||||
@Injectable()
|
||||
export class RestApiCoreServiceV2 {
|
||||
constructor(
|
||||
private readonly coreQueryBuilderFactory: CoreQueryBuilderFactory,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
async delete(request: Request) {
|
||||
const { workspace } = request;
|
||||
const { object: parsedObject, id: recordId } = parseCorePath(request);
|
||||
|
||||
const objectMetadata = await this.coreQueryBuilderFactory.getObjectMetadata(
|
||||
request,
|
||||
parsedObject,
|
||||
);
|
||||
|
||||
if (!objectMetadata) {
|
||||
throw new BadRequestException('Object metadata not found');
|
||||
}
|
||||
|
||||
if (!recordId) {
|
||||
throw new BadRequestException('Record ID not found');
|
||||
}
|
||||
|
||||
const objectMetadataNameSingular =
|
||||
objectMetadata.objectMetadataItem.nameSingular;
|
||||
|
||||
if (!workspace?.id) {
|
||||
throw new BadRequestException('Workspace not found');
|
||||
}
|
||||
|
||||
const repository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
|
||||
workspace.id,
|
||||
objectMetadataNameSingular,
|
||||
);
|
||||
|
||||
const recordToDelete = await repository.findOneOrFail({
|
||||
where: {
|
||||
id: recordId,
|
||||
},
|
||||
});
|
||||
|
||||
await repository.delete(recordId);
|
||||
|
||||
return this.formatResult('delete', objectMetadataNameSingular, {
|
||||
id: recordToDelete.id,
|
||||
});
|
||||
}
|
||||
|
||||
private formatResult<T>(
|
||||
operation: 'delete' | 'create' | 'update' | 'find',
|
||||
objectNameSingular: string,
|
||||
data: T,
|
||||
) {
|
||||
const result = {
|
||||
data: {
|
||||
[operation + capitalize(objectNameSingular)]: data,
|
||||
},
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common';
|
||||
|
||||
import { Response } from 'express';
|
||||
|
||||
import { HttpExceptionHandlerService } from 'src/engine/core-modules/exception-handler/http-exception-handler.service';
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
@Catch()
|
||||
export class RestApiExceptionFilter implements ExceptionFilter {
|
||||
constructor(
|
||||
private readonly httpExceptionHandlerService: HttpExceptionHandlerService,
|
||||
) {}
|
||||
|
||||
catch(exception: unknown, host: ArgumentsHost) {
|
||||
const ctx = host.switchToHttp();
|
||||
const response = ctx.getResponse<Response>();
|
||||
|
||||
return this.httpExceptionHandlerService.handleError(
|
||||
exception as CustomException,
|
||||
response,
|
||||
400,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ import { Module } from '@nestjs/common';
|
||||
import { RestApiCoreBatchController } from 'src/engine/api/rest/core/controllers/rest-api-core-batch.controller';
|
||||
import { RestApiCoreController } from 'src/engine/api/rest/core/controllers/rest-api-core.controller';
|
||||
import { CoreQueryBuilderModule } from 'src/engine/api/rest/core/query-builder/core-query-builder.module';
|
||||
import { RestApiCoreServiceV2 } from 'src/engine/api/rest/core/rest-api-core-v2.service';
|
||||
import { RestApiCoreService } from 'src/engine/api/rest/core/rest-api-core.service';
|
||||
import { EndingBeforeInputFactory } from 'src/engine/api/rest/input-factories/ending-before-input.factory';
|
||||
import { LimitInputFactory } from 'src/engine/api/rest/input-factories/limit-input.factory';
|
||||
@ -13,6 +14,7 @@ import { RestApiMetadataController } from 'src/engine/api/rest/metadata/rest-api
|
||||
import { RestApiMetadataService } from 'src/engine/api/rest/metadata/rest-api-metadata.service';
|
||||
import { RestApiService } from 'src/engine/api/rest/rest-api.service';
|
||||
import { AuthModule } from 'src/engine/core-modules/auth/auth.module';
|
||||
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
|
||||
@Module({
|
||||
@ -22,6 +24,7 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
|
||||
WorkspaceCacheStorageModule,
|
||||
AuthModule,
|
||||
HttpModule,
|
||||
TwentyORMModule,
|
||||
],
|
||||
controllers: [
|
||||
RestApiMetadataController,
|
||||
@ -31,6 +34,7 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
|
||||
providers: [
|
||||
RestApiMetadataService,
|
||||
RestApiCoreService,
|
||||
RestApiCoreServiceV2,
|
||||
RestApiService,
|
||||
StartingAfterInputFactory,
|
||||
EndingBeforeInputFactory,
|
||||
|
||||
Reference in New Issue
Block a user