Refactor global datasource part 2 (#16399)
## Context Deprecating TwentyORMManager in favor of TwentyORMGlobalManager (temporarily, as this will simplify the ultimate goal to later replace all usages with the new TwentyORMGlobalManagerV2 which will have a similar signature) This means this PR had to refactor a bit of code to pass down the workspaceId when not available directly as it is now a requirement, meaning we also deprecated scopedWorkspaceContextFactory to have a less obscure way to fetch the workspaceId and have something more declarative. Step 3 will be to update TwentyORMGlobalManager to use a featureFlag toggling and use the new GlobalWorkspaceOrmManager internally using the new cache service Step 4 will be to remove the feature flag and pg_pool patch
This commit is contained in:
@@ -33,7 +33,7 @@ import {
|
||||
PermissionsExceptionMessage,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
import { standardObjectMetadataDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects';
|
||||
@@ -56,7 +56,7 @@ export interface FormatResult {
|
||||
|
||||
export abstract class RestApiBaseHandler {
|
||||
@Inject()
|
||||
protected readonly twentyORMManager: TwentyORMManager;
|
||||
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager;
|
||||
@Inject()
|
||||
protected readonly workspaceCacheService: WorkspaceCacheService;
|
||||
@Inject()
|
||||
|
||||
+4
-3
@@ -7,7 +7,7 @@ import { StripeSubscriptionItemService } from 'src/engine/core-modules/billing/s
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
export type UpdateSubscriptionQuantityJobData = { workspaceId: string };
|
||||
|
||||
@@ -21,13 +21,14 @@ export class UpdateSubscriptionQuantityJob {
|
||||
constructor(
|
||||
private readonly billingSubscriptionService: BillingSubscriptionService,
|
||||
private readonly stripeSubscriptionItemService: StripeSubscriptionItemService,
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
@Process(UpdateSubscriptionQuantityJob.name)
|
||||
async handle(data: UpdateSubscriptionQuantityJobData): Promise<void> {
|
||||
const workspaceMemberRepository =
|
||||
await this.twentyORMManager.getRepository<WorkspaceMemberWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
|
||||
data.workspaceId,
|
||||
'workspaceMember',
|
||||
);
|
||||
|
||||
|
||||
+8
@@ -7,9 +7,11 @@ import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/
|
||||
import { TIMELINE_CALENDAR_EVENTS_MAX_PAGE_SIZE } from 'src/engine/core-modules/calendar/constants/calendar.constants';
|
||||
import { TimelineCalendarEventsWithTotalDTO } from 'src/engine/core-modules/calendar/dtos/timeline-calendar-events-with-total.dto';
|
||||
import { TimelineCalendarEventService } from 'src/engine/core-modules/calendar/timeline-calendar-event.service';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { AuthWorkspaceMemberId } from 'src/engine/decorators/auth/auth-workspace-member-id.decorator';
|
||||
import { CustomPermissionGuard } from 'src/engine/guards/custom-permission.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
|
||||
@ArgsType()
|
||||
class GetTimelineCalendarEventsFromPersonIdArgs {
|
||||
@@ -62,11 +64,13 @@ export class TimelineCalendarEventResolver {
|
||||
@Args()
|
||||
{ personId, page, pageSize }: GetTimelineCalendarEventsFromPersonIdArgs,
|
||||
@AuthWorkspaceMemberId() workspaceMemberId: string,
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
) {
|
||||
const timelineCalendarEvents =
|
||||
await this.timelineCalendarEventService.getCalendarEventsFromPersonIds({
|
||||
currentWorkspaceMemberId: workspaceMemberId,
|
||||
personIds: [personId],
|
||||
workspaceId: workspace.id,
|
||||
page,
|
||||
pageSize,
|
||||
});
|
||||
@@ -79,11 +83,13 @@ export class TimelineCalendarEventResolver {
|
||||
@Args()
|
||||
{ companyId, page, pageSize }: GetTimelineCalendarEventsFromCompanyIdArgs,
|
||||
@AuthWorkspaceMemberId() workspaceMemberId: string,
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
) {
|
||||
const timelineCalendarEvents =
|
||||
await this.timelineCalendarEventService.getCalendarEventsFromCompanyId({
|
||||
currentWorkspaceMemberId: workspaceMemberId,
|
||||
companyId,
|
||||
workspaceId: workspace.id,
|
||||
page,
|
||||
pageSize,
|
||||
});
|
||||
@@ -100,12 +106,14 @@ export class TimelineCalendarEventResolver {
|
||||
pageSize,
|
||||
}: GetTimelineCalendarEventsFromOpportunityIdArgs,
|
||||
@AuthWorkspaceMemberId() workspaceMemberId: string,
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
) {
|
||||
const timelineCalendarEvents =
|
||||
await this.timelineCalendarEventService.getCalendarEventsFromOpportunityId(
|
||||
{
|
||||
currentWorkspaceMemberId: workspaceMemberId,
|
||||
opportunityId,
|
||||
workspaceId: workspace.id,
|
||||
page,
|
||||
pageSize,
|
||||
},
|
||||
|
||||
+10
-5
@@ -3,7 +3,7 @@ import { Test, type TestingModule } from '@nestjs/testing';
|
||||
import { FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED } from 'twenty-shared/constants';
|
||||
|
||||
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { CalendarChannelVisibility } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import { type CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
|
||||
|
||||
@@ -36,16 +36,18 @@ describe('TimelineCalendarEventService', () => {
|
||||
findAndCount: jest.fn(),
|
||||
};
|
||||
|
||||
const mockTwentyORMManager = {
|
||||
getRepository: jest.fn().mockResolvedValue(mockCalendarEventRepository),
|
||||
const mockTwentyORMGlobalManager = {
|
||||
getRepositoryForWorkspace: jest
|
||||
.fn()
|
||||
.mockResolvedValue(mockCalendarEventRepository),
|
||||
};
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
TimelineCalendarEventService,
|
||||
{
|
||||
provide: TwentyORMManager,
|
||||
useValue: mockTwentyORMManager,
|
||||
provide: TwentyORMGlobalManager,
|
||||
useValue: mockTwentyORMGlobalManager,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
@@ -84,6 +86,7 @@ describe('TimelineCalendarEventService', () => {
|
||||
const result = await service.getCalendarEventsFromPersonIds({
|
||||
currentWorkspaceMemberId,
|
||||
personIds,
|
||||
workspaceId: 'test-workspace-id',
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
@@ -123,6 +126,7 @@ describe('TimelineCalendarEventService', () => {
|
||||
const result = await service.getCalendarEventsFromPersonIds({
|
||||
currentWorkspaceMemberId,
|
||||
personIds,
|
||||
workspaceId: 'test-workspace-id',
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
@@ -164,6 +168,7 @@ describe('TimelineCalendarEventService', () => {
|
||||
const result = await service.getCalendarEventsFromPersonIds({
|
||||
currentWorkspaceMemberId,
|
||||
personIds,
|
||||
workspaceId: 'test-workspace-id',
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
+18
-5
@@ -6,7 +6,7 @@ import { Any } from 'typeorm';
|
||||
|
||||
import { TIMELINE_CALENDAR_EVENTS_DEFAULT_PAGE_SIZE } from 'src/engine/core-modules/calendar/constants/calendar.constants';
|
||||
import { type TimelineCalendarEventsWithTotalDTO } from 'src/engine/core-modules/calendar/dtos/timeline-calendar-events-with-total.dto';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { CalendarChannelVisibility } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import { type CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
|
||||
import { type OpportunityWorkspaceEntity } from 'src/modules/opportunity/standard-objects/opportunity.workspace-entity';
|
||||
@@ -14,24 +14,29 @@ import { type PersonWorkspaceEntity } from 'src/modules/person/standard-objects/
|
||||
|
||||
@Injectable()
|
||||
export class TimelineCalendarEventService {
|
||||
constructor(private readonly twentyORMManager: TwentyORMManager) {}
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
// TODO: Align return type with the entities to avoid mapping
|
||||
async getCalendarEventsFromPersonIds({
|
||||
currentWorkspaceMemberId,
|
||||
personIds,
|
||||
workspaceId,
|
||||
page = 1,
|
||||
pageSize = TIMELINE_CALENDAR_EVENTS_DEFAULT_PAGE_SIZE,
|
||||
}: {
|
||||
currentWorkspaceMemberId: string;
|
||||
personIds: string[];
|
||||
workspaceId: string;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}): Promise<TimelineCalendarEventsWithTotalDTO> {
|
||||
const offset = (page - 1) * pageSize;
|
||||
|
||||
const calendarEventRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarEventWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarEventWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'calendarEvent',
|
||||
);
|
||||
|
||||
@@ -162,16 +167,19 @@ export class TimelineCalendarEventService {
|
||||
async getCalendarEventsFromCompanyId({
|
||||
currentWorkspaceMemberId,
|
||||
companyId,
|
||||
workspaceId,
|
||||
page = 1,
|
||||
pageSize = TIMELINE_CALENDAR_EVENTS_DEFAULT_PAGE_SIZE,
|
||||
}: {
|
||||
currentWorkspaceMemberId: string;
|
||||
companyId: string;
|
||||
workspaceId: string;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}): Promise<TimelineCalendarEventsWithTotalDTO> {
|
||||
const personRepository =
|
||||
await this.twentyORMManager.getRepository<PersonWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<PersonWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'person',
|
||||
);
|
||||
|
||||
@@ -196,6 +204,7 @@ export class TimelineCalendarEventService {
|
||||
const calendarEvents = await this.getCalendarEventsFromPersonIds({
|
||||
currentWorkspaceMemberId,
|
||||
personIds: formattedPersonIds,
|
||||
workspaceId,
|
||||
page,
|
||||
pageSize,
|
||||
});
|
||||
@@ -206,16 +215,19 @@ export class TimelineCalendarEventService {
|
||||
async getCalendarEventsFromOpportunityId({
|
||||
currentWorkspaceMemberId,
|
||||
opportunityId,
|
||||
workspaceId,
|
||||
page = 1,
|
||||
pageSize = TIMELINE_CALENDAR_EVENTS_DEFAULT_PAGE_SIZE,
|
||||
}: {
|
||||
currentWorkspaceMemberId: string;
|
||||
opportunityId: string;
|
||||
workspaceId: string;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}): Promise<TimelineCalendarEventsWithTotalDTO> {
|
||||
const opportunityRepository =
|
||||
await this.twentyORMManager.getRepository<OpportunityWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<OpportunityWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'opportunity',
|
||||
);
|
||||
|
||||
@@ -238,6 +250,7 @@ export class TimelineCalendarEventService {
|
||||
const calendarEvents = await this.getCalendarEventsFromCompanyId({
|
||||
currentWorkspaceMemberId,
|
||||
companyId: opportunity.companyId,
|
||||
workspaceId,
|
||||
page,
|
||||
pageSize,
|
||||
});
|
||||
|
||||
@@ -37,7 +37,6 @@ import { MessageQueueModule } from 'src/engine/core-modules/message-queue/messag
|
||||
import { messageQueueModuleFactory } from 'src/engine/core-modules/message-queue/message-queue.module-factory';
|
||||
import { TimelineMessagingModule } from 'src/engine/core-modules/messaging/timeline-messaging.module';
|
||||
import { OpenApiModule } from 'src/engine/core-modules/open-api/open-api.module';
|
||||
import { PageLayoutModule } from 'src/engine/metadata-modules/page-layout/page-layout.module';
|
||||
import { PostgresCredentialsModule } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.module';
|
||||
import { PublicDomainModule } from 'src/engine/core-modules/public-domain/public-domain.module';
|
||||
import { RedisClientModule } from 'src/engine/core-modules/redis-client/redis-client.module';
|
||||
@@ -56,12 +55,13 @@ import { WorkspaceInvitationModule } from 'src/engine/core-modules/workspace-inv
|
||||
import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.module';
|
||||
import { AiBillingModule } from 'src/engine/metadata-modules/ai/ai-billing/ai-billing.module';
|
||||
import { AiModelsModule } from 'src/engine/metadata-modules/ai/ai-models/ai-models.module';
|
||||
import { FlatPageLayoutTabModule } from 'src/engine/metadata-modules/flat-page-layout-tab/flat-page-layout-tab.module';
|
||||
import { RoleModule } from 'src/engine/metadata-modules/role/role.module';
|
||||
import { SubscriptionsModule } from 'src/engine/subscriptions/subscriptions.module';
|
||||
import { TrashCleanupModule } from 'src/engine/trash-cleanup/trash-cleanup.module';
|
||||
import { WorkspaceEventEmitterModule } from 'src/engine/workspace-event-emitter/workspace-event-emitter.module';
|
||||
import { ChannelSyncModule } from 'src/modules/connected-account/channel-sync/channel-sync.module';
|
||||
import { FlatPageLayoutTabModule } from 'src/engine/metadata-modules/flat-page-layout-tab/flat-page-layout-tab.module';
|
||||
import { PageLayoutModule } from 'src/engine/metadata-modules/page-layout/page-layout.module';
|
||||
|
||||
import { AuditModule } from './audit/audit.module';
|
||||
import { ClientConfigModule } from './client-config/client-config.module';
|
||||
|
||||
+14
-4
@@ -4,20 +4,21 @@ import { TIMELINE_THREADS_DEFAULT_PAGE_SIZE } from 'src/engine/core-modules/mess
|
||||
import { type TimelineThreadsWithTotalDTO } from 'src/engine/core-modules/messaging/dtos/timeline-threads-with-total.dto';
|
||||
import { TimelineMessagingService } from 'src/engine/core-modules/messaging/services/timeline-messaging.service';
|
||||
import { formatThreads } from 'src/engine/core-modules/messaging/utils/format-threads.util';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type OpportunityWorkspaceEntity } from 'src/modules/opportunity/standard-objects/opportunity.workspace-entity';
|
||||
import { type PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class GetMessagesService {
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly timelineMessagingService: TimelineMessagingService,
|
||||
) {}
|
||||
|
||||
async getMessagesFromPersonIds(
|
||||
workspaceMemberId: string,
|
||||
personIds: string[],
|
||||
workspaceId: string,
|
||||
page = 1,
|
||||
pageSize: number = TIMELINE_THREADS_DEFAULT_PAGE_SIZE,
|
||||
): Promise<TimelineThreadsWithTotalDTO> {
|
||||
@@ -26,6 +27,7 @@ export class GetMessagesService {
|
||||
const { messageThreads, totalNumberOfThreads } =
|
||||
await this.timelineMessagingService.getAndCountMessageThreads(
|
||||
personIds,
|
||||
workspaceId,
|
||||
offset,
|
||||
pageSize,
|
||||
);
|
||||
@@ -44,12 +46,14 @@ export class GetMessagesService {
|
||||
const threadParticipantsByThreadId =
|
||||
await this.timelineMessagingService.getThreadParticipantsByThreadId(
|
||||
messageThreadIds,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const threadVisibilityByThreadId =
|
||||
await this.timelineMessagingService.getThreadVisibilityByThreadId(
|
||||
messageThreadIds,
|
||||
workspaceMemberId,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -65,11 +69,13 @@ export class GetMessagesService {
|
||||
async getMessagesFromCompanyId(
|
||||
workspaceMemberId: string,
|
||||
companyId: string,
|
||||
workspaceId: string,
|
||||
page = 1,
|
||||
pageSize: number = TIMELINE_THREADS_DEFAULT_PAGE_SIZE,
|
||||
): Promise<TimelineThreadsWithTotalDTO> {
|
||||
const personRepository =
|
||||
await this.twentyORMManager.getRepository<PersonWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<PersonWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'person',
|
||||
);
|
||||
const personIds = (
|
||||
@@ -93,6 +99,7 @@ export class GetMessagesService {
|
||||
const messageThreads = await this.getMessagesFromPersonIds(
|
||||
workspaceMemberId,
|
||||
personIds,
|
||||
workspaceId,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
@@ -103,11 +110,13 @@ export class GetMessagesService {
|
||||
async getMessagesFromOpportunityId(
|
||||
workspaceMemberId: string,
|
||||
opportunityId: string,
|
||||
workspaceId: string,
|
||||
page = 1,
|
||||
pageSize: number = TIMELINE_THREADS_DEFAULT_PAGE_SIZE,
|
||||
): Promise<TimelineThreadsWithTotalDTO> {
|
||||
const opportunityRepository =
|
||||
await this.twentyORMManager.getRepository<OpportunityWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<OpportunityWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'opportunity',
|
||||
);
|
||||
|
||||
@@ -130,6 +139,7 @@ export class GetMessagesService {
|
||||
const messageThreads = await this.getMessagesFromCompanyId(
|
||||
workspaceMemberId,
|
||||
opportunity.companyId,
|
||||
workspaceId,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
+13
-5
@@ -3,17 +3,20 @@ import { Injectable } from '@nestjs/common';
|
||||
import { In } from 'typeorm';
|
||||
|
||||
import { type TimelineThreadDTO } from 'src/engine/core-modules/messaging/dtos/timeline-thread.dto';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { MessageChannelVisibility } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
|
||||
import { type MessageParticipantWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-participant.workspace-entity';
|
||||
import { type MessageThreadWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-thread.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class TimelineMessagingService {
|
||||
constructor(private readonly twentyORMManager: TwentyORMManager) {}
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
public async getAndCountMessageThreads(
|
||||
personIds: string[],
|
||||
workspaceId: string,
|
||||
offset: number,
|
||||
pageSize: number,
|
||||
): Promise<{
|
||||
@@ -28,7 +31,8 @@ export class TimelineMessagingService {
|
||||
totalNumberOfThreads: number;
|
||||
}> {
|
||||
const messageThreadRepository =
|
||||
await this.twentyORMManager.getRepository<MessageThreadWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageThreadWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageThread',
|
||||
);
|
||||
|
||||
@@ -87,11 +91,13 @@ export class TimelineMessagingService {
|
||||
|
||||
public async getThreadParticipantsByThreadId(
|
||||
messageThreadIds: string[],
|
||||
workspaceId: string,
|
||||
): Promise<{
|
||||
[key: string]: MessageParticipantWorkspaceEntity[];
|
||||
}> {
|
||||
const messageParticipantRepository =
|
||||
await this.twentyORMManager.getRepository<MessageParticipantWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageParticipantWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageParticipant',
|
||||
);
|
||||
|
||||
@@ -176,11 +182,13 @@ export class TimelineMessagingService {
|
||||
public async getThreadVisibilityByThreadId(
|
||||
messageThreadIds: string[],
|
||||
workspaceMemberId: string,
|
||||
workspaceId: string,
|
||||
): Promise<{
|
||||
[key: string]: MessageChannelVisibility;
|
||||
}> {
|
||||
const messageThreadRepository =
|
||||
await this.twentyORMManager.getRepository<MessageThreadWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageThreadWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageThread',
|
||||
);
|
||||
|
||||
|
||||
+3
@@ -93,6 +93,7 @@ export class TimelineMessagingResolver {
|
||||
await this.getMessagesFromPersonIdsService.getMessagesFromPersonIds(
|
||||
workspaceMember.id,
|
||||
[personId],
|
||||
workspace.id,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
@@ -119,6 +120,7 @@ export class TimelineMessagingResolver {
|
||||
await this.getMessagesFromPersonIdsService.getMessagesFromCompanyId(
|
||||
workspaceMember.id,
|
||||
companyId,
|
||||
workspace.id,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
@@ -146,6 +148,7 @@ export class TimelineMessagingResolver {
|
||||
await this.getMessagesFromPersonIdsService.getMessagesFromOpportunityId(
|
||||
workspaceMember.id,
|
||||
opportunityId,
|
||||
workspace.id,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ import {
|
||||
} from 'src/engine/core-modules/__mocks__/mockFlatObjectMetadatas';
|
||||
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
||||
import { SearchService } from 'src/engine/core-modules/search/services/search.service';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
describe('SearchService', () => {
|
||||
@@ -17,7 +17,7 @@ describe('SearchService', () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
SearchService,
|
||||
{ provide: TwentyORMManager, useValue: {} },
|
||||
{ provide: TwentyORMGlobalManager, useValue: {} },
|
||||
{ provide: WorkspaceCacheStorageService, useValue: {} },
|
||||
{ provide: FileService, useValue: {} },
|
||||
],
|
||||
|
||||
@@ -4,9 +4,16 @@ import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||
import { SearchResolver } from 'src/engine/core-modules/search/search.resolver';
|
||||
import { SearchService } from 'src/engine/core-modules/search/services/search.service';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module';
|
||||
|
||||
@Module({
|
||||
imports: [FileModule, WorkspaceManyOrAllFlatEntityMapsCacheModule],
|
||||
imports: [
|
||||
FileModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
UserRoleModule,
|
||||
PermissionsModule,
|
||||
],
|
||||
providers: [SearchResolver, SearchService],
|
||||
})
|
||||
export class SearchModule {}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Args, Query, Resolver } from '@nestjs/graphql';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ApiKeyRoleService } from 'src/engine/core-modules/api-key/services/api-key-role.service';
|
||||
import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter';
|
||||
import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe';
|
||||
import { SearchArgs } from 'src/engine/core-modules/search/dtos/search-args';
|
||||
@@ -10,10 +11,14 @@ import { SearchResultConnectionDTO } from 'src/engine/core-modules/search/dtos/s
|
||||
import { SearchApiExceptionFilter } from 'src/engine/core-modules/search/filters/search-api-exception.filter';
|
||||
import { SearchService } from 'src/engine/core-modules/search/services/search.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthApiKey } from 'src/engine/decorators/auth/auth-api-key.decorator';
|
||||
import { AuthUserWorkspaceId } from 'src/engine/decorators/auth/auth-user-workspace-id.decorator';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { CustomPermissionGuard } from 'src/engine/guards/custom-permission.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
|
||||
@Resolver()
|
||||
@UseFilters(SearchApiExceptionFilter, PreventNestToAutoLogGraphqlErrorsFilter)
|
||||
@@ -23,11 +28,15 @@ export class SearchResolver {
|
||||
constructor(
|
||||
private readonly searchService: SearchService,
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly userRoleService: UserRoleService,
|
||||
private readonly apiKeyRoleService: ApiKeyRoleService,
|
||||
) {}
|
||||
|
||||
@Query(() => SearchResultConnectionDTO)
|
||||
async search(
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
@AuthUserWorkspaceId() userWorkspaceId: string | undefined,
|
||||
@AuthApiKey() apiKey: string | undefined,
|
||||
@Args()
|
||||
{
|
||||
searchInput,
|
||||
@@ -57,6 +66,29 @@ export class SearchResolver {
|
||||
excludedObjectNameSingulars: excludedObjectNameSingulars ?? [],
|
||||
});
|
||||
|
||||
// TODO: move to a service
|
||||
let rolePermissionConfig: RolePermissionConfig | undefined;
|
||||
|
||||
if (isDefined(apiKey)) {
|
||||
const roleId = await this.apiKeyRoleService.getRoleIdForApiKey(
|
||||
apiKey,
|
||||
workspace.id,
|
||||
);
|
||||
|
||||
if (isDefined(roleId)) {
|
||||
rolePermissionConfig = { unionOf: [roleId] };
|
||||
}
|
||||
} else if (isDefined(userWorkspaceId)) {
|
||||
const roleId = await this.userRoleService.getRoleIdForUserWorkspace({
|
||||
userWorkspaceId,
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
if (isDefined(roleId)) {
|
||||
rolePermissionConfig = { unionOf: [roleId] };
|
||||
}
|
||||
}
|
||||
|
||||
const allRecordsWithObjectMetadataItems =
|
||||
await this.searchService.getAllRecordsWithObjectMetadataItems({
|
||||
flatObjectMetadatas: filteredObjectMetadataItems,
|
||||
@@ -67,6 +99,8 @@ export class SearchResolver {
|
||||
includedObjectNameSingulars,
|
||||
excludedObjectNameSingulars,
|
||||
after,
|
||||
workspaceId: workspace.id,
|
||||
rolePermissionConfig,
|
||||
});
|
||||
|
||||
return this.searchService.computeSearchObjectResults({
|
||||
|
||||
@@ -31,7 +31,8 @@ import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-m
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
|
||||
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
|
||||
type LastRanks = { tsRankCD: number; tsRank: number };
|
||||
|
||||
@@ -45,7 +46,7 @@ const OBJECT_METADATA_ITEMS_CHUNK_SIZE = 5;
|
||||
@Injectable()
|
||||
export class SearchService {
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly fileService: FileService,
|
||||
) {}
|
||||
|
||||
@@ -58,9 +59,13 @@ export class SearchService {
|
||||
limit,
|
||||
filter,
|
||||
after,
|
||||
workspaceId,
|
||||
rolePermissionConfig,
|
||||
}: {
|
||||
flatObjectMetadatas: FlatObjectMetadata[];
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
workspaceId: string;
|
||||
rolePermissionConfig?: RolePermissionConfig;
|
||||
} & SearchArgs) {
|
||||
const filteredObjectMetadataItems = this.filterObjectMetadataItems({
|
||||
flatObjectMetadatas,
|
||||
@@ -80,8 +85,10 @@ export class SearchService {
|
||||
const recordsWithObjectMetadataItems = await Promise.all(
|
||||
objectMetadataItemChunk.map(async (flatObjectMetadata) => {
|
||||
const repository =
|
||||
await this.twentyORMManager.getRepository<ObjectRecord>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ObjectRecord>(
|
||||
workspaceId,
|
||||
flatObjectMetadata.nameSingular,
|
||||
rolePermissionConfig,
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
+2
-2
@@ -173,7 +173,7 @@ export class ToolProviderService {
|
||||
description: tool.description,
|
||||
inputSchema: tool.inputSchema,
|
||||
execute: async (parameters: { input: ToolInput }) =>
|
||||
tool.execute(parameters.input),
|
||||
tool.execute(parameters.input, spec.workspaceId),
|
||||
};
|
||||
} else if (spec.rolePermissionConfig && spec.workspaceId) {
|
||||
const hasPermission = await this.permissionsService.hasToolPermission(
|
||||
@@ -187,7 +187,7 @@ export class ToolProviderService {
|
||||
description: tool.description,
|
||||
inputSchema: tool.inputSchema,
|
||||
execute: async (parameters: { input: ToolInput }) =>
|
||||
tool.execute(parameters.input),
|
||||
tool.execute(parameters.input, spec.workspaceId),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -26,7 +26,8 @@ export class ToolRegistryService {
|
||||
return {
|
||||
description: httpTool.description,
|
||||
inputSchema: httpTool.inputSchema,
|
||||
execute: (params) => httpTool.execute(params),
|
||||
execute: (params, workspaceId) =>
|
||||
httpTool.execute(params, workspaceId),
|
||||
flag: PermissionFlagType.HTTP_REQUEST_TOOL,
|
||||
};
|
||||
},
|
||||
@@ -36,8 +37,8 @@ export class ToolRegistryService {
|
||||
() => ({
|
||||
description: this.sendEmailTool.description,
|
||||
inputSchema: this.sendEmailTool.inputSchema,
|
||||
execute: (params) =>
|
||||
this.sendEmailTool.execute(params as SendEmailInput),
|
||||
execute: (params, workspaceId) =>
|
||||
this.sendEmailTool.execute(params as SendEmailInput, workspaceId),
|
||||
flag: PermissionFlagType.SEND_EMAIL_TOOL,
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -20,7 +20,10 @@ export class HttpTool implements Tool {
|
||||
|
||||
constructor(private readonly twentyConfigService: TwentyConfigService) {}
|
||||
|
||||
async execute(parameters: ToolInput): Promise<ToolOutput> {
|
||||
async execute(
|
||||
parameters: ToolInput,
|
||||
_workspaceId: string,
|
||||
): Promise<ToolOutput> {
|
||||
const { url, method, headers, body } = parameters as HttpRequestInput;
|
||||
const headersCopy = { ...headers };
|
||||
const isMethodForBody = ['POST', 'PUT', 'PATCH'].includes(method);
|
||||
|
||||
+4
-12
@@ -18,7 +18,6 @@ import { SendEmailToolParametersZodSchema } from 'src/engine/core-modules/tool/t
|
||||
import { type SendEmailInput } from 'src/engine/core-modules/tool/tools/send-email-tool/types/send-email-input.type';
|
||||
import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type';
|
||||
import { type Tool } from 'src/engine/core-modules/tool/types/tool.type';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import { MessagingSendMessageService } from 'src/modules/messaging/message-import-manager/services/messaging-send-message.service';
|
||||
@@ -36,7 +35,6 @@ export class SendEmailTool implements Tool {
|
||||
inputSchema = SendEmailToolParametersZodSchema;
|
||||
|
||||
constructor(
|
||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly sendMessageService: MessagingSendMessageService,
|
||||
@InjectRepository(FileEntity)
|
||||
@@ -160,9 +158,10 @@ export class SendEmailTool implements Tool {
|
||||
return attachments;
|
||||
}
|
||||
|
||||
async execute(parameters: SendEmailInput): Promise<ToolOutput> {
|
||||
const { workspaceId } = this.scopedWorkspaceContextFactory.create();
|
||||
|
||||
async execute(
|
||||
parameters: SendEmailInput,
|
||||
workspaceId: string,
|
||||
): Promise<ToolOutput> {
|
||||
const { email, subject, body, files } = parameters;
|
||||
let { connectedAccountId } = parameters;
|
||||
|
||||
@@ -180,13 +179,6 @@ export class SendEmailTool implements Tool {
|
||||
);
|
||||
}
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new SendEmailToolException(
|
||||
'Workspace ID not found',
|
||||
SendEmailToolExceptionCode.WORKSPACE_ID_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (!connectedAccountId) {
|
||||
connectedAccountId =
|
||||
await this.getOrThrowFirstConnectedAccountId(workspaceId);
|
||||
|
||||
@@ -7,6 +7,6 @@ import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.
|
||||
export type Tool = {
|
||||
description: string;
|
||||
inputSchema: FlexibleSchema<unknown>;
|
||||
execute(input: ToolInput): Promise<ToolOutput>;
|
||||
execute(input: ToolInput, workspaceId: string): Promise<ToolOutput>;
|
||||
flag?: PermissionFlagType;
|
||||
};
|
||||
|
||||
+1
@@ -141,6 +141,7 @@ export class WorkflowTriggerController {
|
||||
name: 'Webhook',
|
||||
context: {},
|
||||
},
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
+10
-1
@@ -43,21 +43,25 @@ export class WorkflowTriggerResolver {
|
||||
|
||||
@Mutation(() => Boolean)
|
||||
async activateWorkflowVersion(
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
@Args('workflowVersionId', { type: () => UUIDScalarType })
|
||||
workflowVersionId: string,
|
||||
) {
|
||||
return this.workflowTriggerWorkspaceService.activateWorkflowVersion(
|
||||
workflowVersionId,
|
||||
workspace.id,
|
||||
);
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean)
|
||||
async deactivateWorkflowVersion(
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
@Args('workflowVersionId', { type: () => UUIDScalarType })
|
||||
workflowVersionId: string,
|
||||
) {
|
||||
return this.workflowTriggerWorkspaceService.deactivateWorkflowVersion(
|
||||
workflowVersionId,
|
||||
workspace.id,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -92,14 +96,19 @@ export class WorkflowTriggerResolver {
|
||||
},
|
||||
workspaceMemberId: workspaceMember.id,
|
||||
}),
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => WorkflowRunDTO)
|
||||
async stopWorkflowRun(
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
@Args('workflowRunId', { type: () => UUIDScalarType })
|
||||
workflowRunId: string,
|
||||
) {
|
||||
return this.workflowTriggerWorkspaceService.stopWorkflowRun(workflowRunId);
|
||||
return this.workflowTriggerWorkspaceService.stopWorkflowRun(
|
||||
workflowRunId,
|
||||
workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-6
@@ -150,14 +150,18 @@ export class WorkflowVersionStepResolver {
|
||||
|
||||
@Mutation(() => TestHttpRequestOutput)
|
||||
async testHttpRequest(
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
@Args('input')
|
||||
{ url, method, headers, body }: TestHttpRequestInput,
|
||||
): Promise<TestHttpRequestOutput> {
|
||||
return this.httpTool.execute({
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
});
|
||||
return this.httpTool.execute(
|
||||
{
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
},
|
||||
workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,10 +23,10 @@ import { AGENT_SYSTEM_PROMPTS } from 'src/engine/metadata-modules/ai/ai-agent/co
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { repairToolCall } from 'src/engine/metadata-modules/ai/ai-agent/utils/repair-tool-call.util';
|
||||
import { AI_TELEMETRY_CONFIG } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-telemetry.const';
|
||||
import { AgentModelConfigService } from 'src/engine/metadata-modules/ai/ai-models/services/agent-model-config.service';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service';
|
||||
import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
|
||||
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
import { AgentModelConfigService } from 'src/engine/metadata-modules/ai/ai-models/services/agent-model-config.service';
|
||||
|
||||
// Agent execution within workflows uses database and action tools only.
|
||||
// Workflow tools are intentionally excluded to avoid circular dependencies
|
||||
|
||||
+12
-22
@@ -85,38 +85,28 @@ describe('ToolAdapterService', () => {
|
||||
service = moduleRef.get(ToolAdapterService);
|
||||
});
|
||||
|
||||
it('should include unflagged tools regardless of role/workspace', async () => {
|
||||
const toolsNoContext = await service.getTools();
|
||||
it('should include unflagged tools regardless of rolePermissionConfig', async () => {
|
||||
const toolsNoContext = await service.getTools('ws-1');
|
||||
|
||||
expect(Object.keys(toolsNoContext)).toContain('http_request');
|
||||
|
||||
const toolsWithPartialContext = await service.getTools({
|
||||
const toolsWithPartialContext = await service.getTools('ws-1', {
|
||||
unionOf: ['role-1'],
|
||||
});
|
||||
|
||||
expect(Object.keys(toolsWithPartialContext)).toContain('http_request');
|
||||
});
|
||||
|
||||
it('should not include flagged tools when role/workspace are missing', async () => {
|
||||
const toolsNoContext = await service.getTools();
|
||||
it('should not include flagged tools when rolePermissionConfig is missing', async () => {
|
||||
const toolsNoRoleConfig = await service.getTools('ws-1');
|
||||
|
||||
expect(Object.keys(toolsNoContext)).not.toContain('send_email');
|
||||
|
||||
const toolsRoleOnly = await service.getTools({
|
||||
unionOf: ['role-1'],
|
||||
});
|
||||
|
||||
expect(Object.keys(toolsRoleOnly)).not.toContain('send_email');
|
||||
|
||||
const toolsWorkspaceOnly = await service.getTools(undefined, 'ws-1');
|
||||
|
||||
expect(Object.keys(toolsWorkspaceOnly)).not.toContain('send_email');
|
||||
expect(Object.keys(toolsNoRoleConfig)).not.toContain('send_email');
|
||||
});
|
||||
|
||||
it('should include flagged tools when permission is granted', async () => {
|
||||
mockPermissions.hasToolPermission.mockResolvedValueOnce(true);
|
||||
|
||||
const tools = await service.getTools({ unionOf: ['role-1'] }, 'ws-1');
|
||||
const tools = await service.getTools('ws-1', { unionOf: ['role-1'] });
|
||||
|
||||
expect(mockPermissions.hasToolPermission).toHaveBeenCalledWith(
|
||||
{ unionOf: ['role-1'] },
|
||||
@@ -130,13 +120,13 @@ describe('ToolAdapterService', () => {
|
||||
it('should exclude flagged tools when permission is denied', async () => {
|
||||
mockPermissions.hasToolPermission.mockResolvedValueOnce(false);
|
||||
|
||||
const tools = await service.getTools({ unionOf: ['role-1'] }, 'ws-1');
|
||||
const tools = await service.getTools('ws-1', { unionOf: ['role-1'] });
|
||||
|
||||
expect(Object.keys(tools)).not.toContain('send_email');
|
||||
});
|
||||
|
||||
it('should lowercase tool type keys in the returned ToolSet', async () => {
|
||||
const tools = await service.getTools();
|
||||
const tools = await service.getTools('ws-1');
|
||||
|
||||
const keys = Object.keys(tools);
|
||||
|
||||
@@ -145,7 +135,7 @@ describe('ToolAdapterService', () => {
|
||||
});
|
||||
|
||||
it('should forward execute input correctly and return underlying result', async () => {
|
||||
const tools = await service.getTools();
|
||||
const tools = await service.getTools('ws-1');
|
||||
|
||||
const input = { url: 'https://example.com', method: 'GET' } as ToolInput;
|
||||
const result = await tools['http_request'].execute?.(
|
||||
@@ -161,8 +151,8 @@ describe('ToolAdapterService', () => {
|
||||
},
|
||||
);
|
||||
|
||||
// Ensure wrapper forwards only parameters.input
|
||||
expect(unflaggedToolExecute).toHaveBeenCalledWith(input);
|
||||
// Ensure wrapper forwards parameters.input and workspaceId
|
||||
expect(unflaggedToolExecute).toHaveBeenCalledWith(input, 'ws-1');
|
||||
expect(result).toEqual({
|
||||
success: true,
|
||||
message: 'Tool executed successfully',
|
||||
|
||||
+6
-6
@@ -17,8 +17,8 @@ export class ToolAdapterService {
|
||||
) {}
|
||||
|
||||
async getTools(
|
||||
workspaceId: string,
|
||||
rolePermissionConfig?: RolePermissionConfig,
|
||||
workspaceId?: string,
|
||||
): Promise<ToolSet> {
|
||||
const tools: ToolSet = {};
|
||||
|
||||
@@ -26,8 +26,8 @@ export class ToolAdapterService {
|
||||
const tool = this.toolRegistry.getTool(toolType);
|
||||
|
||||
if (!tool.flag) {
|
||||
tools[toolType.toLowerCase()] = this.createToolSet(tool);
|
||||
} else if (rolePermissionConfig && workspaceId) {
|
||||
tools[toolType.toLowerCase()] = this.createToolSet(tool, workspaceId);
|
||||
} else if (rolePermissionConfig) {
|
||||
const hasPermission = await this.permissionsService.hasToolPermission(
|
||||
rolePermissionConfig,
|
||||
workspaceId,
|
||||
@@ -35,7 +35,7 @@ export class ToolAdapterService {
|
||||
);
|
||||
|
||||
if (hasPermission) {
|
||||
tools[toolType.toLowerCase()] = this.createToolSet(tool);
|
||||
tools[toolType.toLowerCase()] = this.createToolSet(tool, workspaceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,12 +43,12 @@ export class ToolAdapterService {
|
||||
return tools;
|
||||
}
|
||||
|
||||
private createToolSet(tool: Tool) {
|
||||
private createToolSet(tool: Tool, workspaceId: string) {
|
||||
return {
|
||||
description: tool.description,
|
||||
inputSchema: tool.inputSchema,
|
||||
execute: async (parameters: { input: ToolInput }) =>
|
||||
tool.execute(parameters.input),
|
||||
tool.execute(parameters.input, workspaceId),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { FlatPageLayoutTabModule } from 'src/engine/metadata-modules/flat-page-layout-tab/flat-page-layout-tab.module';
|
||||
import { FlatPageLayoutWidgetModule } from 'src/engine/metadata-modules/flat-page-layout-widget/flat-page-layout-widget.module';
|
||||
import { PageLayoutTabController } from 'src/engine/metadata-modules/page-layout/controllers/page-layout-tab.controller';
|
||||
import { PageLayoutWidgetController } from 'src/engine/metadata-modules/page-layout/controllers/page-layout-widget.controller';
|
||||
import { PageLayoutController } from 'src/engine/metadata-modules/page-layout/controllers/page-layout.controller';
|
||||
@@ -36,6 +38,8 @@ import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspa
|
||||
WorkspaceCacheStorageModule,
|
||||
WorkspaceMigrationV2Module,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
FlatPageLayoutTabModule,
|
||||
FlatPageLayoutWidgetModule,
|
||||
],
|
||||
controllers: [
|
||||
PageLayoutController,
|
||||
|
||||
+1
-1
@@ -28,6 +28,6 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache
|
||||
RoleTargetModule,
|
||||
],
|
||||
providers: [ApiKeyRoleService, PermissionsService],
|
||||
exports: [PermissionsService],
|
||||
exports: [PermissionsService, ApiKeyRoleService],
|
||||
})
|
||||
export class PermissionsModule {}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { EntitySchemaColumnFactory } from 'src/engine/twenty-orm/factories/entity-schema-column.factory';
|
||||
import { EntitySchemaRelationFactory } from 'src/engine/twenty-orm/factories/entity-schema-relation.factory';
|
||||
import { EntitySchemaFactory } from 'src/engine/twenty-orm/factories/entity-schema.factory';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { WorkspaceDatasourceFactory } from 'src/engine/twenty-orm/factories/workspace-datasource.factory';
|
||||
|
||||
export const entitySchemaFactories = [
|
||||
@@ -9,5 +8,4 @@ export const entitySchemaFactories = [
|
||||
EntitySchemaRelationFactory,
|
||||
EntitySchemaFactory,
|
||||
WorkspaceDatasourceFactory,
|
||||
ScopedWorkspaceContextFactory,
|
||||
];
|
||||
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
import { Inject, Injectable, Optional, Scope } from '@nestjs/common';
|
||||
import { REQUEST } from '@nestjs/core';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class ScopedWorkspaceContextFactory {
|
||||
constructor(
|
||||
@Optional()
|
||||
@Inject(REQUEST)
|
||||
private readonly request: Request | null,
|
||||
) {}
|
||||
|
||||
public create(): {
|
||||
workspaceId: string | null;
|
||||
userWorkspaceId: string | null;
|
||||
isExecutedByApiKey: boolean;
|
||||
apiKeyId: string | null;
|
||||
} {
|
||||
const workspaceId: string | undefined =
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
this.request?.['req']?.['workspaceId'] ||
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
this.request?.['params']?.['workspaceId'] ||
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
this.request?.['workspace']?.['id']; // rest api
|
||||
|
||||
// Extract API key ID (same pattern as userWorkspaceId)
|
||||
const apiKey =
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
this.request?.['req']?.['apiKey'] ||
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
this.request?.['apiKey']; // rest api
|
||||
|
||||
return {
|
||||
workspaceId: workspaceId ?? null,
|
||||
userWorkspaceId:
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
this.request?.['req']?.['userWorkspaceId'] ??
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
this.request?.['userWorkspaceId'] ?? // rest api
|
||||
null,
|
||||
isExecutedByApiKey: !!apiKey,
|
||||
apiKeyId: apiKey?.id ?? null,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
import { Injectable, type Type } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type ObjectLiteral, Repository } from 'typeorm';
|
||||
|
||||
import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { WorkspaceDatasourceFactory } from 'src/engine/twenty-orm/factories/workspace-datasource.factory';
|
||||
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { convertClassNameToObjectMetadataName } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/convert-class-to-object-metadata-name.util';
|
||||
|
||||
@Injectable()
|
||||
export class TwentyORMManager {
|
||||
constructor(
|
||||
@InjectRepository(RoleTargetEntity)
|
||||
private readonly roleTargetRepository: Repository<RoleTargetEntity>,
|
||||
private readonly workspaceDataSourceFactory: WorkspaceDatasourceFactory,
|
||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||
) {}
|
||||
|
||||
async getRepository<T extends ObjectLiteral>(
|
||||
workspaceEntity: Type<T>,
|
||||
): Promise<WorkspaceRepository<T>>;
|
||||
|
||||
async getRepository<T extends ObjectLiteral>(
|
||||
objectMetadataName: string,
|
||||
): Promise<WorkspaceRepository<T>>;
|
||||
|
||||
async getRepository<T extends ObjectLiteral>(
|
||||
workspaceEntityOrObjectMetadataName: Type<T> | string,
|
||||
): Promise<WorkspaceRepository<T>> {
|
||||
const { workspaceId, userWorkspaceId, apiKeyId } =
|
||||
this.scopedWorkspaceContextFactory.create();
|
||||
|
||||
let objectMetadataName: string;
|
||||
|
||||
if (typeof workspaceEntityOrObjectMetadataName === 'string') {
|
||||
objectMetadataName = workspaceEntityOrObjectMetadataName;
|
||||
} else {
|
||||
objectMetadataName = convertClassNameToObjectMetadataName(
|
||||
workspaceEntityOrObjectMetadataName.name,
|
||||
);
|
||||
}
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new Error('Workspace not found');
|
||||
}
|
||||
|
||||
const workspaceDataSource =
|
||||
await this.workspaceDataSourceFactory.create(workspaceId);
|
||||
|
||||
let roleId: string | undefined;
|
||||
|
||||
if (isDefined(userWorkspaceId)) {
|
||||
const roleTarget = await this.roleTargetRepository.findOne({
|
||||
where: {
|
||||
userWorkspaceId,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
roleId = roleTarget?.roleId;
|
||||
} else if (isDefined(apiKeyId)) {
|
||||
const roleTarget = await this.roleTargetRepository.findOne({
|
||||
where: {
|
||||
apiKeyId,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
roleId = roleTarget?.roleId;
|
||||
}
|
||||
|
||||
return workspaceDataSource.getRepository<T>(
|
||||
objectMetadataName,
|
||||
roleId ? { unionOf: [roleId] } : undefined,
|
||||
);
|
||||
}
|
||||
|
||||
async getDatasource() {
|
||||
const { workspaceId } = this.scopedWorkspaceContextFactory.create();
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new Error('Workspace not found');
|
||||
}
|
||||
|
||||
return this.workspaceDataSourceFactory.create(workspaceId);
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,7 @@ import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-t
|
||||
import { WorkspaceFeatureFlagsMapCacheModule } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.module';
|
||||
import { entitySchemaFactories } from 'src/engine/twenty-orm/factories';
|
||||
import { EntitySchemaFactory } from 'src/engine/twenty-orm/factories/entity-schema.factory';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
|
||||
|
||||
@@ -38,17 +36,7 @@ import { PgPoolSharedModule } from './pg-shared-pool/pg-shared-pool.module';
|
||||
PgPoolSharedModule,
|
||||
WorkspaceCacheModule,
|
||||
],
|
||||
providers: [
|
||||
...entitySchemaFactories,
|
||||
TwentyORMManager,
|
||||
TwentyORMGlobalManager,
|
||||
],
|
||||
exports: [
|
||||
EntitySchemaFactory,
|
||||
TwentyORMManager,
|
||||
TwentyORMGlobalManager,
|
||||
PgPoolSharedModule,
|
||||
ScopedWorkspaceContextFactory,
|
||||
],
|
||||
providers: [...entitySchemaFactories, TwentyORMGlobalManager],
|
||||
exports: [EntitySchemaFactory, TwentyORMGlobalManager, PgPoolSharedModule],
|
||||
})
|
||||
export class TwentyORMModule {}
|
||||
|
||||
+8
-5
@@ -7,7 +7,7 @@ import { type ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emit
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event-batch.type';
|
||||
import { type BlocklistWorkspaceEntity } from 'src/modules/blocklist/standard-objects/blocklist.workspace-entity';
|
||||
import { CalendarEventCleanerService } from 'src/modules/calendar/calendar-event-cleaner/services/calendar-event-cleaner.service';
|
||||
@@ -24,7 +24,7 @@ export type BlocklistItemDeleteCalendarEventsJobData = WorkspaceEventBatch<
|
||||
})
|
||||
export class BlocklistItemDeleteCalendarEventsJob {
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly calendarEventCleanerService: CalendarEventCleanerService,
|
||||
) {}
|
||||
|
||||
@@ -37,7 +37,8 @@ export class BlocklistItemDeleteCalendarEventsJob {
|
||||
);
|
||||
|
||||
const blocklistRepository =
|
||||
await this.twentyORMManager.getRepository<BlocklistWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<BlocklistWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'blocklist',
|
||||
);
|
||||
|
||||
@@ -67,12 +68,14 @@ export class BlocklistItemDeleteCalendarEventsJob {
|
||||
);
|
||||
|
||||
const calendarChannelRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'calendarChannel',
|
||||
);
|
||||
|
||||
const calendarChannelEventAssociationRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarChannelEventAssociationWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelEventAssociationWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'calendarChannelEventAssociation',
|
||||
);
|
||||
|
||||
|
||||
+4
-3
@@ -6,7 +6,7 @@ import { type ObjectRecordDeleteEvent } from 'src/engine/core-modules/event-emit
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event-batch.type';
|
||||
import { type BlocklistWorkspaceEntity } from 'src/modules/blocklist/standard-objects/blocklist.workspace-entity';
|
||||
import { CalendarChannelSyncStatusService } from 'src/modules/calendar/common/services/calendar-channel-sync-status.service';
|
||||
@@ -25,7 +25,7 @@ export type BlocklistReimportCalendarEventsJobData = WorkspaceEventBatch<
|
||||
})
|
||||
export class BlocklistReimportCalendarEventsJob {
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly calendarChannelSyncStatusService: CalendarChannelSyncStatusService,
|
||||
) {}
|
||||
|
||||
@@ -34,7 +34,8 @@ export class BlocklistReimportCalendarEventsJob {
|
||||
const workspaceId = data.workspaceId;
|
||||
|
||||
const calendarChannelRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'calendarChannel',
|
||||
);
|
||||
|
||||
|
||||
+8
-3
@@ -2,16 +2,21 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { Any, IsNull } from 'typeorm';
|
||||
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { deleteUsingPagination } from 'src/modules/messaging/message-cleaner/utils/delete-using-pagination.util';
|
||||
|
||||
@Injectable()
|
||||
export class CalendarEventCleanerService {
|
||||
constructor(private readonly twentyORMManager: TwentyORMManager) {}
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
public async cleanWorkspaceCalendarEvents(workspaceId: string) {
|
||||
const calendarEventRepository =
|
||||
await this.twentyORMManager.getRepository('calendarEvent');
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
|
||||
workspaceId,
|
||||
'calendarEvent',
|
||||
);
|
||||
|
||||
await deleteUsingPagination(
|
||||
workspaceId,
|
||||
|
||||
+4
-3
@@ -3,7 +3,7 @@ import { Scope } from '@nestjs/common';
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { CalendarFetchEventsService } from 'src/modules/calendar/calendar-event-import-manager/services/calendar-fetch-events.service';
|
||||
import { CalendarChannelSyncStatusService } from 'src/modules/calendar/common/services/calendar-channel-sync-status.service';
|
||||
import {
|
||||
@@ -23,7 +23,7 @@ export type CalendarEventListFetchJobData = {
|
||||
})
|
||||
export class CalendarEventListFetchJob {
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly calendarChannelSyncStatusService: CalendarChannelSyncStatusService,
|
||||
private readonly calendarFetchEventsService: CalendarFetchEventsService,
|
||||
) {}
|
||||
@@ -33,7 +33,8 @@ export class CalendarEventListFetchJob {
|
||||
const { workspaceId, calendarChannelId } = data;
|
||||
|
||||
const calendarChannelRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'calendarChannel',
|
||||
);
|
||||
|
||||
|
||||
+4
-3
@@ -3,7 +3,7 @@ import { Scope } from '@nestjs/common';
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { CalendarEventsImportService } from 'src/modules/calendar/calendar-event-import-manager/services/calendar-events-import.service';
|
||||
import { CalendarChannelSyncStatusService } from 'src/modules/calendar/common/services/calendar-channel-sync-status.service';
|
||||
import {
|
||||
@@ -25,7 +25,7 @@ export class CalendarEventsImportJob {
|
||||
constructor(
|
||||
private readonly calendarEventsImportService: CalendarEventsImportService,
|
||||
private readonly calendarChannelSyncStatusService: CalendarChannelSyncStatusService,
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
@Process(CalendarEventsImportJob.name)
|
||||
@@ -33,7 +33,8 @@ export class CalendarEventsImportJob {
|
||||
const { calendarChannelId, workspaceId } = data;
|
||||
|
||||
const calendarChannelRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'calendarChannel',
|
||||
);
|
||||
const calendarChannel = await calendarChannelRepository.findOne({
|
||||
|
||||
+4
-3
@@ -5,7 +5,7 @@ import { In } from 'typeorm';
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { isSyncStale } from 'src/modules/calendar/calendar-event-import-manager/utils/is-sync-stale.util';
|
||||
import { CalendarChannelSyncStatusService } from 'src/modules/calendar/common/services/calendar-channel-sync-status.service';
|
||||
import {
|
||||
@@ -24,7 +24,7 @@ export type CalendarOngoingStaleJobData = {
|
||||
export class CalendarOngoingStaleJob {
|
||||
private readonly logger = new Logger(CalendarOngoingStaleJob.name);
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly calendarChannelSyncStatusService: CalendarChannelSyncStatusService,
|
||||
) {}
|
||||
|
||||
@@ -33,7 +33,8 @@ export class CalendarOngoingStaleJob {
|
||||
const { workspaceId } = data;
|
||||
|
||||
const calendarChannelRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'calendarChannel',
|
||||
);
|
||||
|
||||
|
||||
+4
-3
@@ -5,7 +5,7 @@ import {
|
||||
type TwentyORMException,
|
||||
TwentyORMExceptionCode,
|
||||
} from 'src/engine/twenty-orm/exceptions/twenty-orm.exception';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { CALENDAR_THROTTLE_MAX_ATTEMPTS } from 'src/modules/calendar/calendar-event-import-manager/constants/calendar-throttle-max-attempts';
|
||||
import {
|
||||
type CalendarEventImportDriverException,
|
||||
@@ -28,7 +28,7 @@ export class CalendarEventImportErrorHandlerService {
|
||||
CalendarEventImportErrorHandlerService.name,
|
||||
);
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly calendarChannelSyncStatusService: CalendarChannelSyncStatusService,
|
||||
private readonly exceptionHandlerService: ExceptionHandlerService,
|
||||
) {}
|
||||
@@ -131,7 +131,8 @@ export class CalendarEventImportErrorHandlerService {
|
||||
}
|
||||
|
||||
const calendarChannelRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'calendarChannel',
|
||||
);
|
||||
|
||||
|
||||
+4
-3
@@ -7,7 +7,7 @@ import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decora
|
||||
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
|
||||
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
|
||||
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { BlocklistRepository } from 'src/modules/blocklist/repositories/blocklist.repository';
|
||||
import { BlocklistWorkspaceEntity } from 'src/modules/blocklist/standard-objects/blocklist.workspace-entity';
|
||||
import { CalendarEventCleanerService } from 'src/modules/calendar/calendar-event-cleaner/services/calendar-event-cleaner.service';
|
||||
@@ -34,7 +34,7 @@ export class CalendarEventsImportService {
|
||||
constructor(
|
||||
@InjectCacheStorage(CacheStorageNamespace.ModuleCalendar)
|
||||
private readonly cacheStorage: CacheStorageService,
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
@InjectObjectMetadataRepository(BlocklistWorkspaceEntity)
|
||||
private readonly blocklistRepository: BlocklistRepository,
|
||||
private readonly calendarEventCleanerService: CalendarEventCleanerService,
|
||||
@@ -137,7 +137,8 @@ export class CalendarEventsImportService {
|
||||
);
|
||||
}
|
||||
const calendarChannelEventAssociationRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarChannelEventAssociationWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelEventAssociationWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'calendarChannelEventAssociation',
|
||||
);
|
||||
|
||||
|
||||
+4
-3
@@ -5,7 +5,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decorators/cache-storage.decorator';
|
||||
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
|
||||
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import {
|
||||
CalendarEventImportDriverException,
|
||||
CalendarEventImportDriverExceptionCode,
|
||||
@@ -27,7 +27,7 @@ export class CalendarFetchEventsService {
|
||||
constructor(
|
||||
@InjectCacheStorage(CacheStorageNamespace.ModuleCalendar)
|
||||
private readonly cacheStorage: CacheStorageService,
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly calendarChannelSyncStatusService: CalendarChannelSyncStatusService,
|
||||
private readonly getCalendarEventsService: CalendarGetCalendarEventsService,
|
||||
private readonly calendarEventImportErrorHandlerService: CalendarEventImportErrorHandlerService,
|
||||
@@ -83,7 +83,8 @@ export class CalendarFetchEventsService {
|
||||
const nextSyncCursor = getCalendarEventsResponse.nextSyncCursor;
|
||||
|
||||
const calendarChannelRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'calendarChannel',
|
||||
);
|
||||
|
||||
|
||||
+10
-5
@@ -3,7 +3,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { Any } from 'typeorm';
|
||||
|
||||
import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { CalendarEventParticipantService } from 'src/modules/calendar/calendar-event-participant-manager/services/calendar-event-participant.service';
|
||||
import { type CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel-event-association.workspace-entity';
|
||||
import { type CalendarChannelWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
@@ -20,7 +20,7 @@ type FetchedCalendarEventWithDBEvent = {
|
||||
@Injectable()
|
||||
export class CalendarSaveEventsService {
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly calendarEventParticipantService: CalendarEventParticipantService,
|
||||
) {}
|
||||
|
||||
@@ -31,12 +31,14 @@ export class CalendarSaveEventsService {
|
||||
workspaceId: string,
|
||||
): Promise<void> {
|
||||
const calendarEventRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarEventWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarEventWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'calendarEvent',
|
||||
);
|
||||
|
||||
const calendarChannelEventAssociationRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarChannelEventAssociationWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelEventAssociationWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'calendarChannelEventAssociation',
|
||||
);
|
||||
|
||||
@@ -61,7 +63,10 @@ export class CalendarSaveEventsService {
|
||||
};
|
||||
});
|
||||
|
||||
const workspaceDataSource = await this.twentyORMManager.getDatasource();
|
||||
const workspaceDataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await workspaceDataSource.transaction(
|
||||
async (transactionManager: WorkspaceEntityManager) => {
|
||||
|
||||
+2
@@ -53,6 +53,7 @@ export class CalendarEventParticipantMatchParticipantJob {
|
||||
await this.matchParticipantService.matchParticipantsForPeople({
|
||||
objectMetadataName: 'calendarEventParticipant',
|
||||
participantMatching,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -60,6 +61,7 @@ export class CalendarEventParticipantMatchParticipantJob {
|
||||
await this.matchParticipantService.matchParticipantsForWorkspaceMembers({
|
||||
objectMetadataName: 'calendarEventParticipant',
|
||||
participantMatching,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+5
-3
@@ -10,7 +10,7 @@ import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decora
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
|
||||
import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type CalendarChannelWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import { type CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
|
||||
import { type FetchedCalendarEventParticipant } from 'src/modules/calendar/common/types/fetched-calendar-event';
|
||||
@@ -34,7 +34,7 @@ type FetchedCalendarEventParticipantWithCalendarEventIdAndExistingId =
|
||||
@Injectable()
|
||||
export class CalendarEventParticipantService {
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly matchParticipantService: MatchParticipantService<CalendarEventParticipantWorkspaceEntity>,
|
||||
@InjectMessageQueue(MessageQueue.contactCreationQueue)
|
||||
private readonly messageQueueService: MessageQueueService,
|
||||
@@ -58,7 +58,8 @@ export class CalendarEventParticipantService {
|
||||
const chunkedParticipantsToUpdate = chunk(participantsToUpdate, 200);
|
||||
|
||||
const calendarEventParticipantRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarEventParticipantWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarEventParticipantWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'calendarEventParticipant',
|
||||
);
|
||||
|
||||
@@ -172,6 +173,7 @@ export class CalendarEventParticipantService {
|
||||
objectMetadataName: 'calendarEventParticipant',
|
||||
transactionManager,
|
||||
matchWith: 'workspaceMemberAndPerson',
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -31,8 +31,15 @@ export class CalendarEventFindManyPostQueryHook
|
||||
throw new ForbiddenError('User is required');
|
||||
}
|
||||
|
||||
const workspace = authContext.workspace;
|
||||
|
||||
if (!workspace) {
|
||||
throw new ForbiddenError('Workspace is required');
|
||||
}
|
||||
|
||||
await this.applyCalendarEventsVisibilityRestrictionsService.applyCalendarEventsVisibilityRestrictions(
|
||||
payload,
|
||||
workspace.id,
|
||||
user?.id,
|
||||
);
|
||||
}
|
||||
|
||||
+7
@@ -31,8 +31,15 @@ export class CalendarEventFindOnePostQueryHook
|
||||
throw new ForbiddenError('User is required');
|
||||
}
|
||||
|
||||
const workspace = authContext.workspace;
|
||||
|
||||
if (!workspace) {
|
||||
throw new ForbiddenError('Workspace is required');
|
||||
}
|
||||
|
||||
await this.applyCalendarEventsVisibilityRestrictionsService.applyCalendarEventsVisibilityRestrictions(
|
||||
payload,
|
||||
workspace.id,
|
||||
user?.id,
|
||||
);
|
||||
}
|
||||
|
||||
+23
-15
@@ -2,7 +2,7 @@ import { Test, type TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED } from 'twenty-shared/constants';
|
||||
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { CalendarChannelVisibility } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import { type CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
|
||||
|
||||
@@ -52,18 +52,20 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
findOneByOrFail: jest.fn(),
|
||||
};
|
||||
|
||||
const mockTwentyORMManager = {
|
||||
getRepository: jest.fn().mockImplementation((name) => {
|
||||
if (name === 'calendarChannelEventAssociation') {
|
||||
return mockCalendarEventAssociationRepository;
|
||||
}
|
||||
if (name === 'connectedAccount') {
|
||||
return mockConnectedAccountRepository;
|
||||
}
|
||||
if (name === 'workspaceMember') {
|
||||
return mockWorkspaceMemberRepository;
|
||||
}
|
||||
}),
|
||||
const mockTwentyORMGlobalManager = {
|
||||
getRepositoryForWorkspace: jest
|
||||
.fn()
|
||||
.mockImplementation((workspaceId, name) => {
|
||||
if (name === 'calendarChannelEventAssociation') {
|
||||
return mockCalendarEventAssociationRepository;
|
||||
}
|
||||
if (name === 'connectedAccount') {
|
||||
return mockConnectedAccountRepository;
|
||||
}
|
||||
if (name === 'workspaceMember') {
|
||||
return mockWorkspaceMemberRepository;
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -71,8 +73,8 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
providers: [
|
||||
ApplyCalendarEventsVisibilityRestrictionsService,
|
||||
{
|
||||
provide: TwentyORMManager,
|
||||
useValue: mockTwentyORMManager,
|
||||
provide: TwentyORMGlobalManager,
|
||||
useValue: mockTwentyORMGlobalManager,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
@@ -106,6 +108,7 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
|
||||
const result = await service.applyCalendarEventsVisibilityRestrictions(
|
||||
calendarEvents,
|
||||
'test-workspace-id',
|
||||
'user-id',
|
||||
);
|
||||
|
||||
@@ -143,6 +146,7 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
|
||||
const result = await service.applyCalendarEventsVisibilityRestrictions(
|
||||
calendarEvents,
|
||||
'test-workspace-id',
|
||||
'user-id',
|
||||
);
|
||||
|
||||
@@ -178,6 +182,7 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
|
||||
const result = await service.applyCalendarEventsVisibilityRestrictions(
|
||||
calendarEvents,
|
||||
'test-workspace-id',
|
||||
'user-id',
|
||||
);
|
||||
|
||||
@@ -213,6 +218,7 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
|
||||
const result = await service.applyCalendarEventsVisibilityRestrictions(
|
||||
calendarEvents,
|
||||
'test-workspace-id',
|
||||
'user-id',
|
||||
);
|
||||
|
||||
@@ -260,6 +266,7 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
|
||||
const result = await service.applyCalendarEventsVisibilityRestrictions(
|
||||
calendarEvents,
|
||||
'test-workspace-id',
|
||||
'user-id',
|
||||
);
|
||||
|
||||
@@ -315,6 +322,7 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => {
|
||||
|
||||
const result = await service.applyCalendarEventsVisibilityRestrictions(
|
||||
calendarEvents,
|
||||
'test-workspace-id',
|
||||
undefined,
|
||||
);
|
||||
|
||||
|
||||
+11
-5
@@ -5,7 +5,7 @@ import { FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED } from 'twenty-shared/
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { In } from 'typeorm';
|
||||
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel-event-association.workspace-entity';
|
||||
import { CalendarChannelVisibility } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import { type CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
|
||||
@@ -14,14 +14,18 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
|
||||
|
||||
@Injectable()
|
||||
export class ApplyCalendarEventsVisibilityRestrictionsService {
|
||||
constructor(private readonly twentyORMManager: TwentyORMManager) {}
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
public async applyCalendarEventsVisibilityRestrictions(
|
||||
calendarEvents: CalendarEventWorkspaceEntity[],
|
||||
workspaceId: string,
|
||||
userId?: string, // undefined when request is made with api key
|
||||
) {
|
||||
const calendarChannelEventAssociationRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarChannelEventAssociationWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelEventAssociationWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'calendarChannelEventAssociation',
|
||||
);
|
||||
|
||||
@@ -34,12 +38,14 @@ export class ApplyCalendarEventsVisibilityRestrictionsService {
|
||||
});
|
||||
|
||||
const connectedAccountRepository =
|
||||
await this.twentyORMManager.getRepository<ConnectedAccountWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ConnectedAccountWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'connectedAccount',
|
||||
);
|
||||
|
||||
const workspaceMemberRepository =
|
||||
await this.twentyORMManager.getRepository<WorkspaceMemberWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workspaceMember',
|
||||
);
|
||||
|
||||
|
||||
+4
-3
@@ -3,7 +3,7 @@ import { Test, type TestingModule } from '@nestjs/testing';
|
||||
import { ConnectedAccountProvider } from 'twenty-shared/types';
|
||||
import { type Repository } from 'typeorm';
|
||||
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { GoogleEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/google/services/google-email-alias-manager.service';
|
||||
import { microsoftGraphMeResponseWithProxyAddresses } from 'src/modules/connected-account/email-alias-manager/drivers/microsoft/mocks/microsoft-api-examples';
|
||||
import { MicrosoftEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/microsoft/services/microsoft-email-alias-manager.service';
|
||||
@@ -28,9 +28,9 @@ describe('Email Alias Manager Service', () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: TwentyORMManager,
|
||||
provide: TwentyORMGlobalManager,
|
||||
useValue: {
|
||||
getRepository: jest
|
||||
getRepositoryForWorkspace: jest
|
||||
.fn()
|
||||
.mockResolvedValue(connectedAccountRepository),
|
||||
},
|
||||
@@ -86,6 +86,7 @@ describe('Email Alias Manager Service', () => {
|
||||
|
||||
await emailAliasManagerService.refreshHandleAliases(
|
||||
mockConnectedAccount as ConnectedAccountWorkspaceEntity,
|
||||
'test-workspace-id',
|
||||
);
|
||||
|
||||
expect(
|
||||
|
||||
+5
-3
@@ -3,7 +3,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { ConnectedAccountProvider } from 'twenty-shared/types';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { GoogleEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/google/services/google-email-alias-manager.service';
|
||||
import { MicrosoftEmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/drivers/microsoft/services/microsoft-email-alias-manager.service';
|
||||
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
@@ -13,11 +13,12 @@ export class EmailAliasManagerService {
|
||||
constructor(
|
||||
private readonly googleEmailAliasManagerService: GoogleEmailAliasManagerService,
|
||||
private readonly microsoftEmailAliasManagerService: MicrosoftEmailAliasManagerService,
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
public async refreshHandleAliases(
|
||||
connectedAccount: ConnectedAccountWorkspaceEntity,
|
||||
workspaceId: string,
|
||||
) {
|
||||
let handleAliases: string[];
|
||||
|
||||
@@ -46,7 +47,8 @@ export class EmailAliasManagerService {
|
||||
}
|
||||
|
||||
const connectedAccountRepository =
|
||||
await this.twentyORMManager.getRepository<ConnectedAccountWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ConnectedAccountWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'connectedAccount',
|
||||
);
|
||||
|
||||
|
||||
+4
-3
@@ -12,7 +12,7 @@ import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-contex
|
||||
import { WorkspaceNotFoundDefaultError } from 'src/engine/core-modules/workspace/workspace.exception';
|
||||
import { fromObjectMetadataEntityToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-entity-to-flat-object-metadata.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||
import { type MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
|
||||
|
||||
@@ -21,7 +21,7 @@ export class ConnectedAccountDeleteOnePreQueryHook
|
||||
implements WorkspacePreQueryHookInstance
|
||||
{
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly workspaceEventEmitter: WorkspaceEventEmitter,
|
||||
@InjectRepository(ObjectMetadataEntity)
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
@@ -39,7 +39,8 @@ export class ConnectedAccountDeleteOnePreQueryHook
|
||||
assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError);
|
||||
|
||||
const messageChannelRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'messageChannel',
|
||||
);
|
||||
|
||||
|
||||
+13
-9
@@ -2,7 +2,7 @@ import { Test, type TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { ConnectedAccountProvider } from 'twenty-shared/types';
|
||||
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { GoogleAPIRefreshAccessTokenService } from 'src/modules/connected-account/refresh-tokens-manager/drivers/google/services/google-api-refresh-tokens.service';
|
||||
import { MicrosoftAPIRefreshAccessTokenService } from 'src/modules/connected-account/refresh-tokens-manager/drivers/microsoft/services/microsoft-api-refresh-tokens.service';
|
||||
import {
|
||||
@@ -17,7 +17,7 @@ describe('ConnectedAccountRefreshTokensService', () => {
|
||||
let service: ConnectedAccountRefreshTokensService;
|
||||
let googleAPIRefreshAccessTokenService: GoogleAPIRefreshAccessTokenService;
|
||||
let microsoftAPIRefreshAccessTokenService: MicrosoftAPIRefreshAccessTokenService;
|
||||
let twentyORMManager: TwentyORMManager;
|
||||
let twentyORMGlobalManager: TwentyORMGlobalManager;
|
||||
|
||||
const mockWorkspaceId = 'workspace-123';
|
||||
const mockConnectedAccountId = 'account-456';
|
||||
@@ -42,9 +42,9 @@ describe('ConnectedAccountRefreshTokensService', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: TwentyORMManager,
|
||||
provide: TwentyORMGlobalManager,
|
||||
useValue: {
|
||||
getRepository: jest.fn(),
|
||||
getRepositoryForWorkspace: jest.fn(),
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -61,7 +61,9 @@ describe('ConnectedAccountRefreshTokensService', () => {
|
||||
module.get<MicrosoftAPIRefreshAccessTokenService>(
|
||||
MicrosoftAPIRefreshAccessTokenService,
|
||||
);
|
||||
twentyORMManager = module.get<TwentyORMManager>(TwentyORMManager);
|
||||
twentyORMGlobalManager = module.get<TwentyORMGlobalManager>(
|
||||
TwentyORMGlobalManager,
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -90,7 +92,9 @@ describe('ConnectedAccountRefreshTokensService', () => {
|
||||
expect(
|
||||
microsoftAPIRefreshAccessTokenService.refreshTokens,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(twentyORMManager.getRepository).not.toHaveBeenCalled();
|
||||
expect(
|
||||
twentyORMGlobalManager.getRepositoryForWorkspace,
|
||||
).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should refresh and save new Microsoft token when expired (lastCredentialsRefreshedAt is old)', async () => {
|
||||
@@ -112,7 +116,7 @@ describe('ConnectedAccountRefreshTokensService', () => {
|
||||
.spyOn(microsoftAPIRefreshAccessTokenService, 'refreshTokens')
|
||||
.mockResolvedValue(newTokens);
|
||||
jest
|
||||
.spyOn(twentyORMManager, 'getRepository')
|
||||
.spyOn(twentyORMGlobalManager, 'getRepositoryForWorkspace')
|
||||
.mockResolvedValue(mockRepository as any);
|
||||
|
||||
const result = await service.refreshAndSaveTokens(
|
||||
@@ -152,7 +156,7 @@ describe('ConnectedAccountRefreshTokensService', () => {
|
||||
.spyOn(googleAPIRefreshAccessTokenService, 'refreshTokens')
|
||||
.mockResolvedValue(newTokens);
|
||||
jest
|
||||
.spyOn(twentyORMManager, 'getRepository')
|
||||
.spyOn(twentyORMGlobalManager, 'getRepositoryForWorkspace')
|
||||
.mockResolvedValue(mockRepository as any);
|
||||
|
||||
const result = await service.refreshAndSaveTokens(
|
||||
@@ -192,7 +196,7 @@ describe('ConnectedAccountRefreshTokensService', () => {
|
||||
.spyOn(microsoftAPIRefreshAccessTokenService, 'refreshTokens')
|
||||
.mockResolvedValue(newTokens);
|
||||
jest
|
||||
.spyOn(twentyORMManager, 'getRepository')
|
||||
.spyOn(twentyORMGlobalManager, 'getRepositoryForWorkspace')
|
||||
.mockResolvedValue(mockRepository as any);
|
||||
|
||||
const result = await service.refreshAndSaveTokens(
|
||||
|
||||
+4
-3
@@ -3,7 +3,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { ConnectedAccountProvider } from 'twenty-shared/types';
|
||||
import { assertUnreachable, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { GoogleAPIRefreshAccessTokenService } from 'src/modules/connected-account/refresh-tokens-manager/drivers/google/services/google-api-refresh-tokens.service';
|
||||
import { MicrosoftAPIRefreshAccessTokenService } from 'src/modules/connected-account/refresh-tokens-manager/drivers/microsoft/services/microsoft-api-refresh-tokens.service';
|
||||
import {
|
||||
@@ -29,7 +29,7 @@ export class ConnectedAccountRefreshTokensService {
|
||||
constructor(
|
||||
private readonly googleAPIRefreshAccessTokenService: GoogleAPIRefreshAccessTokenService,
|
||||
private readonly microsoftAPIRefreshAccessTokenService: MicrosoftAPIRefreshAccessTokenService,
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
async refreshAndSaveTokens(
|
||||
@@ -76,7 +76,8 @@ export class ConnectedAccountRefreshTokensService {
|
||||
);
|
||||
|
||||
const connectedAccountRepository =
|
||||
await this.twentyORMManager.getRepository<ConnectedAccountWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ConnectedAccountWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'connectedAccount',
|
||||
);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { In, Repository } from 'typeorm';
|
||||
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { FAVORITE_DELETION_BATCH_SIZE } from 'src/modules/favorite/constants/favorite-deletion-batch-size';
|
||||
import { type FavoriteWorkspaceEntity } from 'src/modules/favorite/standard-objects/favorite.workspace-entity';
|
||||
|
||||
@@ -18,7 +18,7 @@ export class FavoriteDeletionService {
|
||||
|
||||
@InjectRepository(FieldMetadataEntity)
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
async deleteFavoritesForDeletedRecords(
|
||||
@@ -26,7 +26,8 @@ export class FavoriteDeletionService {
|
||||
workspaceId: string,
|
||||
): Promise<void> {
|
||||
const favoriteRepository =
|
||||
await this.twentyORMManager.getRepository<FavoriteWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<FavoriteWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'favorite',
|
||||
);
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { MatchParticipantService } from 'src/modules/match-participant/match-participant.service';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
providers: [ScopedWorkspaceContextFactory, MatchParticipantService],
|
||||
providers: [MatchParticipantService],
|
||||
exports: [MatchParticipantService],
|
||||
})
|
||||
export class MatchParticipantModule {}
|
||||
|
||||
@@ -5,7 +5,6 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { Any, In } from 'typeorm';
|
||||
|
||||
import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||
import { type CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
|
||||
@@ -22,6 +21,7 @@ type MatchParticipantsForWorkspaceMembersArgs = {
|
||||
workspaceMemberIds: string[];
|
||||
};
|
||||
objectMetadataName: ObjectMetadataName;
|
||||
workspaceId: string;
|
||||
};
|
||||
|
||||
type MatchParticipantsForPeopleArgs = {
|
||||
@@ -30,6 +30,7 @@ type MatchParticipantsForPeopleArgs = {
|
||||
personEmails: string[];
|
||||
};
|
||||
objectMetadataName: ObjectMetadataName;
|
||||
workspaceId: string;
|
||||
};
|
||||
|
||||
type MatchParticipantsArgs<
|
||||
@@ -47,6 +48,7 @@ type MatchParticipantsArgs<
|
||||
objectMetadataName: ObjectMetadataName;
|
||||
transactionManager?: WorkspaceEntityManager;
|
||||
matchWith: 'workspaceMemberOnly' | 'personOnly' | 'workspaceMemberAndPerson';
|
||||
workspaceId: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
@@ -58,7 +60,6 @@ export class MatchParticipantService<
|
||||
constructor(
|
||||
private readonly workspaceEventEmitter: WorkspaceEventEmitter,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||
) {}
|
||||
|
||||
private async getParticipantRepository(
|
||||
@@ -83,17 +84,12 @@ export class MatchParticipantService<
|
||||
objectMetadataName,
|
||||
transactionManager,
|
||||
matchWith = 'workspaceMemberAndPerson',
|
||||
workspaceId,
|
||||
}: MatchParticipantsArgs<ParticipantWorkspaceEntity>) {
|
||||
if (participants.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workspaceId = this.scopedWorkspaceContextFactory.create().workspaceId;
|
||||
|
||||
if (!isDefined(workspaceId)) {
|
||||
throw new Error('Workspace ID is required');
|
||||
}
|
||||
|
||||
const personRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<PersonWorkspaceEntity>(
|
||||
workspaceId,
|
||||
@@ -209,13 +205,8 @@ export class MatchParticipantService<
|
||||
public async matchParticipantsForWorkspaceMembers({
|
||||
participantMatching,
|
||||
objectMetadataName,
|
||||
workspaceId,
|
||||
}: MatchParticipantsForWorkspaceMembersArgs) {
|
||||
const workspaceId = this.scopedWorkspaceContextFactory.create().workspaceId;
|
||||
|
||||
if (!isDefined(workspaceId)) {
|
||||
throw new Error('Workspace ID is required');
|
||||
}
|
||||
|
||||
const participantRepository = await this.getParticipantRepository(
|
||||
workspaceId,
|
||||
objectMetadataName,
|
||||
@@ -238,19 +229,15 @@ export class MatchParticipantService<
|
||||
matchWith: 'workspaceMemberOnly',
|
||||
participants: tobeRematchedParticipants as ParticipantWorkspaceEntity[],
|
||||
objectMetadataName,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
public async matchParticipantsForPeople({
|
||||
participantMatching,
|
||||
objectMetadataName,
|
||||
workspaceId,
|
||||
}: MatchParticipantsForPeopleArgs) {
|
||||
const workspaceId = this.scopedWorkspaceContextFactory.create().workspaceId;
|
||||
|
||||
if (!isDefined(workspaceId)) {
|
||||
throw new Error('Workspace ID is required');
|
||||
}
|
||||
|
||||
const participantRepository = await this.getParticipantRepository(
|
||||
workspaceId,
|
||||
objectMetadataName,
|
||||
@@ -293,6 +280,7 @@ export class MatchParticipantService<
|
||||
matchWith: 'personOnly',
|
||||
participants: tobeRematchedParticipants,
|
||||
objectMetadataName,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+8
-5
@@ -7,7 +7,7 @@ import { type ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emit
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event-batch.type';
|
||||
import { type BlocklistWorkspaceEntity } from 'src/modules/blocklist/standard-objects/blocklist.workspace-entity';
|
||||
import { type MessageChannelMessageAssociationWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity';
|
||||
@@ -25,7 +25,7 @@ export type BlocklistItemDeleteMessagesJobData = WorkspaceEventBatch<
|
||||
export class BlocklistItemDeleteMessagesJob {
|
||||
constructor(
|
||||
private readonly threadCleanerService: MessagingMessageCleanerService,
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
@Process(BlocklistItemDeleteMessagesJob.name)
|
||||
@@ -37,7 +37,8 @@ export class BlocklistItemDeleteMessagesJob {
|
||||
);
|
||||
|
||||
const blocklistRepository =
|
||||
await this.twentyORMManager.getRepository<BlocklistWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<BlocklistWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'blocklist',
|
||||
);
|
||||
|
||||
@@ -67,12 +68,14 @@ export class BlocklistItemDeleteMessagesJob {
|
||||
);
|
||||
|
||||
const messageChannelRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannel',
|
||||
);
|
||||
|
||||
const messageChannelMessageAssociationRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelMessageAssociationWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelMessageAssociationWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannelMessageAssociation',
|
||||
);
|
||||
|
||||
|
||||
+4
-3
@@ -6,7 +6,7 @@ import { type ObjectRecordDeleteEvent } from 'src/engine/core-modules/event-emit
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event-batch.type';
|
||||
import { type BlocklistWorkspaceEntity } from 'src/modules/blocklist/standard-objects/blocklist.workspace-entity';
|
||||
import { MessageChannelSyncStatusService } from 'src/modules/messaging/common/services/message-channel-sync-status.service';
|
||||
@@ -25,7 +25,7 @@ export type BlocklistReimportMessagesJobData = WorkspaceEventBatch<
|
||||
})
|
||||
export class BlocklistReimportMessagesJob {
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly messagingChannelSyncStatusService: MessageChannelSyncStatusService,
|
||||
) {}
|
||||
|
||||
@@ -34,7 +34,8 @@ export class BlocklistReimportMessagesJob {
|
||||
const workspaceId = data.workspaceId;
|
||||
|
||||
const messageChannelRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannel',
|
||||
);
|
||||
|
||||
|
||||
+24
-15
@@ -2,7 +2,7 @@ import { Test, type TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED } from 'twenty-shared/constants';
|
||||
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { MessageChannelVisibility } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
|
||||
import { type MessageWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message.workspace-entity';
|
||||
|
||||
@@ -42,18 +42,20 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
findOneByOrFail: jest.fn(),
|
||||
};
|
||||
|
||||
const mockTwentyORMManager = {
|
||||
getRepository: jest.fn().mockImplementation((name) => {
|
||||
if (name === 'messageChannelMessageAssociation') {
|
||||
return mockMessageChannelMessageAssociationRepository;
|
||||
}
|
||||
if (name === 'connectedAccount') {
|
||||
return mockConnectedAccountRepository;
|
||||
}
|
||||
if (name === 'workspaceMember') {
|
||||
return mockWorkspaceMemberRepository;
|
||||
}
|
||||
}),
|
||||
const mockTwentyORMGlobalManager = {
|
||||
getRepositoryForWorkspace: jest
|
||||
.fn()
|
||||
.mockImplementation((workspaceId, name) => {
|
||||
if (name === 'messageChannelMessageAssociation') {
|
||||
return mockMessageChannelMessageAssociationRepository;
|
||||
}
|
||||
if (name === 'connectedAccount') {
|
||||
return mockConnectedAccountRepository;
|
||||
}
|
||||
if (name === 'workspaceMember') {
|
||||
return mockWorkspaceMemberRepository;
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -61,8 +63,8 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
providers: [
|
||||
ApplyMessagesVisibilityRestrictionsService,
|
||||
{
|
||||
provide: TwentyORMManager,
|
||||
useValue: mockTwentyORMManager,
|
||||
provide: TwentyORMGlobalManager,
|
||||
useValue: mockTwentyORMGlobalManager,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
@@ -91,6 +93,7 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
|
||||
const result = await service.applyMessagesVisibilityRestrictions(
|
||||
messages,
|
||||
'test-workspace-id',
|
||||
'user-id',
|
||||
);
|
||||
|
||||
@@ -127,6 +130,7 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
|
||||
const result = await service.applyMessagesVisibilityRestrictions(
|
||||
messages,
|
||||
'test-workspace-id',
|
||||
'user-id',
|
||||
);
|
||||
|
||||
@@ -161,6 +165,7 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
|
||||
const result = await service.applyMessagesVisibilityRestrictions(
|
||||
messages,
|
||||
'test-workspace-id',
|
||||
'user-id',
|
||||
);
|
||||
|
||||
@@ -196,6 +201,7 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
|
||||
const result = await service.applyMessagesVisibilityRestrictions(
|
||||
messages,
|
||||
'test-workspace-id',
|
||||
'user-id',
|
||||
);
|
||||
|
||||
@@ -230,6 +236,7 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
|
||||
const result = await service.applyMessagesVisibilityRestrictions(
|
||||
messages,
|
||||
'test-workspace-id',
|
||||
'user-id',
|
||||
);
|
||||
|
||||
@@ -277,6 +284,7 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
|
||||
const result = await service.applyMessagesVisibilityRestrictions(
|
||||
messages,
|
||||
'test-workspace-id',
|
||||
'user-id',
|
||||
);
|
||||
|
||||
@@ -335,6 +343,7 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => {
|
||||
|
||||
const result = await service.applyMessagesVisibilityRestrictions(
|
||||
messages,
|
||||
'test-workspace-id',
|
||||
undefined,
|
||||
);
|
||||
|
||||
|
||||
+11
-5
@@ -6,7 +6,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { In } from 'typeorm';
|
||||
|
||||
import { NotFoundError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import { type MessageChannelMessageAssociationWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity';
|
||||
import { MessageChannelVisibility } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
|
||||
@@ -15,14 +15,18 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
|
||||
|
||||
@Injectable()
|
||||
export class ApplyMessagesVisibilityRestrictionsService {
|
||||
constructor(private readonly twentyORMManager: TwentyORMManager) {}
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
public async applyMessagesVisibilityRestrictions(
|
||||
messages: MessageWorkspaceEntity[],
|
||||
workspaceId: string,
|
||||
userId?: string, // undefined when request is made with api key
|
||||
) {
|
||||
const messageChannelMessageAssociationRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelMessageAssociationWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelMessageAssociationWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannelMessageAssociation',
|
||||
);
|
||||
|
||||
@@ -35,12 +39,14 @@ export class ApplyMessagesVisibilityRestrictionsService {
|
||||
});
|
||||
|
||||
const connectedAccountRepository =
|
||||
await this.twentyORMManager.getRepository<ConnectedAccountWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ConnectedAccountWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'connectedAccount',
|
||||
);
|
||||
|
||||
const workspaceMemberRepository =
|
||||
await this.twentyORMManager.getRepository<WorkspaceMemberWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workspaceMember',
|
||||
);
|
||||
|
||||
|
||||
+7
@@ -31,8 +31,15 @@ export class MessageFindManyPostQueryHook
|
||||
throw new ForbiddenError('User is required');
|
||||
}
|
||||
|
||||
const workspace = authContext.workspace;
|
||||
|
||||
if (!workspace) {
|
||||
throw new ForbiddenError('Workspace is required');
|
||||
}
|
||||
|
||||
await this.applyMessagesVisibilityRestrictionsService.applyMessagesVisibilityRestrictions(
|
||||
payload,
|
||||
workspace.id,
|
||||
user?.id,
|
||||
);
|
||||
}
|
||||
|
||||
+7
@@ -31,8 +31,15 @@ export class MessageFindOnePostQueryHook
|
||||
throw new ForbiddenError('User is required');
|
||||
}
|
||||
|
||||
const workspace = authContext.workspace;
|
||||
|
||||
if (!workspace) {
|
||||
throw new ForbiddenError('Workspace is required');
|
||||
}
|
||||
|
||||
await this.applyMessagesVisibilityRestrictionsService.applyMessagesVisibilityRestrictions(
|
||||
payload,
|
||||
workspace.id,
|
||||
user?.id,
|
||||
);
|
||||
}
|
||||
|
||||
+4
-3
@@ -3,7 +3,7 @@ import { Scope } from '@nestjs/common';
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { isThrottled } from 'src/modules/connected-account/utils/is-throttled';
|
||||
import { MessageChannelSyncStatusService } from 'src/modules/messaging/common/services/message-channel-sync-status.service';
|
||||
import {
|
||||
@@ -30,7 +30,7 @@ export class MessagingMessageListFetchJob {
|
||||
constructor(
|
||||
private readonly messagingMessageListFetchService: MessagingMessageListFetchService,
|
||||
private readonly messagingMonitoringService: MessagingMonitoringService,
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly messageImportErrorHandlerService: MessageImportExceptionHandlerService,
|
||||
private readonly messageChannelSyncStatusService: MessageChannelSyncStatusService,
|
||||
) {}
|
||||
@@ -46,7 +46,8 @@ export class MessagingMessageListFetchJob {
|
||||
});
|
||||
|
||||
const messageChannelRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannel',
|
||||
);
|
||||
|
||||
|
||||
+4
-3
@@ -3,7 +3,7 @@ import { Scope } from '@nestjs/common';
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { isThrottled } from 'src/modules/connected-account/utils/is-throttled';
|
||||
import { MessageChannelSyncStatusService } from 'src/modules/messaging/common/services/message-channel-sync-status.service';
|
||||
import {
|
||||
@@ -26,7 +26,7 @@ export class MessagingMessagesImportJob {
|
||||
private readonly messagingMessagesImportService: MessagingMessagesImportService,
|
||||
private readonly messagingMonitoringService: MessagingMonitoringService,
|
||||
private readonly messageChannelSyncStatusService: MessageChannelSyncStatusService,
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
@Process(MessagingMessagesImportJob.name)
|
||||
@@ -40,7 +40,8 @@ export class MessagingMessagesImportJob {
|
||||
});
|
||||
|
||||
const messageChannelRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannel',
|
||||
);
|
||||
|
||||
|
||||
+4
-3
@@ -5,7 +5,7 @@ import { In } from 'typeorm';
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { MessageChannelSyncStatusService } from 'src/modules/messaging/common/services/message-channel-sync-status.service';
|
||||
import {
|
||||
MessageChannelSyncStage,
|
||||
@@ -24,7 +24,7 @@ export type MessagingOngoingStaleJobData = {
|
||||
export class MessagingOngoingStaleJob {
|
||||
private readonly logger = new Logger(MessagingOngoingStaleJob.name);
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly messageChannelSyncStatusService: MessageChannelSyncStatusService,
|
||||
) {}
|
||||
|
||||
@@ -33,7 +33,8 @@ export class MessagingOngoingStaleJob {
|
||||
const { workspaceId } = data;
|
||||
|
||||
const messageChannelRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannel',
|
||||
);
|
||||
|
||||
|
||||
+25
-19
@@ -4,7 +4,7 @@ import { ConnectedAccountProvider } from 'twenty-shared/types';
|
||||
|
||||
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
|
||||
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { MessageChannelSyncStatusService } from 'src/modules/messaging/common/services/message-channel-sync-status.service';
|
||||
import { type MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
|
||||
import { type MessageFolderWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-folder.workspace-entity';
|
||||
@@ -24,7 +24,7 @@ describe('MessagingMessageListFetchService', () => {
|
||||
let messagingGetMessageListService: MessagingGetMessageListService;
|
||||
let messagingAccountAuthenticationService: MessagingAccountAuthenticationService;
|
||||
let messageChannelSyncStatusService: MessageChannelSyncStatusService;
|
||||
let twentyORMManager: TwentyORMManager;
|
||||
let twentyORMGlobalManager: TwentyORMGlobalManager;
|
||||
let messagingCursorService: MessagingCursorService;
|
||||
|
||||
let mockMicrosoftMessageChannel: MessageChannelWorkspaceEntity;
|
||||
@@ -196,19 +196,21 @@ describe('MessagingMessageListFetchService', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: TwentyORMManager,
|
||||
provide: TwentyORMGlobalManager,
|
||||
useValue: {
|
||||
getDatasource: jest.fn().mockResolvedValue({
|
||||
getDataSourceForWorkspace: jest.fn().mockResolvedValue({
|
||||
manager: {},
|
||||
}),
|
||||
getRepository: jest.fn().mockImplementation((name) => {
|
||||
if (name === 'messageChannelMessageAssociation') {
|
||||
return mockMessageChannelMessageAssociationRepository;
|
||||
}
|
||||
if (name === 'messageFolder') {
|
||||
return mockMessageFolderRepository;
|
||||
}
|
||||
}),
|
||||
getRepositoryForWorkspace: jest
|
||||
.fn()
|
||||
.mockImplementation((workspaceId, name) => {
|
||||
if (name === 'messageChannelMessageAssociation') {
|
||||
return mockMessageChannelMessageAssociationRepository;
|
||||
}
|
||||
if (name === 'messageFolder') {
|
||||
return mockMessageFolderRepository;
|
||||
}
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -273,7 +275,9 @@ describe('MessagingMessageListFetchService', () => {
|
||||
module.get<MessageChannelSyncStatusService>(
|
||||
MessageChannelSyncStatusService,
|
||||
);
|
||||
twentyORMManager = module.get<TwentyORMManager>(TwentyORMManager);
|
||||
twentyORMGlobalManager = module.get<TwentyORMGlobalManager>(
|
||||
TwentyORMGlobalManager,
|
||||
);
|
||||
messagingCursorService = module.get<MessagingCursorService>(
|
||||
MessagingCursorService,
|
||||
);
|
||||
@@ -316,9 +320,9 @@ describe('MessagingMessageListFetchService', () => {
|
||||
],
|
||||
);
|
||||
|
||||
expect(twentyORMManager.getRepository).toHaveBeenCalledWith(
|
||||
'messageChannelMessageAssociation',
|
||||
);
|
||||
expect(
|
||||
twentyORMGlobalManager.getRepositoryForWorkspace,
|
||||
).toHaveBeenCalledWith(workspaceId, 'messageChannelMessageAssociation');
|
||||
|
||||
expect(messagingCursorService.updateCursor).toHaveBeenCalledWith(
|
||||
{
|
||||
@@ -330,6 +334,7 @@ describe('MessagingMessageListFetchService', () => {
|
||||
},
|
||||
},
|
||||
'new-sync-cursor',
|
||||
workspaceId,
|
||||
'inbox-folder-id',
|
||||
);
|
||||
|
||||
@@ -375,9 +380,9 @@ describe('MessagingMessageListFetchService', () => {
|
||||
],
|
||||
);
|
||||
|
||||
expect(twentyORMManager.getRepository).toHaveBeenCalledWith(
|
||||
'messageChannelMessageAssociation',
|
||||
);
|
||||
expect(
|
||||
twentyORMGlobalManager.getRepositoryForWorkspace,
|
||||
).toHaveBeenCalledWith(workspaceId, 'messageChannelMessageAssociation');
|
||||
|
||||
expect(messagingCursorService.updateCursor).toHaveBeenCalledWith(
|
||||
{
|
||||
@@ -389,6 +394,7 @@ describe('MessagingMessageListFetchService', () => {
|
||||
},
|
||||
},
|
||||
'new-google-history-id',
|
||||
workspaceId,
|
||||
undefined,
|
||||
);
|
||||
|
||||
|
||||
+11
-8
@@ -5,7 +5,7 @@ import { ConnectedAccountProvider } from 'twenty-shared/types';
|
||||
|
||||
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
|
||||
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { BlocklistRepository } from 'src/modules/blocklist/repositories/blocklist.repository';
|
||||
import { EmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/services/email-alias-manager.service';
|
||||
import { ConnectedAccountRefreshTokensService } from 'src/modules/connected-account/refresh-tokens-manager/services/connected-account-refresh-tokens.service';
|
||||
@@ -106,9 +106,9 @@ describe('MessagingMessagesImportService', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: TwentyORMManager,
|
||||
provide: TwentyORMGlobalManager,
|
||||
useValue: {
|
||||
getRepository: jest.fn().mockResolvedValue({
|
||||
getRepositoryForWorkspace: jest.fn().mockResolvedValue({
|
||||
update: jest.fn().mockResolvedValue(undefined),
|
||||
}),
|
||||
},
|
||||
@@ -219,11 +219,14 @@ describe('MessagingMessagesImportService', () => {
|
||||
connectedAccountRefreshTokensService.refreshAndSaveTokens,
|
||||
).toHaveBeenCalledWith(mockConnectedAccount, workspaceId);
|
||||
|
||||
expect(emailAliasManagerService.refreshHandleAliases).toHaveBeenCalledWith({
|
||||
...mockConnectedAccount,
|
||||
accessToken: 'new-access-token',
|
||||
refreshToken: 'new-refresh-token',
|
||||
});
|
||||
expect(emailAliasManagerService.refreshHandleAliases).toHaveBeenCalledWith(
|
||||
{
|
||||
...mockConnectedAccount,
|
||||
accessToken: 'new-access-token',
|
||||
refreshToken: 'new-refresh-token',
|
||||
},
|
||||
workspaceId,
|
||||
);
|
||||
expect(messagingGetMessagesService.getMessages).toHaveBeenCalledWith(
|
||||
['message-id-1', 'message-id-2'],
|
||||
{
|
||||
|
||||
+9
-4
@@ -1,24 +1,29 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
|
||||
import { type MessageFolderWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-folder.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class MessagingCursorService {
|
||||
constructor(private readonly twentyORMManager: TwentyORMManager) {}
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
public async updateCursor(
|
||||
messageChannel: MessageChannelWorkspaceEntity,
|
||||
nextSyncCursor: string,
|
||||
workspaceId: string,
|
||||
folderId?: string,
|
||||
) {
|
||||
const messageChannelRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannel',
|
||||
);
|
||||
const folderRepository =
|
||||
await this.twentyORMManager.getRepository<MessageFolderWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageFolderWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageFolder',
|
||||
);
|
||||
|
||||
|
||||
+4
-3
@@ -5,7 +5,7 @@ import {
|
||||
type TwentyORMException,
|
||||
TwentyORMExceptionCode,
|
||||
} from 'src/engine/twenty-orm/exceptions/twenty-orm.exception';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { MessageChannelSyncStatusService } from 'src/modules/messaging/common/services/message-channel-sync-status.service';
|
||||
import {
|
||||
MessageChannelSyncStatus,
|
||||
@@ -27,7 +27,7 @@ export enum MessageImportSyncStep {
|
||||
@Injectable()
|
||||
export class MessageImportExceptionHandlerService {
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly messageChannelSyncStatusService: MessageChannelSyncStatusService,
|
||||
private readonly exceptionHandlerService: ExceptionHandlerService,
|
||||
) {}
|
||||
@@ -149,7 +149,8 @@ export class MessageImportExceptionHandlerService {
|
||||
}
|
||||
|
||||
const messageChannelRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannel',
|
||||
);
|
||||
|
||||
|
||||
+19
-8
@@ -8,7 +8,7 @@ import { In, MoreThanOrEqual } from 'typeorm';
|
||||
import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decorators/cache-storage.decorator';
|
||||
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
|
||||
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { MessageChannelSyncStatusService } from 'src/modules/messaging/common/services/message-channel-sync-status.service';
|
||||
import { type MessageChannelMessageAssociationWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity';
|
||||
import {
|
||||
@@ -43,7 +43,7 @@ export class MessagingMessageListFetchService {
|
||||
@InjectCacheStorage(CacheStorageNamespace.ModuleMessaging)
|
||||
private readonly cacheStorage: CacheStorageService,
|
||||
private readonly messageChannelSyncStatusService: MessageChannelSyncStatusService,
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly messagingGetMessageListService: MessagingGetMessageListService,
|
||||
private readonly messageImportErrorHandlerService: MessageImportExceptionHandlerService,
|
||||
private readonly messagingMessageCleanerService: MessagingMessageCleanerService,
|
||||
@@ -76,7 +76,8 @@ export class MessagingMessageListFetchService {
|
||||
);
|
||||
|
||||
const messageChannelRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannel',
|
||||
);
|
||||
|
||||
@@ -116,7 +117,10 @@ export class MessagingMessageListFetchService {
|
||||
},
|
||||
};
|
||||
|
||||
const datasource = await this.twentyORMManager.getDatasource();
|
||||
const datasource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await this.syncMessageFoldersService.syncMessageFolders({
|
||||
workspaceId,
|
||||
@@ -125,7 +129,8 @@ export class MessagingMessageListFetchService {
|
||||
});
|
||||
|
||||
const messageFolderRepository =
|
||||
await this.twentyORMManager.getRepository<MessageFolderWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageFolderWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageFolder',
|
||||
);
|
||||
|
||||
@@ -176,7 +181,8 @@ export class MessagingMessageListFetchService {
|
||||
);
|
||||
|
||||
const messageChannelMessageAssociationRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelMessageAssociationWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelMessageAssociationWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannelMessageAssociation',
|
||||
);
|
||||
|
||||
@@ -228,6 +234,7 @@ export class MessagingMessageListFetchService {
|
||||
await this.messagingCursorService.updateCursor(
|
||||
messageChannelWithFreshTokens,
|
||||
nextSyncCursor,
|
||||
workspaceId,
|
||||
folderId,
|
||||
);
|
||||
}
|
||||
@@ -236,6 +243,7 @@ export class MessagingMessageListFetchService {
|
||||
? await this.computeFullSyncMessageChannelMessageAssociationsToDelete(
|
||||
freshMessageChannel,
|
||||
messageExternalIds,
|
||||
workspaceId,
|
||||
)
|
||||
: [];
|
||||
|
||||
@@ -342,7 +350,8 @@ export class MessagingMessageListFetchService {
|
||||
workspaceId: string,
|
||||
): Promise<boolean> {
|
||||
const messageFolderRepository =
|
||||
await this.twentyORMManager.getRepository<MessageFolderWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageFolderWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageFolder',
|
||||
);
|
||||
|
||||
@@ -373,9 +382,11 @@ export class MessagingMessageListFetchService {
|
||||
private async computeFullSyncMessageChannelMessageAssociationsToDelete(
|
||||
messageChannel: Pick<MessageChannelWorkspaceEntity, 'id'>,
|
||||
messageExternalIds: string[],
|
||||
workspaceId: string,
|
||||
) {
|
||||
const messageChannelMessageAssociationRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelMessageAssociationWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelMessageAssociationWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannelMessageAssociation',
|
||||
);
|
||||
|
||||
|
||||
+10
-5
@@ -5,7 +5,7 @@ import { In } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type MessageChannelMessageAssociationWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity';
|
||||
import { type MessageThreadWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-thread.workspace-entity';
|
||||
import { type MessageWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message.workspace-entity';
|
||||
@@ -38,7 +38,9 @@ type MessageAccumulator = {
|
||||
export class MessagingMessageService {
|
||||
private readonly logger = new Logger(MessagingMessageService.name);
|
||||
|
||||
constructor(private readonly twentyORMManager: TwentyORMManager) {}
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
public async saveMessagesWithinTransaction(
|
||||
messages: MessageWithParticipants[],
|
||||
@@ -50,17 +52,20 @@ export class MessagingMessageService {
|
||||
messageExternalIdsAndIdsMap: Map<string, string>;
|
||||
}> {
|
||||
const messageChannelMessageAssociationRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelMessageAssociationWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelMessageAssociationWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannelMessageAssociation',
|
||||
);
|
||||
|
||||
const messageRepository =
|
||||
await this.twentyORMManager.getRepository<MessageWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'message',
|
||||
);
|
||||
|
||||
const messageThreadRepository =
|
||||
await this.twentyORMManager.getRepository<MessageThreadWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageThreadWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageThread',
|
||||
);
|
||||
|
||||
|
||||
+5
-3
@@ -6,7 +6,7 @@ import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decora
|
||||
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
|
||||
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
|
||||
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { BlocklistRepository } from 'src/modules/blocklist/repositories/blocklist.repository';
|
||||
import { BlocklistWorkspaceEntity } from 'src/modules/blocklist/standard-objects/blocklist.workspace-entity';
|
||||
import { EmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/services/email-alias-manager.service';
|
||||
@@ -43,7 +43,7 @@ export class MessagingMessagesImportService {
|
||||
@InjectObjectMetadataRepository(BlocklistWorkspaceEntity)
|
||||
private readonly blocklistRepository: BlocklistRepository,
|
||||
private readonly emailAliasManagerService: EmailAliasManagerService,
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly messagingGetMessagesService: MessagingGetMessagesService,
|
||||
private readonly messageImportErrorHandlerService: MessageImportExceptionHandlerService,
|
||||
private readonly messagingAccountAuthenticationService: MessagingAccountAuthenticationService,
|
||||
@@ -93,6 +93,7 @@ export class MessagingMessagesImportService {
|
||||
|
||||
await this.emailAliasManagerService.refreshHandleAliases(
|
||||
connectedAccountWithFreshTokens,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
messageIdsToFetch = await this.cacheStorage.setPop(
|
||||
@@ -170,7 +171,8 @@ export class MessagingMessagesImportService {
|
||||
}
|
||||
|
||||
const messageChannelRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannel',
|
||||
);
|
||||
|
||||
|
||||
+8
-4
@@ -4,7 +4,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { In } from 'typeorm';
|
||||
|
||||
import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
|
||||
import {
|
||||
MessageFolderPendingSyncAction,
|
||||
@@ -19,7 +19,7 @@ export class MessagingProcessFolderActionsService {
|
||||
);
|
||||
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly messagingDeleteFolderMessagesService: MessagingDeleteFolderMessagesService,
|
||||
) {}
|
||||
|
||||
@@ -86,12 +86,16 @@ export class MessagingProcessFolderActionsService {
|
||||
}
|
||||
|
||||
if (processedFolderIds.length > 0 || folderIdsToDelete.length > 0) {
|
||||
const workspaceDataSource = await this.twentyORMManager.getDatasource();
|
||||
const workspaceDataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await workspaceDataSource?.transaction(
|
||||
async (transactionManager: WorkspaceEntityManager) => {
|
||||
const messageFolderRepository =
|
||||
await this.twentyORMManager.getRepository<MessageFolderWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageFolderWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageFolder',
|
||||
);
|
||||
|
||||
|
||||
+18
-7
@@ -3,7 +3,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import {
|
||||
MessageChannelPendingGroupEmailsAction,
|
||||
MessageChannelWorkspaceEntity,
|
||||
@@ -18,7 +18,7 @@ export class MessagingProcessGroupEmailActionsService {
|
||||
);
|
||||
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly messagingDeleteGroupEmailMessagesService: MessagingDeleteGroupEmailMessagesService,
|
||||
) {}
|
||||
|
||||
@@ -28,7 +28,8 @@ export class MessagingProcessGroupEmailActionsService {
|
||||
pendingGroupEmailsAction: MessageChannelPendingGroupEmailsAction,
|
||||
): Promise<void> {
|
||||
const messageChannelRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannel',
|
||||
);
|
||||
|
||||
@@ -59,13 +60,17 @@ export class MessagingProcessGroupEmailActionsService {
|
||||
`WorkspaceId: ${workspaceId}, MessageChannelId: ${messageChannel.id} - Processing group email action: ${pendingGroupEmailsAction}`,
|
||||
);
|
||||
|
||||
const workspaceDataSource = await this.twentyORMManager.getDatasource();
|
||||
const workspaceDataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await workspaceDataSource?.transaction(
|
||||
async (transactionManager: WorkspaceEntityManager) => {
|
||||
try {
|
||||
const messageChannelRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannel',
|
||||
);
|
||||
|
||||
@@ -120,6 +125,7 @@ export class MessagingProcessGroupEmailActionsService {
|
||||
);
|
||||
|
||||
await this.resetCursors({
|
||||
workspaceId,
|
||||
messageChannelId,
|
||||
transactionManager,
|
||||
});
|
||||
@@ -135,6 +141,7 @@ export class MessagingProcessGroupEmailActionsService {
|
||||
transactionManager: WorkspaceEntityManager,
|
||||
): Promise<void> {
|
||||
await this.resetCursors({
|
||||
workspaceId,
|
||||
messageChannelId,
|
||||
transactionManager,
|
||||
});
|
||||
@@ -145,14 +152,17 @@ export class MessagingProcessGroupEmailActionsService {
|
||||
}
|
||||
|
||||
private async resetCursors({
|
||||
workspaceId,
|
||||
messageChannelId,
|
||||
transactionManager,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
messageChannelId: string;
|
||||
transactionManager: WorkspaceEntityManager;
|
||||
}) {
|
||||
const messageChannelRepository =
|
||||
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannel',
|
||||
);
|
||||
|
||||
@@ -165,7 +175,8 @@ export class MessagingProcessGroupEmailActionsService {
|
||||
);
|
||||
|
||||
const messageFolderRepository =
|
||||
await this.twentyORMManager.getRepository<MessageFolderWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageFolderWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageFolder',
|
||||
);
|
||||
|
||||
|
||||
+5
-3
@@ -7,7 +7,7 @@ import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queu
|
||||
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
|
||||
import { getQueueToken } from 'src/engine/core-modules/message-queue/utils/get-queue-token.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import { CreateCompanyAndContactJob } from 'src/modules/contact-creation-manager/jobs/create-company-and-contact.job';
|
||||
import { MessageDirection } from 'src/modules/messaging/common/enums/message-direction.enum';
|
||||
@@ -132,9 +132,11 @@ describe('MessagingSaveMessagesAndEnqueueContactCreationService', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: TwentyORMManager,
|
||||
provide: TwentyORMGlobalManager,
|
||||
useValue: {
|
||||
getDatasource: jest.fn().mockResolvedValue(datasourceInstance),
|
||||
getDataSourceForWorkspace: jest
|
||||
.fn()
|
||||
.mockResolvedValue(datasourceInstance),
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
+7
-3
@@ -6,7 +6,7 @@ import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decora
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
|
||||
import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import {
|
||||
CreateCompanyAndContactJob,
|
||||
@@ -32,7 +32,7 @@ export class MessagingSaveMessagesAndEnqueueContactCreationService {
|
||||
private readonly messageQueueService: MessageQueueService,
|
||||
private readonly messageService: MessagingMessageService,
|
||||
private readonly messageParticipantService: MessagingMessageParticipantService,
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
async saveMessagesAndEnqueueContactCreation(
|
||||
@@ -43,7 +43,10 @@ export class MessagingSaveMessagesAndEnqueueContactCreationService {
|
||||
) {
|
||||
const handleAliases = connectedAccount.handleAliases?.split(',') || [];
|
||||
|
||||
const workspaceDataSource = await this.twentyORMManager.getDatasource();
|
||||
const workspaceDataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const participantsWithMessageId = await workspaceDataSource?.transaction(
|
||||
async (transactionManager: WorkspaceEntityManager) => {
|
||||
@@ -99,6 +102,7 @@ export class MessagingSaveMessagesAndEnqueueContactCreationService {
|
||||
|
||||
await this.messageParticipantService.saveMessageParticipants(
|
||||
participantsWithMessageId,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
|
||||
+3
-1
@@ -26,7 +26,7 @@ export class MessageParticipantMatchParticipantJob {
|
||||
|
||||
@Process(MessageParticipantMatchParticipantJob.name)
|
||||
async handle(data: MessageParticipantMatchParticipantJobData): Promise<void> {
|
||||
const { participantMatching } = data;
|
||||
const { participantMatching, workspaceId } = data;
|
||||
|
||||
if (
|
||||
participantMatching.personIds.length > 0 ||
|
||||
@@ -35,6 +35,7 @@ export class MessageParticipantMatchParticipantJob {
|
||||
await this.matchParticipantService.matchParticipantsForPeople({
|
||||
participantMatching,
|
||||
objectMetadataName: 'messageParticipant',
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -42,6 +43,7 @@ export class MessageParticipantMatchParticipantJob {
|
||||
await this.matchParticipantService.matchParticipantsForWorkspaceMembers({
|
||||
participantMatching,
|
||||
objectMetadataName: 'messageParticipant',
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+6
-3
@@ -3,7 +3,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { In } from 'typeorm';
|
||||
|
||||
import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { MatchParticipantService } from 'src/modules/match-participant/match-participant.service';
|
||||
import { type MessageParticipantWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-participant.workspace-entity';
|
||||
import { type ParticipantWithMessageId } from 'src/modules/messaging/message-import-manager/drivers/gmail/types/gmail-message.type';
|
||||
@@ -11,16 +11,18 @@ import { type ParticipantWithMessageId } from 'src/modules/messaging/message-imp
|
||||
@Injectable()
|
||||
export class MessagingMessageParticipantService {
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly matchParticipantService: MatchParticipantService<MessageParticipantWorkspaceEntity>,
|
||||
) {}
|
||||
|
||||
public async saveMessageParticipants(
|
||||
participants: ParticipantWithMessageId[],
|
||||
workspaceId: string,
|
||||
transactionManager?: WorkspaceEntityManager,
|
||||
): Promise<void> {
|
||||
const messageParticipantRepository =
|
||||
await this.twentyORMManager.getRepository<MessageParticipantWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageParticipantWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageParticipant',
|
||||
);
|
||||
|
||||
@@ -66,6 +68,7 @@ export class MessagingMessageParticipantService {
|
||||
objectMetadataName: 'messageParticipant',
|
||||
transactionManager,
|
||||
matchWith: 'workspaceMemberAndPerson',
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -6,7 +6,7 @@ import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runne
|
||||
import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import {
|
||||
WorkflowVersionStatus,
|
||||
type WorkflowVersionWorkspaceEntity,
|
||||
@@ -22,7 +22,7 @@ export class WorkflowCreateManyPostQueryHook
|
||||
implements WorkspacePostQueryHookInstance
|
||||
{
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly recordPositionService: RecordPositionService,
|
||||
) {}
|
||||
|
||||
@@ -36,7 +36,8 @@ export class WorkflowCreateManyPostQueryHook
|
||||
assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError);
|
||||
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'workflowVersion',
|
||||
);
|
||||
|
||||
|
||||
+4
-3
@@ -6,7 +6,7 @@ import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runne
|
||||
import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import {
|
||||
WorkflowVersionStatus,
|
||||
type WorkflowVersionWorkspaceEntity,
|
||||
@@ -22,7 +22,7 @@ export class WorkflowCreateOnePostQueryHook
|
||||
implements WorkspacePostQueryHookInstance
|
||||
{
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly recordPositionService: RecordPositionService,
|
||||
) {}
|
||||
|
||||
@@ -38,7 +38,8 @@ export class WorkflowCreateOnePostQueryHook
|
||||
const workflow = payload[0];
|
||||
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'workflowVersion',
|
||||
);
|
||||
|
||||
|
||||
-5
@@ -8,7 +8,6 @@ import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-t
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { type ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
|
||||
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||
import { WorkflowVersionStepOperationsWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-operations.workspace-service';
|
||||
@@ -105,10 +104,6 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
|
||||
provide: AiAgentRoleService,
|
||||
useValue: aiAgentRoleService,
|
||||
},
|
||||
{
|
||||
provide: ScopedWorkspaceContextFactory,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
||||
+1
-6
@@ -6,7 +6,6 @@ import { AiAgentExecutionModule } from 'src/engine/metadata-modules/ai/ai-agent-
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { AiBillingModule } from 'src/engine/metadata-modules/ai/ai-billing/ai-billing.module';
|
||||
import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { WorkflowExecutionContextService } from 'src/modules/workflow/workflow-executor/services/workflow-execution-context.service';
|
||||
import { WorkflowRunModule } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.module';
|
||||
|
||||
@@ -21,11 +20,7 @@ import { AiAgentWorkflowAction } from './ai-agent.workflow-action';
|
||||
UserWorkspaceModule,
|
||||
UserRoleModule,
|
||||
],
|
||||
providers: [
|
||||
ScopedWorkspaceContextFactory,
|
||||
WorkflowExecutionContextService,
|
||||
AiAgentWorkflowAction,
|
||||
],
|
||||
providers: [WorkflowExecutionContextService, AiAgentWorkflowAction],
|
||||
exports: [AiAgentWorkflowAction],
|
||||
})
|
||||
export class AiAgentActionModule {}
|
||||
|
||||
+1
-2
@@ -1,12 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless-function/serverless-function.module';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { CodeWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/code/code.workflow-action';
|
||||
|
||||
@Module({
|
||||
imports: [ServerlessFunctionModule],
|
||||
providers: [ScopedWorkspaceContextFactory, CodeWorkflowAction],
|
||||
providers: [CodeWorkflowAction],
|
||||
exports: [CodeWorkflowAction],
|
||||
})
|
||||
export class CodeActionModule {}
|
||||
|
||||
+2
-10
@@ -5,7 +5,6 @@ import { resolveInput } from 'twenty-shared/utils';
|
||||
import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
||||
|
||||
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import {
|
||||
WorkflowStepExecutorException,
|
||||
WorkflowStepExecutorExceptionCode,
|
||||
@@ -20,13 +19,13 @@ import { type WorkflowCodeActionInput } from 'src/modules/workflow/workflow-exec
|
||||
export class CodeWorkflowAction implements WorkflowAction {
|
||||
constructor(
|
||||
private readonly serverlessFunctionService: ServerlessFunctionService,
|
||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||
) {}
|
||||
|
||||
async execute({
|
||||
currentStepId,
|
||||
steps,
|
||||
context,
|
||||
runInfo,
|
||||
}: WorkflowActionInput): Promise<WorkflowActionOutput> {
|
||||
const step = findStepOrThrow({
|
||||
stepId: currentStepId,
|
||||
@@ -46,14 +45,7 @@ export class CodeWorkflowAction implements WorkflowAction {
|
||||
) as WorkflowCodeActionInput;
|
||||
|
||||
try {
|
||||
const { workspaceId } = this.scopedWorkspaceContextFactory.create();
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new WorkflowStepExecutorException(
|
||||
'Scoped workspace not found',
|
||||
WorkflowStepExecutorExceptionCode.SCOPED_WORKSPACE_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
const { workspaceId } = runInfo;
|
||||
|
||||
const result =
|
||||
await this.serverlessFunctionService.executeOneServerlessFunction(
|
||||
|
||||
+1
-10
@@ -10,7 +10,6 @@ import {
|
||||
RecordCrudExceptionCode,
|
||||
} from 'src/engine/core-modules/record-crud/exceptions/record-crud.exception';
|
||||
import { CreateRecordService } from 'src/engine/core-modules/record-crud/services/create-record.service';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { WorkflowExecutionContextService } from 'src/modules/workflow/workflow-executor/services/workflow-execution-context.service';
|
||||
import { type WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
|
||||
import { type WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
|
||||
@@ -22,7 +21,6 @@ import { type WorkflowCreateRecordActionInput } from 'src/modules/workflow/workf
|
||||
export class CreateRecordWorkflowAction implements WorkflowAction {
|
||||
constructor(
|
||||
private readonly createRecordService: CreateRecordService,
|
||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||
private readonly workflowExecutionContextService: WorkflowExecutionContextService,
|
||||
) {}
|
||||
|
||||
@@ -37,14 +35,7 @@ export class CreateRecordWorkflowAction implements WorkflowAction {
|
||||
stepId: currentStepId,
|
||||
});
|
||||
|
||||
const { workspaceId } = this.scopedWorkspaceContextFactory.create();
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new RecordCrudException(
|
||||
'Failed to create: Workspace ID is required',
|
||||
RecordCrudExceptionCode.INVALID_REQUEST,
|
||||
);
|
||||
}
|
||||
const { workspaceId } = runInfo;
|
||||
|
||||
const workflowActionInput = resolveInput(
|
||||
step.settings.input,
|
||||
|
||||
+1
-10
@@ -9,7 +9,6 @@ import {
|
||||
RecordCrudExceptionCode,
|
||||
} from 'src/engine/core-modules/record-crud/exceptions/record-crud.exception';
|
||||
import { DeleteRecordService } from 'src/engine/core-modules/record-crud/services/delete-record.service';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import {
|
||||
WorkflowStepExecutorException,
|
||||
WorkflowStepExecutorExceptionCode,
|
||||
@@ -25,7 +24,6 @@ import { type WorkflowDeleteRecordActionInput } from 'src/modules/workflow/workf
|
||||
export class DeleteRecordWorkflowAction implements WorkflowAction {
|
||||
constructor(
|
||||
private readonly deleteRecordService: DeleteRecordService,
|
||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||
private readonly workflowExecutionContextService: WorkflowExecutionContextService,
|
||||
) {}
|
||||
|
||||
@@ -63,14 +61,7 @@ export class DeleteRecordWorkflowAction implements WorkflowAction {
|
||||
);
|
||||
}
|
||||
|
||||
const { workspaceId } = this.scopedWorkspaceContextFactory.create();
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new RecordCrudException(
|
||||
'Failed to delete: Workspace ID is required',
|
||||
RecordCrudExceptionCode.INVALID_REQUEST,
|
||||
);
|
||||
}
|
||||
const { workspaceId } = runInfo;
|
||||
|
||||
const executionContext =
|
||||
await this.workflowExecutionContextService.getExecutionContext(runInfo);
|
||||
|
||||
+1
-10
@@ -17,7 +17,6 @@ import {
|
||||
RecordCrudExceptionCode,
|
||||
} from 'src/engine/core-modules/record-crud/exceptions/record-crud.exception';
|
||||
import { FindRecordsService } from 'src/engine/core-modules/record-crud/services/find-records.service';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||
import {
|
||||
WorkflowStepExecutorException,
|
||||
@@ -34,7 +33,6 @@ import { type WorkflowFindRecordsActionInput } from 'src/modules/workflow/workfl
|
||||
export class FindRecordsWorkflowAction implements WorkflowAction {
|
||||
constructor(
|
||||
private readonly findRecordsService: FindRecordsService,
|
||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||
private readonly workflowExecutionContextService: WorkflowExecutionContextService,
|
||||
private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService,
|
||||
) {}
|
||||
@@ -62,14 +60,7 @@ export class FindRecordsWorkflowAction implements WorkflowAction {
|
||||
context,
|
||||
) as WorkflowFindRecordsActionInput;
|
||||
|
||||
const { workspaceId } = this.scopedWorkspaceContextFactory.create();
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new RecordCrudException(
|
||||
'Failed to read: Workspace ID is required',
|
||||
RecordCrudExceptionCode.INVALID_REQUEST,
|
||||
);
|
||||
}
|
||||
const { workspaceId } = runInfo;
|
||||
|
||||
const executionContext =
|
||||
await this.workflowExecutionContextService.getExecutionContext(runInfo);
|
||||
|
||||
-2
@@ -3,7 +3,6 @@ import { Module } from '@nestjs/common';
|
||||
import { RecordCrudModule } from 'src/engine/core-modules/record-crud/record-crud.module';
|
||||
import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module';
|
||||
import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module';
|
||||
import { WorkflowExecutionContextService } from 'src/modules/workflow/workflow-executor/services/workflow-execution-context.service';
|
||||
import { CreateRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action';
|
||||
@@ -22,7 +21,6 @@ import { WorkflowRunModule } from 'src/modules/workflow/workflow-runner/workflow
|
||||
WorkflowCommonModule,
|
||||
],
|
||||
providers: [
|
||||
ScopedWorkspaceContextFactory,
|
||||
WorkflowExecutionContextService,
|
||||
CreateRecordWorkflowAction,
|
||||
UpsertRecordWorkflowAction,
|
||||
|
||||
+1
-10
@@ -9,7 +9,6 @@ import {
|
||||
RecordCrudExceptionCode,
|
||||
} from 'src/engine/core-modules/record-crud/exceptions/record-crud.exception';
|
||||
import { UpdateRecordService } from 'src/engine/core-modules/record-crud/services/update-record.service';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import {
|
||||
WorkflowStepExecutorException,
|
||||
WorkflowStepExecutorExceptionCode,
|
||||
@@ -25,7 +24,6 @@ import { type WorkflowUpdateRecordActionInput } from 'src/modules/workflow/workf
|
||||
export class UpdateRecordWorkflowAction implements WorkflowAction {
|
||||
constructor(
|
||||
private readonly updateRecordService: UpdateRecordService,
|
||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||
private readonly workflowExecutionContextService: WorkflowExecutionContextService,
|
||||
) {}
|
||||
|
||||
@@ -63,14 +61,7 @@ export class UpdateRecordWorkflowAction implements WorkflowAction {
|
||||
);
|
||||
}
|
||||
|
||||
const { workspaceId } = this.scopedWorkspaceContextFactory.create();
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new RecordCrudException(
|
||||
'Failed to update: Workspace ID is required',
|
||||
RecordCrudExceptionCode.INVALID_REQUEST,
|
||||
);
|
||||
}
|
||||
const { workspaceId } = runInfo;
|
||||
|
||||
const executionContext =
|
||||
await this.workflowExecutionContextService.getExecutionContext(runInfo);
|
||||
|
||||
+1
-10
@@ -9,7 +9,6 @@ import {
|
||||
RecordCrudExceptionCode,
|
||||
} from 'src/engine/core-modules/record-crud/exceptions/record-crud.exception';
|
||||
import { UpsertRecordService } from 'src/engine/core-modules/record-crud/services/upsert-record.service';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import {
|
||||
WorkflowStepExecutorException,
|
||||
WorkflowStepExecutorExceptionCode,
|
||||
@@ -25,7 +24,6 @@ import { WorkflowUpsertRecordActionInput } from 'src/modules/workflow/workflow-e
|
||||
export class UpsertRecordWorkflowAction implements WorkflowAction {
|
||||
constructor(
|
||||
private readonly upsertRecordService: UpsertRecordService,
|
||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||
private readonly workflowExecutionContextService: WorkflowExecutionContextService,
|
||||
) {}
|
||||
|
||||
@@ -59,14 +57,7 @@ export class UpsertRecordWorkflowAction implements WorkflowAction {
|
||||
);
|
||||
}
|
||||
|
||||
const { workspaceId } = this.scopedWorkspaceContextFactory.create();
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new RecordCrudException(
|
||||
'Failed to update: Workspace ID is required',
|
||||
RecordCrudExceptionCode.INVALID_REQUEST,
|
||||
);
|
||||
}
|
||||
const { workspaceId } = runInfo;
|
||||
|
||||
const executionContext =
|
||||
await this.workflowExecutionContextService.getExecutionContext(runInfo);
|
||||
|
||||
+2
-1
@@ -30,6 +30,7 @@ export class ToolExecutorWorkflowAction implements WorkflowAction {
|
||||
currentStepId,
|
||||
steps,
|
||||
context,
|
||||
runInfo,
|
||||
}: WorkflowActionInput): Promise<WorkflowActionOutput> {
|
||||
const step = steps.find((step) => step.id === currentStepId);
|
||||
|
||||
@@ -45,7 +46,7 @@ export class ToolExecutorWorkflowAction implements WorkflowAction {
|
||||
|
||||
const toolInput = resolveInput(step.settings.input, context) as ToolInput;
|
||||
|
||||
const toolOutput = await tool.execute(toolInput);
|
||||
const toolOutput = await tool.execute(toolInput, runInfo.workspaceId);
|
||||
|
||||
return {
|
||||
result: toolOutput.result as object,
|
||||
|
||||
-2
@@ -3,7 +3,6 @@ import { Module } from '@nestjs/common';
|
||||
import { BillingModule } from 'src/engine/core-modules/billing/billing.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { ToolModule } from 'src/engine/core-modules/tool/tool.module';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module';
|
||||
import { WorkflowActionFactory } from 'src/modules/workflow/workflow-executor/factories/workflow-action.factory';
|
||||
import { AiAgentActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/ai-agent/ai-agent-action.module';
|
||||
@@ -36,7 +35,6 @@ import { WorkflowRunModule } from 'src/modules/workflow/workflow-runner/workflow
|
||||
],
|
||||
providers: [
|
||||
WorkflowExecutorWorkspaceService,
|
||||
ScopedWorkspaceContextFactory,
|
||||
WorkflowActionFactory,
|
||||
ToolExecutorWorkflowAction,
|
||||
],
|
||||
|
||||
+1
-6
@@ -8,7 +8,6 @@ import { RecordPositionModule } from 'src/engine/core-modules/record-position/re
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module';
|
||||
import { DeleteWorkflowRunsCommand } from 'src/modules/workflow/workflow-runner/workflow-run/command/delete-workflow-runs.command';
|
||||
import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service';
|
||||
@@ -25,11 +24,7 @@ import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runne
|
||||
MetricsModule,
|
||||
DataSourceModule,
|
||||
],
|
||||
providers: [
|
||||
WorkflowRunWorkspaceService,
|
||||
ScopedWorkspaceContextFactory,
|
||||
DeleteWorkflowRunsCommand,
|
||||
],
|
||||
providers: [WorkflowRunWorkspaceService, DeleteWorkflowRunsCommand],
|
||||
exports: [WorkflowRunWorkspaceService, DeleteWorkflowRunsCommand],
|
||||
})
|
||||
export class WorkflowRunModule {}
|
||||
|
||||
+2
-12
@@ -10,7 +10,6 @@ import { WithLock } from 'src/engine/core-modules/cache-lock/with-lock.decorator
|
||||
import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service';
|
||||
import { MetricsKeys } from 'src/engine/core-modules/metrics/types/metrics-keys.type';
|
||||
import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import {
|
||||
WorkflowRunStatus,
|
||||
@@ -30,7 +29,6 @@ export class WorkflowRunWorkspaceService {
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService,
|
||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||
private readonly recordPositionService: RecordPositionService,
|
||||
private readonly metricsService: MetricsService,
|
||||
) {}
|
||||
@@ -42,6 +40,7 @@ export class WorkflowRunWorkspaceService {
|
||||
status,
|
||||
triggerPayload,
|
||||
error,
|
||||
workspaceId,
|
||||
}: {
|
||||
workflowVersionId: string;
|
||||
createdBy: ActorMetadata;
|
||||
@@ -52,17 +51,8 @@ export class WorkflowRunWorkspaceService {
|
||||
triggerPayload: object;
|
||||
workflowRunId?: string;
|
||||
error?: string;
|
||||
workspaceId: string;
|
||||
}) {
|
||||
const workspaceId =
|
||||
this.scopedWorkspaceContextFactory.create()?.workspaceId;
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new WorkflowRunException(
|
||||
'Workspace id is invalid',
|
||||
WorkflowRunExceptionCode.WORKFLOW_RUN_INVALID,
|
||||
);
|
||||
}
|
||||
|
||||
const workflowRunRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowRunWorkspaceEntity>(
|
||||
workspaceId,
|
||||
|
||||
+6
@@ -83,6 +83,7 @@ export class WorkflowRunnerWorkspaceService {
|
||||
|
||||
if (isHardThrottled) {
|
||||
return this.createFailedWorkflowRun({
|
||||
workspaceId,
|
||||
workflowVersionId,
|
||||
initialWorkflowRunId,
|
||||
source,
|
||||
@@ -258,11 +259,13 @@ export class WorkflowRunnerWorkspaceService {
|
||||
}
|
||||
|
||||
private async createFailedWorkflowRun({
|
||||
workspaceId,
|
||||
workflowVersionId,
|
||||
initialWorkflowRunId,
|
||||
source,
|
||||
payload,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
workflowVersionId: string;
|
||||
initialWorkflowRunId?: string;
|
||||
source: ActorMetadata;
|
||||
@@ -276,6 +279,7 @@ export class WorkflowRunnerWorkspaceService {
|
||||
status: WorkflowRunStatus.FAILED,
|
||||
triggerPayload: payload,
|
||||
error: 'Throttle limit reached',
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return { workflowRunId };
|
||||
@@ -301,6 +305,7 @@ export class WorkflowRunnerWorkspaceService {
|
||||
createdBy: source,
|
||||
status: WorkflowRunStatus.ENQUEUED,
|
||||
triggerPayload: payload,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await this.messageQueueService.add<RunWorkflowJobData>(
|
||||
@@ -334,6 +339,7 @@ export class WorkflowRunnerWorkspaceService {
|
||||
createdBy: source,
|
||||
status: WorkflowRunStatus.NOT_STARTED,
|
||||
triggerPayload: payload,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await this.workflowThrottlingWorkspaceService.increaseWorkflowRunNotStartedCount(
|
||||
|
||||
+2
@@ -97,9 +97,11 @@ export class WorkflowToolWorkspaceService {
|
||||
createUpdateWorkflowVersionPositionsTool(this.deps, context);
|
||||
const activateWorkflowVersion = createActivateWorkflowVersionTool(
|
||||
this.deps,
|
||||
context,
|
||||
);
|
||||
const deactivateWorkflowVersion = createDeactivateWorkflowVersionTool(
|
||||
this.deps,
|
||||
context,
|
||||
);
|
||||
const computeStepOutputSchema = createComputeStepOutputSchemaTool(
|
||||
this.deps,
|
||||
|
||||
+6
-1
@@ -1,6 +1,9 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { type WorkflowToolDependencies } from 'src/modules/workflow/workflow-tools/types/workflow-tool-dependencies.type';
|
||||
import {
|
||||
type WorkflowToolContext,
|
||||
type WorkflowToolDependencies,
|
||||
} from 'src/modules/workflow/workflow-tools/types/workflow-tool-dependencies.type';
|
||||
|
||||
const activateWorkflowVersionSchema = z.object({
|
||||
workflowVersionId: z
|
||||
@@ -14,6 +17,7 @@ type ActivateWorkflowVersionInput = z.infer<
|
||||
|
||||
export const createActivateWorkflowVersionTool = (
|
||||
deps: Pick<WorkflowToolDependencies, 'workflowTriggerService'>,
|
||||
context: WorkflowToolContext,
|
||||
) => ({
|
||||
name: 'activate_workflow_version' as const,
|
||||
description:
|
||||
@@ -23,6 +27,7 @@ export const createActivateWorkflowVersionTool = (
|
||||
try {
|
||||
return await deps.workflowTriggerService.activateWorkflowVersion(
|
||||
parameters.workflowVersionId,
|
||||
context.workspaceId,
|
||||
);
|
||||
} catch (error) {
|
||||
return {
|
||||
|
||||
+1
@@ -155,6 +155,7 @@ This is the most efficient way for AI to create workflows as it handles all the
|
||||
if (parameters.activate) {
|
||||
await deps.workflowTriggerService.activateWorkflowVersion(
|
||||
workflowVersionId,
|
||||
context.workspaceId,
|
||||
);
|
||||
|
||||
await updateWorkflowStatus({
|
||||
|
||||
+6
-1
@@ -1,6 +1,9 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { type WorkflowToolDependencies } from 'src/modules/workflow/workflow-tools/types/workflow-tool-dependencies.type';
|
||||
import {
|
||||
type WorkflowToolContext,
|
||||
type WorkflowToolDependencies,
|
||||
} from 'src/modules/workflow/workflow-tools/types/workflow-tool-dependencies.type';
|
||||
|
||||
const deactivateWorkflowVersionSchema = z.object({
|
||||
workflowVersionId: z
|
||||
@@ -14,6 +17,7 @@ type DeactivateWorkflowVersionInput = z.infer<
|
||||
|
||||
export const createDeactivateWorkflowVersionTool = (
|
||||
deps: Pick<WorkflowToolDependencies, 'workflowTriggerService'>,
|
||||
context: WorkflowToolContext,
|
||||
) => ({
|
||||
name: 'deactivate_workflow_version' as const,
|
||||
description:
|
||||
@@ -23,6 +27,7 @@ export const createDeactivateWorkflowVersionTool = (
|
||||
try {
|
||||
return await deps.workflowTriggerService.deactivateWorkflowVersion(
|
||||
parameters.workflowVersionId,
|
||||
context.workspaceId,
|
||||
);
|
||||
} catch (error) {
|
||||
return {
|
||||
|
||||
+17
-5
@@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import {
|
||||
type AutomatedTriggerType,
|
||||
type WorkflowAutomatedTriggerWorkspaceEntity,
|
||||
@@ -9,19 +9,24 @@ import { type AutomatedTriggerSettings } from 'src/modules/workflow/workflow-tri
|
||||
|
||||
@Injectable()
|
||||
export class AutomatedTriggerWorkspaceService {
|
||||
constructor(private readonly twentyORMManager: TwentyORMManager) {}
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
async addAutomatedTrigger({
|
||||
workflowId,
|
||||
type,
|
||||
settings,
|
||||
workspaceId,
|
||||
}: {
|
||||
workflowId: string;
|
||||
type: AutomatedTriggerType;
|
||||
settings: AutomatedTriggerSettings;
|
||||
workspaceId: string;
|
||||
}) {
|
||||
const workflowAutomatedTriggerRepository =
|
||||
await this.twentyORMManager.getRepository<WorkflowAutomatedTriggerWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowAutomatedTriggerWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowAutomatedTrigger',
|
||||
);
|
||||
|
||||
@@ -32,9 +37,16 @@ export class AutomatedTriggerWorkspaceService {
|
||||
});
|
||||
}
|
||||
|
||||
async deleteAutomatedTrigger({ workflowId }: { workflowId: string }) {
|
||||
async deleteAutomatedTrigger({
|
||||
workflowId,
|
||||
workspaceId,
|
||||
}: {
|
||||
workflowId: string;
|
||||
workspaceId: string;
|
||||
}) {
|
||||
const workflowAutomatedTriggerRepository =
|
||||
await this.twentyORMManager.getRepository<WorkflowAutomatedTriggerWorkspaceEntity>(
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowAutomatedTriggerWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowAutomatedTrigger',
|
||||
);
|
||||
|
||||
|
||||
+1
-6
@@ -3,7 +3,6 @@ import { Module } from '@nestjs/common';
|
||||
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
||||
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module';
|
||||
import { WorkflowRunnerModule } from 'src/modules/workflow/workflow-runner/workflow-runner.module';
|
||||
import { AutomatedTriggerModule } from 'src/modules/workflow/workflow-trigger/automated-trigger/automated-trigger.module';
|
||||
@@ -17,11 +16,7 @@ import { WorkflowTriggerWorkspaceService } from 'src/modules/workflow/workflow-t
|
||||
AutomatedTriggerModule,
|
||||
NestjsQueryTypeOrmModule.forFeature([ObjectMetadataEntity]),
|
||||
],
|
||||
providers: [
|
||||
WorkflowTriggerWorkspaceService,
|
||||
ScopedWorkspaceContextFactory,
|
||||
WorkflowTriggerJob,
|
||||
],
|
||||
providers: [WorkflowTriggerWorkspaceService, WorkflowTriggerJob],
|
||||
exports: [WorkflowTriggerWorkspaceService],
|
||||
})
|
||||
export class WorkflowTriggerModule {}
|
||||
|
||||
+37
-29
@@ -3,7 +3,6 @@ import { Injectable } from '@nestjs/common';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { type ActorMetadata } from 'twenty-shared/types';
|
||||
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||
@@ -34,43 +33,31 @@ export class WorkflowTriggerWorkspaceService {
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService,
|
||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||
private readonly workflowRunnerWorkspaceService: WorkflowRunnerWorkspaceService,
|
||||
private readonly automatedTriggerWorkspaceService: AutomatedTriggerWorkspaceService,
|
||||
private readonly workspaceEventEmitter: WorkspaceEventEmitter,
|
||||
) {}
|
||||
|
||||
private getWorkspaceId() {
|
||||
const workspaceId = this.scopedWorkspaceContextFactory.create().workspaceId;
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new WorkflowTriggerException(
|
||||
'No workspace id found',
|
||||
WorkflowTriggerExceptionCode.INTERNAL_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
return workspaceId;
|
||||
}
|
||||
|
||||
async runWorkflowVersion({
|
||||
workflowVersionId,
|
||||
payload,
|
||||
createdBy,
|
||||
workflowRunId,
|
||||
workspaceId,
|
||||
}: {
|
||||
workflowVersionId: string;
|
||||
payload: object;
|
||||
createdBy: ActorMetadata;
|
||||
workflowRunId?: string;
|
||||
workspaceId: string;
|
||||
}) {
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail({
|
||||
workflowVersionId,
|
||||
workspaceId: this.getWorkspaceId(),
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return this.workflowRunnerWorkspaceService.run({
|
||||
workspaceId: this.getWorkspaceId(),
|
||||
workspaceId,
|
||||
workflowRunId,
|
||||
workflowVersionId,
|
||||
payload,
|
||||
@@ -78,11 +65,13 @@ export class WorkflowTriggerWorkspaceService {
|
||||
});
|
||||
}
|
||||
|
||||
async activateWorkflowVersion(workflowVersionId: string) {
|
||||
const workspaceId = this.getWorkspaceId();
|
||||
async activateWorkflowVersion(
|
||||
workflowVersionId: string,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
|
||||
this.getWorkspaceId(),
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true }, // settings permissions are checked at resolver-level
|
||||
);
|
||||
@@ -121,15 +110,19 @@ export class WorkflowTriggerWorkspaceService {
|
||||
workflowVersion,
|
||||
workflowRepository,
|
||||
workflowVersionRepository,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async deactivateWorkflowVersion(workflowVersionId: string) {
|
||||
async deactivateWorkflowVersion(
|
||||
workflowVersionId: string,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
|
||||
this.getWorkspaceId(),
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
@@ -137,14 +130,15 @@ export class WorkflowTriggerWorkspaceService {
|
||||
await this.performDeactivationSteps(
|
||||
workflowVersionId,
|
||||
workflowVersionRepository,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async stopWorkflowRun(workflowRunId: string) {
|
||||
async stopWorkflowRun(workflowRunId: string, workspaceId: string) {
|
||||
return this.workflowRunnerWorkspaceService.stopWorkflowRun(
|
||||
this.getWorkspaceId(),
|
||||
workspaceId,
|
||||
workflowRunId,
|
||||
);
|
||||
}
|
||||
@@ -154,6 +148,7 @@ export class WorkflowTriggerWorkspaceService {
|
||||
workflowVersion: WorkflowVersionWorkspaceEntity,
|
||||
workflowRepository: WorkspaceRepository<WorkflowWorkspaceEntity>,
|
||||
workflowVersionRepository: WorkspaceRepository<WorkflowVersionWorkspaceEntity>,
|
||||
workspaceId: string,
|
||||
) {
|
||||
if (
|
||||
workflow.lastPublishedVersionId &&
|
||||
@@ -162,6 +157,7 @@ export class WorkflowTriggerWorkspaceService {
|
||||
await this.performDeactivationSteps(
|
||||
workflow.lastPublishedVersionId,
|
||||
workflowVersionRepository,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -175,14 +171,16 @@ export class WorkflowTriggerWorkspaceService {
|
||||
await this.setActiveVersionStatus(
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
await this.enableTrigger(workflowVersion);
|
||||
await this.enableTrigger(workflowVersion, workspaceId);
|
||||
}
|
||||
|
||||
private async performDeactivationSteps(
|
||||
workflowVersionId: string,
|
||||
workflowVersionRepository: WorkspaceRepository<WorkflowVersionWorkspaceEntity>,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const workflowVersionNullable = await workflowVersionRepository.findOne({
|
||||
where: { id: workflowVersionId },
|
||||
@@ -200,14 +198,16 @@ export class WorkflowTriggerWorkspaceService {
|
||||
await this.setDeactivatedVersionStatus(
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
await this.disableTrigger(workflowVersion);
|
||||
await this.disableTrigger(workflowVersion, workspaceId);
|
||||
}
|
||||
|
||||
private async setActiveVersionStatus(
|
||||
workflowVersion: WorkflowVersionWorkspaceEntity,
|
||||
workflowVersionRepository: WorkspaceRepository<WorkflowVersionWorkspaceEntity>,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const activeWorkflowVersions = await workflowVersionRepository.find({
|
||||
where: {
|
||||
@@ -234,13 +234,14 @@ export class WorkflowTriggerWorkspaceService {
|
||||
await this.emitStatusUpdateEvents(
|
||||
workflowVersion,
|
||||
WorkflowVersionStatus.ACTIVE,
|
||||
this.getWorkspaceId(),
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
private async setDeactivatedVersionStatus(
|
||||
workflowVersion: WorkflowVersionWorkspaceEntity,
|
||||
workflowVersionRepository: WorkspaceRepository<WorkflowVersionWorkspaceEntity>,
|
||||
workspaceId: string,
|
||||
) {
|
||||
if (workflowVersion.status !== WorkflowVersionStatus.ACTIVE) {
|
||||
throw new WorkflowTriggerException(
|
||||
@@ -260,7 +261,7 @@ export class WorkflowTriggerWorkspaceService {
|
||||
await this.emitStatusUpdateEvents(
|
||||
workflowVersion,
|
||||
WorkflowVersionStatus.DEACTIVATED,
|
||||
this.getWorkspaceId(),
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -287,7 +288,10 @@ export class WorkflowTriggerWorkspaceService {
|
||||
);
|
||||
}
|
||||
|
||||
private async enableTrigger(workflowVersion: WorkflowVersionWorkspaceEntity) {
|
||||
private async enableTrigger(
|
||||
workflowVersion: WorkflowVersionWorkspaceEntity,
|
||||
workspaceId: string,
|
||||
) {
|
||||
assertWorkflowVersionTriggerIsDefined(workflowVersion);
|
||||
|
||||
switch (workflowVersion.trigger.type) {
|
||||
@@ -302,6 +306,7 @@ export class WorkflowTriggerWorkspaceService {
|
||||
workflowId: workflowVersion.workflowId,
|
||||
type: AutomatedTriggerType.DATABASE_EVENT,
|
||||
settings,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return;
|
||||
@@ -313,6 +318,7 @@ export class WorkflowTriggerWorkspaceService {
|
||||
workflowId: workflowVersion.workflowId,
|
||||
type: AutomatedTriggerType.CRON,
|
||||
settings: { pattern },
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return;
|
||||
@@ -325,6 +331,7 @@ export class WorkflowTriggerWorkspaceService {
|
||||
|
||||
private async disableTrigger(
|
||||
workflowVersion: WorkflowVersionWorkspaceEntity,
|
||||
workspaceId: string,
|
||||
) {
|
||||
assertWorkflowVersionTriggerIsDefined(workflowVersion);
|
||||
|
||||
@@ -333,6 +340,7 @@ export class WorkflowTriggerWorkspaceService {
|
||||
case WorkflowTriggerType.CRON:
|
||||
await this.automatedTriggerWorkspaceService.deleteAutomatedTrigger({
|
||||
workflowId: workflowVersion.workflowId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user