11311 object create webhook should be triggered when object is created via rest api (#11336)
Fixes https://github.com/twentyhq/twenty/issues/11311
This commit is contained in:
2
packages/twenty-server/@types/express.d.ts
vendored
2
packages/twenty-server/@types/express.d.ts
vendored
@ -6,7 +6,7 @@ declare module 'express-serve-static-core' {
|
||||
interface Request {
|
||||
user?: User | null;
|
||||
apiKey?: ApiKeyWorkspaceEntity | null;
|
||||
workspace?: Workspace;
|
||||
workspace: Workspace;
|
||||
workspaceId?: string;
|
||||
workspaceMetadataVersion?: number;
|
||||
workspaceMemberId?: string;
|
||||
|
||||
@ -4,6 +4,8 @@ import { Request } from 'express';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { ObjectLiteral, OrderByCondition, SelectQueryBuilder } from 'typeorm';
|
||||
|
||||
import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||
|
||||
import { GraphqlQueryFilterConditionParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-filter/graphql-query-filter-condition.parser';
|
||||
import { GraphqlQueryOrderFieldParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-order/graphql-query-order.parser';
|
||||
import { CoreQueryBuilderFactory } from 'src/engine/api/rest/core/query-builder/core-query-builder.factory';
|
||||
@ -14,13 +16,14 @@ import { FilterInputFactory } from 'src/engine/api/rest/input-factories/filter-i
|
||||
import { LimitInputFactory } from 'src/engine/api/rest/input-factories/limit-input.factory';
|
||||
import { OrderByInputFactory } from 'src/engine/api/rest/input-factories/order-by-input.factory';
|
||||
import { StartingAfterInputFactory } from 'src/engine/api/rest/input-factories/starting-after-input.factory';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map';
|
||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { formatResult as formatGetManyData } from 'src/engine/twenty-orm/utils/format-result.util';
|
||||
import { ApiEventEmitterService } from 'src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
|
||||
interface FindManyMeta {
|
||||
hasNextPage: boolean;
|
||||
@ -44,10 +47,10 @@ export class RestApiCoreServiceV2 {
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly limitInputFactory: LimitInputFactory,
|
||||
private readonly filterInputFactory: FilterInputFactory,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
private readonly orderByInputFactory: OrderByInputFactory,
|
||||
private readonly startingAfterInputFactory: StartingAfterInputFactory,
|
||||
private readonly endingBeforeInputFactory: EndingBeforeInputFactory,
|
||||
protected readonly apiEventEmitterService: ApiEventEmitterService,
|
||||
) {}
|
||||
|
||||
async delete(request: Request) {
|
||||
@ -57,7 +60,7 @@ export class RestApiCoreServiceV2 {
|
||||
throw new BadRequestException('Record ID not found');
|
||||
}
|
||||
|
||||
const { objectMetadataNameSingular, repository } =
|
||||
const { objectMetadataNameSingular, objectMetadata, repository } =
|
||||
await this.getRepositoryAndMetadataOrFail(request);
|
||||
const recordToDelete = await repository.findOneOrFail({
|
||||
where: { id: recordId },
|
||||
@ -65,6 +68,12 @@ export class RestApiCoreServiceV2 {
|
||||
|
||||
await repository.delete(recordId);
|
||||
|
||||
this.apiEventEmitterService.emitDeletedEvents(
|
||||
[recordToDelete],
|
||||
this.getAuthContextFromRequest(request),
|
||||
objectMetadata.objectMetadataMapItem,
|
||||
);
|
||||
|
||||
return this.formatResult({
|
||||
operation: 'delete',
|
||||
objectNameSingular: objectMetadataNameSingular,
|
||||
@ -77,10 +86,16 @@ export class RestApiCoreServiceV2 {
|
||||
async createOne(request: Request) {
|
||||
const { body } = request;
|
||||
|
||||
const { objectMetadataNameSingular, repository } =
|
||||
const { objectMetadataNameSingular, objectMetadata, repository } =
|
||||
await this.getRepositoryAndMetadataOrFail(request);
|
||||
const createdRecord = await repository.save(body);
|
||||
|
||||
this.apiEventEmitterService.emitCreateEvents(
|
||||
[createdRecord],
|
||||
this.getAuthContextFromRequest(request),
|
||||
objectMetadata.objectMetadataMapItem,
|
||||
);
|
||||
|
||||
return this.formatResult({
|
||||
operation: 'create',
|
||||
objectNameSingular: objectMetadataNameSingular,
|
||||
@ -95,7 +110,7 @@ export class RestApiCoreServiceV2 {
|
||||
throw new BadRequestException('Record ID not found');
|
||||
}
|
||||
|
||||
const { objectMetadataNameSingular, repository } =
|
||||
const { objectMetadataNameSingular, objectMetadata, repository } =
|
||||
await this.getRepositoryAndMetadataOrFail(request);
|
||||
|
||||
const recordToUpdate = await repository.findOneOrFail({
|
||||
@ -107,6 +122,14 @@ export class RestApiCoreServiceV2 {
|
||||
...request.body,
|
||||
});
|
||||
|
||||
this.apiEventEmitterService.emitUpdateEvents(
|
||||
[recordToUpdate],
|
||||
[updatedRecord],
|
||||
Object.keys(request.body),
|
||||
this.getAuthContextFromRequest(request),
|
||||
objectMetadata.objectMetadataMapItem,
|
||||
);
|
||||
|
||||
return this.formatResult({
|
||||
operation: 'update',
|
||||
objectNameSingular: objectMetadataNameSingular,
|
||||
@ -441,7 +464,7 @@ export class RestApiCoreServiceV2 {
|
||||
);
|
||||
|
||||
const repository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ObjectRecord>(
|
||||
workspace.id,
|
||||
objectMetadataNameSingular,
|
||||
);
|
||||
@ -465,4 +488,14 @@ export class RestApiCoreServiceV2 {
|
||||
id: isForwardPagination ? { gt: cursorData.id } : { lt: cursorData.id },
|
||||
};
|
||||
}
|
||||
|
||||
private getAuthContextFromRequest(request: Request): AuthContext {
|
||||
return {
|
||||
user: request.user,
|
||||
workspace: request.workspace,
|
||||
apiKey: request.apiKey,
|
||||
workspaceMemberId: request.workspaceMemberId,
|
||||
userWorkspaceId: request.userWorkspaceId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
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';
|
||||
@ -17,11 +16,9 @@ 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 { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
import { ApiEventEmitterService } from 'src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -31,8 +28,6 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
|
||||
AuthModule,
|
||||
HttpModule,
|
||||
TwentyORMModule,
|
||||
TypeOrmModule.forFeature([FeatureFlag], 'core'),
|
||||
FeatureFlagModule,
|
||||
],
|
||||
controllers: [
|
||||
RestApiMetadataController,
|
||||
@ -44,12 +39,12 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
|
||||
RestApiCoreService,
|
||||
RestApiCoreServiceV2,
|
||||
RestApiService,
|
||||
FeatureFlagService,
|
||||
StartingAfterInputFactory,
|
||||
EndingBeforeInputFactory,
|
||||
LimitInputFactory,
|
||||
FilterInputFactory,
|
||||
OrderByInputFactory,
|
||||
ApiEventEmitterService,
|
||||
],
|
||||
exports: [RestApiMetadataService],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user