Common - Update one/many, restore one/many, findDuplicates & mergeMany (#15279)
closes https://github.com/twentyhq/core-team-issues/issues/1739
This commit is contained in:
+26
@@ -106,6 +106,32 @@ export class RestApiCoreController {
|
||||
res.status(200).send(result);
|
||||
}
|
||||
|
||||
@Patch('restore/*')
|
||||
async handleApiRestore(
|
||||
@Req() request: AuthenticatedRequest,
|
||||
@Res() res: Response,
|
||||
) {
|
||||
this.logger.log(
|
||||
`[REST API] Processing RESTORE request to ${request.path} on workspace ${request.workspaceId}`,
|
||||
);
|
||||
const result = await this.restApiCoreService.restore(request);
|
||||
|
||||
res.status(200).send(result);
|
||||
}
|
||||
|
||||
@Patch('*/merge')
|
||||
async handleApiMerge(
|
||||
@Req() request: AuthenticatedRequest,
|
||||
@Res() res: Response,
|
||||
) {
|
||||
this.logger.log(
|
||||
`[REST API] Processing MERGE request to ${request.path} on workspace ${request.workspaceId}`,
|
||||
);
|
||||
const result = await this.restApiCoreService.mergeMany(request);
|
||||
|
||||
res.status(200).send(result);
|
||||
}
|
||||
|
||||
@Patch('*')
|
||||
async handleApiPatch(
|
||||
@Req() request: AuthenticatedRequest,
|
||||
|
||||
-2
@@ -11,7 +11,6 @@ import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonCreateManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-create-many-query-runner/common-create-many-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseDepthRestRequest } from 'src/engine/api/rest/input-request-parsers/depth-parser-utils/parse-depth-rest-request.util';
|
||||
import { parseUpsertRestRequest } from 'src/engine/api/rest/input-request-parsers/upsert-parser-utils/parse-upsert-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
@@ -49,7 +48,6 @@ export class RestApiCreateManyHandler extends RestApiBaseHandler {
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.CREATE_MANY,
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
|
||||
-2
@@ -11,7 +11,6 @@ import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonCreateOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-create-one-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseDepthRestRequest } from 'src/engine/api/rest/input-request-parsers/depth-parser-utils/parse-depth-rest-request.util';
|
||||
import { parseUpsertRestRequest } from 'src/engine/api/rest/input-request-parsers/upsert-parser-utils/parse-upsert-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
@@ -50,7 +49,6 @@ export class RestApiCreateOneHandler extends RestApiBaseHandler {
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.CREATE_ONE,
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
|
||||
-2
@@ -6,7 +6,6 @@ import { capitalize } from 'twenty-shared/utils';
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonDeleteManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-delete-many-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseFilterRestRequest } from 'src/engine/api/rest/input-request-parsers/filter-parser-utils/parse-filter-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
import { workspaceQueryRunnerRestApiExceptionHandler } from 'src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util';
|
||||
@@ -36,7 +35,6 @@ export class RestApiDeleteManyHandler extends RestApiBaseHandler {
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.DELETE_MANY,
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
|
||||
-4
@@ -6,9 +6,7 @@ import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonDeleteOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-delete-one-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
|
||||
import { parseDepthRestRequest } from 'src/engine/api/rest/input-request-parsers/depth-parser-utils/parse-depth-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
import { workspaceQueryRunnerRestApiExceptionHandler } from 'src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util';
|
||||
|
||||
@@ -37,7 +35,6 @@ export class RestApiDeleteOneHandler extends RestApiBaseHandler {
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.DELETE_ONE,
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
@@ -64,7 +61,6 @@ export class RestApiDeleteOneHandler extends RestApiBaseHandler {
|
||||
|
||||
return {
|
||||
id,
|
||||
depth: parseDepthRestRequest(request),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
-2
@@ -6,7 +6,6 @@ import { capitalize } from 'twenty-shared/utils';
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonDestroyManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-destroy-many-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseFilterRestRequest } from 'src/engine/api/rest/input-request-parsers/filter-parser-utils/parse-filter-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
import { workspaceQueryRunnerRestApiExceptionHandler } from 'src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util';
|
||||
@@ -36,7 +35,6 @@ export class RestApiDestroyManyHandler extends RestApiBaseHandler {
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.DESTROY_MANY,
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
|
||||
-2
@@ -6,7 +6,6 @@ import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonDestroyOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-destroy-one-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
import { workspaceQueryRunnerRestApiExceptionHandler } from 'src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util';
|
||||
@@ -37,7 +36,6 @@ export class RestApiDestroyOneHandler extends RestApiBaseHandler {
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.DESTROY_ONE,
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
|
||||
+82
@@ -10,11 +10,93 @@ import {
|
||||
RestApiBaseHandler,
|
||||
} from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonFindDuplicatesQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-find-duplicates-query-runner.service';
|
||||
import { parseDepthRestRequest } from 'src/engine/api/rest/input-request-parsers/depth-parser-utils/parse-depth-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
import { workspaceQueryRunnerRestApiExceptionHandler } from 'src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util';
|
||||
import { buildDuplicateConditions } from 'src/engine/api/utils/build-duplicate-conditions.utils';
|
||||
|
||||
@Injectable()
|
||||
export class RestApiFindDuplicatesHandler extends RestApiBaseHandler {
|
||||
constructor(
|
||||
private readonly commonFindDuplicatesQueryRunnerService: CommonFindDuplicatesQueryRunnerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async commonHandle(request: AuthenticatedRequest) {
|
||||
try {
|
||||
this.validate(request);
|
||||
|
||||
const { data, ids, depth } = this.parseRequestArgs(request);
|
||||
|
||||
const {
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
} = await this.buildCommonOptions(request);
|
||||
|
||||
const selectedFields = await this.computeSelectedFields({
|
||||
depth,
|
||||
objectMetadataMapItem: objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
authContext,
|
||||
});
|
||||
|
||||
const duplicateConnections =
|
||||
await this.commonFindDuplicatesQueryRunnerService.execute(
|
||||
{ data, ids, selectedFields },
|
||||
{
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
duplicateConnections,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerRestApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
private formatRestResponse(
|
||||
duplicateConnections: Array<{
|
||||
records: ObjectRecord[];
|
||||
totalCount: number;
|
||||
hasNextPage: boolean;
|
||||
hasPreviousPage: boolean;
|
||||
startCursor: string | null;
|
||||
endCursor: string | null;
|
||||
}>,
|
||||
objectNameSingular: string,
|
||||
) {
|
||||
return {
|
||||
data: duplicateConnections.map((connection) => ({
|
||||
data: {
|
||||
[`${objectNameSingular}Duplicates`]: connection.records,
|
||||
},
|
||||
totalCount: connection.totalCount,
|
||||
pageInfo: {
|
||||
hasNextPage: connection.hasNextPage,
|
||||
hasPreviousPage: connection.hasPreviousPage,
|
||||
startCursor: connection.startCursor,
|
||||
endCursor: connection.endCursor,
|
||||
},
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
private parseRequestArgs(request: AuthenticatedRequest) {
|
||||
return {
|
||||
data: request.body.data,
|
||||
ids: request.body.ids,
|
||||
depth: parseDepthRestRequest(request),
|
||||
};
|
||||
}
|
||||
|
||||
async handle(request: AuthenticatedRequest) {
|
||||
this.validate(request);
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
} from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonFindManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-find-many-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseDepthRestRequest } from 'src/engine/api/rest/input-request-parsers/depth-parser-utils/parse-depth-rest-request.util';
|
||||
import { parseEndingBeforeRestRequest } from 'src/engine/api/rest/input-request-parsers/ending-before-parser-utils/parse-ending-before-rest-request.util';
|
||||
import { parseFilterRestRequest } from 'src/engine/api/rest/input-request-parsers/filter-parser-utils/parse-filter-rest-request.util';
|
||||
@@ -118,7 +117,6 @@ export class RestApiFindManyHandler extends RestApiBaseHandler {
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.FIND_MANY,
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
|
||||
@@ -6,7 +6,6 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonFindOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-find-one-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
|
||||
import { parseDepthRestRequest } from 'src/engine/api/rest/input-request-parsers/depth-parser-utils/parse-depth-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
@@ -81,7 +80,6 @@ export class RestApiFindOneHandler extends RestApiBaseHandler {
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.FIND_ONE,
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
|
||||
+1
-3
@@ -3,7 +3,6 @@ import { Injectable } from '@nestjs/common';
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonGroupByQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-group-by-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseAggregateFieldsRestRequest } from 'src/engine/api/rest/input-request-parsers/aggregate-fields-parser-utils/parse-aggregate-fields-rest-request.util';
|
||||
import { parseFilterRestRequest } from 'src/engine/api/rest/input-request-parsers/filter-parser-utils/parse-filter-rest-request.util';
|
||||
import { parseGroupByRestRequest } from 'src/engine/api/rest/input-request-parsers/group-by-parser-utils/parse-group-by-rest-request.util';
|
||||
@@ -44,10 +43,9 @@ export class RestApiGroupByHandler extends RestApiBaseHandler {
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.GROUP_BY,
|
||||
);
|
||||
} catch (error) {
|
||||
throw workspaceQueryRunnerRestApiExceptionHandler(error);
|
||||
return workspaceQueryRunnerRestApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { ObjectRecord } from 'twenty-shared/types';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonMergeManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-merge-many-query-runner.service';
|
||||
import { parseDepthRestRequest } from 'src/engine/api/rest/input-request-parsers/depth-parser-utils/parse-depth-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
import { workspaceQueryRunnerRestApiExceptionHandler } from 'src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util';
|
||||
|
||||
@Injectable()
|
||||
export class RestApiMergeManyHandler extends RestApiBaseHandler {
|
||||
constructor(
|
||||
private readonly commonMergeManyQueryRunnerService: CommonMergeManyQueryRunnerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async handle(request: AuthenticatedRequest) {
|
||||
try {
|
||||
const { depth, ...restArgs } = await this.parseRequestArgs(request);
|
||||
const {
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
} = await this.buildCommonOptions(request);
|
||||
|
||||
const selectedFields = await this.computeSelectedFields({
|
||||
depth,
|
||||
objectMetadataMapItem: objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
authContext,
|
||||
});
|
||||
|
||||
const record = await this.commonMergeManyQueryRunnerService.execute(
|
||||
{ ...restArgs, selectedFields },
|
||||
{
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
record,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
);
|
||||
} catch (error) {
|
||||
return workspaceQueryRunnerRestApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
private formatRestResponse(record: ObjectRecord, objectNamePlural: string) {
|
||||
return { data: { [`merge${capitalize(objectNamePlural)}`]: record } };
|
||||
}
|
||||
|
||||
private async parseRequestArgs(request: AuthenticatedRequest) {
|
||||
const depth = parseDepthRestRequest(request);
|
||||
|
||||
return {
|
||||
conflictPriorityIndex: request.body.conflictPriorityIndex,
|
||||
dryRun: request.body.dryRun,
|
||||
ids: request.body.ids,
|
||||
depth,
|
||||
};
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { ObjectRecord } from 'twenty-shared/types';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonRestoreManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-restore-many-query-runner.service';
|
||||
import { parseDepthRestRequest } from 'src/engine/api/rest/input-request-parsers/depth-parser-utils/parse-depth-rest-request.util';
|
||||
import { parseFilterRestRequest } from 'src/engine/api/rest/input-request-parsers/filter-parser-utils/parse-filter-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
import { workspaceQueryRunnerRestApiExceptionHandler } from 'src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util';
|
||||
|
||||
@Injectable()
|
||||
export class RestApiRestoreManyHandler extends RestApiBaseHandler {
|
||||
constructor(
|
||||
private readonly commonRestoreManyQueryRunnerService: CommonRestoreManyQueryRunnerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async handle(request: AuthenticatedRequest): Promise<{
|
||||
data: {
|
||||
[x: string]: ObjectRecord[];
|
||||
};
|
||||
}> {
|
||||
try {
|
||||
const { filter, depth } = this.parseRequestArgs(request);
|
||||
|
||||
const {
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
} = await this.buildCommonOptions(request);
|
||||
|
||||
const selectedFields = await this.computeSelectedFields({
|
||||
depth,
|
||||
objectMetadataMapItem: objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
authContext,
|
||||
});
|
||||
|
||||
const records = await this.commonRestoreManyQueryRunnerService.execute(
|
||||
{ filter, selectedFields },
|
||||
{
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
records,
|
||||
objectMetadataItemWithFieldMaps.namePlural,
|
||||
);
|
||||
} catch (error) {
|
||||
return workspaceQueryRunnerRestApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
private formatRestResponse(
|
||||
records: ObjectRecord[],
|
||||
objectNamePlural: string,
|
||||
) {
|
||||
return {
|
||||
data: {
|
||||
[`restore${capitalize(objectNamePlural)}`]: records,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private parseRequestArgs(request: AuthenticatedRequest) {
|
||||
const filter = parseFilterRestRequest(request);
|
||||
|
||||
return {
|
||||
filter,
|
||||
depth: parseDepthRestRequest(request),
|
||||
};
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
|
||||
import { ObjectRecord } from 'twenty-shared/types';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonRestoreOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-restore-one-query-runner.service';
|
||||
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
|
||||
import { parseDepthRestRequest } from 'src/engine/api/rest/input-request-parsers/depth-parser-utils/parse-depth-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
import { workspaceQueryRunnerRestApiExceptionHandler } from 'src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util';
|
||||
|
||||
@Injectable()
|
||||
export class RestApiRestoreOneHandler extends RestApiBaseHandler {
|
||||
constructor(
|
||||
private readonly commonRestoreOneQueryRunnerService: CommonRestoreOneQueryRunnerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async handle(request: AuthenticatedRequest) {
|
||||
try {
|
||||
const { id, depth } = this.parseRequestArgs(request);
|
||||
|
||||
const {
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
} = await this.buildCommonOptions(request);
|
||||
|
||||
const selectedFields = await this.computeSelectedFields({
|
||||
depth,
|
||||
objectMetadataMapItem: objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
authContext,
|
||||
});
|
||||
|
||||
const record = await this.commonRestoreOneQueryRunnerService.execute(
|
||||
{ id, selectedFields },
|
||||
{
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
record,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
);
|
||||
} catch (error) {
|
||||
return workspaceQueryRunnerRestApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
private formatRestResponse(record: ObjectRecord, objectNameSingular: string) {
|
||||
return {
|
||||
data: { [`restore${capitalize(objectNameSingular)}`]: record },
|
||||
};
|
||||
}
|
||||
|
||||
private parseRequestArgs(request: AuthenticatedRequest) {
|
||||
const { id } = parseCorePath(request);
|
||||
|
||||
if (!isDefined(id)) {
|
||||
throw new BadRequestException('Record ID not found');
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
depth: parseDepthRestRequest(request),
|
||||
};
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { ObjectRecord } from 'twenty-shared/types';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonUpdateManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-update-many-query-runner.service';
|
||||
import { parseDepthRestRequest } from 'src/engine/api/rest/input-request-parsers/depth-parser-utils/parse-depth-rest-request.util';
|
||||
import { parseFilterRestRequest } from 'src/engine/api/rest/input-request-parsers/filter-parser-utils/parse-filter-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
import { workspaceQueryRunnerRestApiExceptionHandler } from 'src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util';
|
||||
|
||||
@Injectable()
|
||||
export class RestApiUpdateManyHandler extends RestApiBaseHandler {
|
||||
constructor(
|
||||
private readonly commonUpdateManyQueryRunnerService: CommonUpdateManyQueryRunnerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async handle(request: AuthenticatedRequest) {
|
||||
try {
|
||||
const { data, depth, filter } = this.parseRequestArgs(request);
|
||||
|
||||
const {
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
} = await this.buildCommonOptions(request);
|
||||
|
||||
const selectedFields = await this.computeSelectedFields({
|
||||
depth,
|
||||
objectMetadataMapItem: objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
authContext,
|
||||
});
|
||||
|
||||
const records = await this.commonUpdateManyQueryRunnerService.execute(
|
||||
{ data, filter, selectedFields },
|
||||
{
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
records,
|
||||
objectMetadataItemWithFieldMaps.namePlural,
|
||||
);
|
||||
} catch (error) {
|
||||
return workspaceQueryRunnerRestApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
private formatRestResponse(
|
||||
records: ObjectRecord[],
|
||||
objectNamePlural: string,
|
||||
) {
|
||||
return {
|
||||
data: {
|
||||
[`update${capitalize(objectNamePlural)}`]: records,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private parseRequestArgs(request: AuthenticatedRequest) {
|
||||
return {
|
||||
data: request.body,
|
||||
depth: parseDepthRestRequest(request),
|
||||
filter: parseFilterRestRequest(request),
|
||||
};
|
||||
}
|
||||
}
|
||||
+63
-1
@@ -5,17 +5,79 @@ import {
|
||||
} from '@nestjs/common';
|
||||
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { ObjectRecord } from 'twenty-shared/types';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonUpdateOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-update-one-query-runner.service';
|
||||
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
|
||||
import { parseDepthRestRequest } from 'src/engine/api/rest/input-request-parsers/depth-parser-utils/parse-depth-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
import { workspaceQueryRunnerRestApiExceptionHandler } from 'src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util';
|
||||
import { getAllSelectableFields } from 'src/engine/api/utils/get-all-selectable-fields.utils';
|
||||
|
||||
@Injectable()
|
||||
export class RestApiUpdateOneHandler extends RestApiBaseHandler {
|
||||
constructor(
|
||||
private readonly commonUpdateOneQueryRunnerService: CommonUpdateOneQueryRunnerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async commonHandle(request: AuthenticatedRequest) {
|
||||
try {
|
||||
const { id, data, depth } = this.parseRequestArgs(request);
|
||||
|
||||
const {
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
} = await this.buildCommonOptions(request);
|
||||
|
||||
const selectedFields = await this.computeSelectedFields({
|
||||
depth,
|
||||
objectMetadataMapItem: objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
authContext,
|
||||
});
|
||||
|
||||
const record = await this.commonUpdateOneQueryRunnerService.execute(
|
||||
{ id, data, selectedFields },
|
||||
{
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
record,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerRestApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
private formatRestResponse(record: ObjectRecord, objectNameSingular: string) {
|
||||
return { data: { [`update${capitalize(objectNameSingular)}`]: record } };
|
||||
}
|
||||
|
||||
private parseRequestArgs(request: AuthenticatedRequest) {
|
||||
const { id } = parseCorePath(request);
|
||||
|
||||
if (!id) {
|
||||
throw new BadRequestException('Record ID not found');
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
data: request.body,
|
||||
depth: parseDepthRestRequest(request),
|
||||
};
|
||||
}
|
||||
|
||||
async handle(request: AuthenticatedRequest) {
|
||||
const { id: recordId } = parseCorePath(request);
|
||||
|
||||
|
||||
+23
-3
@@ -1,7 +1,7 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
|
||||
import { type Request } from 'express';
|
||||
import { isValidUuid } from 'twenty-shared/utils';
|
||||
import { isDefined, isValidUuid } from 'twenty-shared/utils';
|
||||
|
||||
export const parseCorePath = (
|
||||
request: Request,
|
||||
@@ -12,7 +12,10 @@ export const parseCorePath = (
|
||||
.split('/')
|
||||
.filter(Boolean);
|
||||
|
||||
if (queryAction.length > 2) {
|
||||
if (
|
||||
queryAction.length > 2 ||
|
||||
(queryAction.length > 3 && queryAction[0] === 'restore')
|
||||
) {
|
||||
throw new BadRequestException(
|
||||
`Query path '${request.path}' invalid. Valid examples: /rest/companies/id or /rest/companies or /rest/batch/companies`,
|
||||
);
|
||||
@@ -32,10 +35,27 @@ export const parseCorePath = (
|
||||
return { object: queryAction[1] };
|
||||
}
|
||||
|
||||
if (queryAction[1] === 'duplicates' || queryAction[1] === 'group') {
|
||||
if (
|
||||
queryAction[1] === 'duplicates' ||
|
||||
queryAction[1] === 'group' ||
|
||||
queryAction[1] === 'merge'
|
||||
) {
|
||||
return { object: queryAction[0] };
|
||||
}
|
||||
|
||||
if (queryAction[0] === 'restore') {
|
||||
const recordId = queryAction[2];
|
||||
|
||||
if (isDefined(recordId) && !isValidUuid(recordId)) {
|
||||
throw new BadRequestException(`'${recordId}' is not a valid UUID`);
|
||||
}
|
||||
|
||||
return {
|
||||
object: queryAction[1],
|
||||
id: recordId,
|
||||
};
|
||||
}
|
||||
|
||||
const recordId = queryAction[1];
|
||||
|
||||
if (!isValidUuid(recordId)) {
|
||||
|
||||
@@ -13,6 +13,10 @@ import { RestApiFindDuplicatesHandler } from 'src/engine/api/rest/core/handlers/
|
||||
import { RestApiFindManyHandler } from 'src/engine/api/rest/core/handlers/rest-api-find-many.handler';
|
||||
import { RestApiFindOneHandler } from 'src/engine/api/rest/core/handlers/rest-api-find-one.handler';
|
||||
import { RestApiGroupByHandler } from 'src/engine/api/rest/core/handlers/rest-api-group-by.handler';
|
||||
import { RestApiMergeManyHandler } from 'src/engine/api/rest/core/handlers/rest-api-merge-many.handler';
|
||||
import { RestApiRestoreManyHandler } from 'src/engine/api/rest/core/handlers/rest-api-restore-many.handler';
|
||||
import { RestApiRestoreOneHandler } from 'src/engine/api/rest/core/handlers/rest-api-restore-one.handler';
|
||||
import { RestApiUpdateManyHandler } from 'src/engine/api/rest/core/handlers/rest-api-update-many.handler';
|
||||
import { RestApiUpdateOneHandler } from 'src/engine/api/rest/core/handlers/rest-api-update-one.handler';
|
||||
import { CoreQueryBuilderModule } from 'src/engine/api/rest/core/query-builder/core-query-builder.module';
|
||||
import { coreQueryBuilderFactories } from 'src/engine/api/rest/core/query-builder/factories/factories';
|
||||
@@ -33,6 +37,8 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
|
||||
const restApiCoreResolvers = [
|
||||
RestApiCreateOneHandler,
|
||||
RestApiCreateManyHandler,
|
||||
RestApiUpdateOneHandler,
|
||||
RestApiUpdateManyHandler,
|
||||
RestApiFindOneHandler,
|
||||
RestApiFindManyHandler,
|
||||
RestApiFindDuplicatesHandler,
|
||||
@@ -42,6 +48,9 @@ const restApiCoreResolvers = [
|
||||
RestApiDestroyManyHandler,
|
||||
RestApiDeleteOneHandler,
|
||||
RestApiDeleteManyHandler,
|
||||
RestApiRestoreOneHandler,
|
||||
RestApiRestoreManyHandler,
|
||||
RestApiMergeManyHandler,
|
||||
];
|
||||
|
||||
@Module({
|
||||
|
||||
@@ -12,6 +12,10 @@ import { RestApiFindDuplicatesHandler } from 'src/engine/api/rest/core/handlers/
|
||||
import { RestApiFindManyHandler } from 'src/engine/api/rest/core/handlers/rest-api-find-many.handler';
|
||||
import { RestApiFindOneHandler } from 'src/engine/api/rest/core/handlers/rest-api-find-one.handler';
|
||||
import { RestApiGroupByHandler } from 'src/engine/api/rest/core/handlers/rest-api-group-by.handler';
|
||||
import { RestApiMergeManyHandler } from 'src/engine/api/rest/core/handlers/rest-api-merge-many.handler';
|
||||
import { RestApiRestoreManyHandler } from 'src/engine/api/rest/core/handlers/rest-api-restore-many.handler';
|
||||
import { RestApiRestoreOneHandler } from 'src/engine/api/rest/core/handlers/rest-api-restore-one.handler';
|
||||
import { RestApiUpdateManyHandler } from 'src/engine/api/rest/core/handlers/rest-api-update-many.handler';
|
||||
import { RestApiUpdateOneHandler } from 'src/engine/api/rest/core/handlers/rest-api-update-one.handler';
|
||||
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
|
||||
import { parseSoftDeleteRestRequest } from 'src/engine/api/rest/input-request-parsers/soft-delete-parser-utils/parse-soft-delete-rest-request.util';
|
||||
@@ -24,15 +28,19 @@ export class RestApiCoreService {
|
||||
constructor(
|
||||
private readonly restApiCreateOneHandler: RestApiCreateOneHandler,
|
||||
private readonly restApiCreateManyHandler: RestApiCreateManyHandler,
|
||||
private readonly restApiUpdateOneHandler: RestApiUpdateOneHandler,
|
||||
private readonly restApiUpdateManyHandler: RestApiUpdateManyHandler,
|
||||
private readonly restApiFindOneHandler: RestApiFindOneHandler,
|
||||
private readonly restApiFindManyHandler: RestApiFindManyHandler,
|
||||
private readonly restApiFindDuplicatesHandler: RestApiFindDuplicatesHandler,
|
||||
private readonly restApiGroupByHandler: RestApiGroupByHandler,
|
||||
private readonly restApiUpdateOneHandler: RestApiUpdateOneHandler,
|
||||
private readonly restApiDestroyOneHandler: RestApiDestroyOneHandler,
|
||||
private readonly restApiDestroyManyHandler: RestApiDestroyManyHandler,
|
||||
private readonly restApiDeleteOneHandler: RestApiDeleteOneHandler,
|
||||
private readonly restApiDeleteManyHandler: RestApiDeleteManyHandler,
|
||||
private readonly restApiRestoreOneHandler: RestApiRestoreOneHandler,
|
||||
private readonly restApiRestoreManyHandler: RestApiRestoreManyHandler,
|
||||
private readonly restApiMergeManyHandler: RestApiMergeManyHandler,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
) {}
|
||||
|
||||
@@ -63,6 +71,37 @@ export class RestApiCoreService {
|
||||
}
|
||||
}
|
||||
|
||||
async findDuplicates(request: AuthenticatedRequest) {
|
||||
const isCommonApiEnabled = await this.isCommonApiEnabled(request);
|
||||
|
||||
if (isCommonApiEnabled) {
|
||||
return await this.restApiFindDuplicatesHandler.commonHandle(request);
|
||||
} else {
|
||||
return await this.restApiFindDuplicatesHandler.handle(request);
|
||||
}
|
||||
}
|
||||
|
||||
async update(request: AuthenticatedRequest) {
|
||||
const { id: recordId } = parseCorePath(request);
|
||||
const isCommonApiEnabled = await this.isCommonApiEnabled(request);
|
||||
|
||||
if (isCommonApiEnabled) {
|
||||
if (isDefined(recordId)) {
|
||||
return await this.restApiUpdateOneHandler.commonHandle(request);
|
||||
} else {
|
||||
return await this.restApiUpdateManyHandler.handle(request);
|
||||
}
|
||||
} else {
|
||||
if (isDefined(recordId)) {
|
||||
return await this.restApiUpdateOneHandler.handle(request);
|
||||
} else {
|
||||
throw new BadRequestException(
|
||||
'Activate feature flag to use UpdateMany in the REST API',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async get(request: AuthenticatedRequest) {
|
||||
const { id: recordId } = parseCorePath(request);
|
||||
const isCommonApiEnabled = await this.isCommonApiEnabled(request);
|
||||
@@ -94,14 +133,6 @@ export class RestApiCoreService {
|
||||
}
|
||||
}
|
||||
|
||||
async findDuplicates(request: AuthenticatedRequest) {
|
||||
return await this.restApiFindDuplicatesHandler.handle(request);
|
||||
}
|
||||
|
||||
async update(request: AuthenticatedRequest) {
|
||||
return await this.restApiUpdateOneHandler.handle(request);
|
||||
}
|
||||
|
||||
async delete(request: AuthenticatedRequest) {
|
||||
const { id: recordId } = parseCorePath(request);
|
||||
|
||||
@@ -125,4 +156,34 @@ export class RestApiCoreService {
|
||||
'Activate feature flag IS_COMMON_API_ENABLED to use Delete in the REST API',
|
||||
);
|
||||
}
|
||||
|
||||
async restore(request: AuthenticatedRequest) {
|
||||
const { id: recordId } = parseCorePath(request);
|
||||
|
||||
const isCommonApiEnabled = await this.isCommonApiEnabled(request);
|
||||
|
||||
if (isCommonApiEnabled) {
|
||||
if (isDefined(recordId)) {
|
||||
return await this.restApiRestoreOneHandler.handle(request);
|
||||
} else {
|
||||
return await this.restApiRestoreManyHandler.handle(request);
|
||||
}
|
||||
} else {
|
||||
throw new BadRequestException(
|
||||
'Activate feature flag to use Restore in the REST API',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async mergeMany(request: AuthenticatedRequest) {
|
||||
const isCommonApiEnabled = await this.isCommonApiEnabled(request);
|
||||
|
||||
if (isCommonApiEnabled) {
|
||||
return await this.restApiMergeManyHandler.handle(request);
|
||||
} else {
|
||||
throw new BadRequestException(
|
||||
'Activate feature flag to use Merge in the REST API',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user