Remove connected account feature flag (#19286)

Co-authored-by: martmull <martmull@hotmail.fr>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
neo773
2026-04-06 13:43:41 +05:30
committed by GitHub
parent 3a1e112b86
commit d3f0162cf5
246 changed files with 5216 additions and 6424 deletions
@@ -1,8 +1,8 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { MessageChannelVisibility } from 'twenty-shared/types';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { TimelineThreadParticipantDTO } from 'src/engine/core-modules/messaging/dtos/timeline-thread-participant.dto';
import { MessageChannelVisibility } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
@ObjectType('TimelineThread')
export class TimelineThreadDTO {
@@ -1,19 +1,32 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { MessageParticipantRole } from 'twenty-shared/types';
import { In } from 'typeorm';
import {
MessageChannelVisibility,
MessageParticipantRole,
} from 'twenty-shared/types';
import { In, type Repository } from 'typeorm';
import { type TimelineThreadDTO } from 'src/engine/core-modules/messaging/dtos/timeline-thread.dto';
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { ConnectedAccountEntity } from 'src/engine/metadata-modules/connected-account/entities/connected-account.entity';
import { MessageChannelEntity } from 'src/engine/metadata-modules/message-channel/entities/message-channel.entity';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
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';
import { type WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
@Injectable()
export class TimelineMessagingService {
constructor(
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
@InjectRepository(MessageChannelEntity)
private readonly messageChannelRepository: Repository<MessageChannelEntity>,
@InjectRepository(ConnectedAccountEntity)
private readonly connectedAccountRepository: Repository<ConnectedAccountEntity>,
@InjectRepository(UserWorkspaceEntity)
private readonly userWorkspaceRepository: Repository<UserWorkspaceEntity>,
) {}
public async getAndCountMessageThreads(
@@ -212,57 +225,126 @@ export class TimelineMessagingService {
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
async () => {
const workspaceMemberRepository =
await this.globalWorkspaceOrmManager.getRepository<WorkspaceMemberWorkspaceEntity>(
workspaceId,
'workspaceMember',
{ shouldBypassPermissionChecks: true },
);
const currentMember = await workspaceMemberRepository.findOne({
where: { id: workspaceMemberId },
select: { userId: true },
});
if (!currentMember) {
return {};
}
const currentUserWorkspace = await this.userWorkspaceRepository.findOne(
{
where: { userId: currentMember.userId, workspaceId },
select: { id: true },
},
);
if (!currentUserWorkspace) {
return {};
}
const currentUserWorkspaceId = currentUserWorkspace.id;
const messageThreadRepository =
await this.globalWorkspaceOrmManager.getRepository<MessageThreadWorkspaceEntity>(
workspaceId,
'messageThread',
);
const threadVisibility = await messageThreadRepository
const threadChannelRows = await messageThreadRepository
.createQueryBuilder()
.select('messageThread.id', 'id')
.addSelect('messageChannel.visibility', 'visibility')
.addSelect('connectedAccount.accountOwnerId', 'accountOwnerId')
.addSelect(
'messageChannelMessageAssociation.messageChannelId',
'messageChannelId',
)
.leftJoin('messageThread.messages', 'message')
.leftJoin(
'message.messageChannelMessageAssociations',
'messageChannelMessageAssociation',
)
.leftJoin(
'messageChannelMessageAssociation.messageChannel',
'messageChannel',
)
.leftJoin('messageChannel.connectedAccount', 'connectedAccount')
.where('messageThread.id = ANY(:messageThreadIds)', {
messageThreadIds: messageThreadIds,
messageThreadIds,
})
.getRawMany();
.getRawMany<{ id: string; messageChannelId: string | null }>();
const allMessageChannelIds = [
...new Set(
threadChannelRows
.map((row) => row.messageChannelId)
.filter((id): id is string => id !== null && id !== undefined),
),
];
if (allMessageChannelIds.length === 0) {
return {};
}
const messageChannels = await this.messageChannelRepository.find({
where: { id: In(allMessageChannelIds), workspaceId },
select: { id: true, visibility: true, connectedAccountId: true },
});
const allConnectedAccountIds = [
...new Set(
messageChannels.map((channel) => channel.connectedAccountId),
),
];
const ownedAccountIds = new Set(
(
await this.connectedAccountRepository.find({
where: {
id: In(allConnectedAccountIds),
userWorkspaceId: currentUserWorkspaceId,
},
select: { id: true },
})
).map((account) => account.id),
);
const channelVisibilityMap = new Map(
messageChannels.map((channel) => [
channel.id,
ownedAccountIds.has(channel.connectedAccountId)
? MessageChannelVisibility.SHARE_EVERYTHING
: channel.visibility,
]),
);
const visibilityValues = Object.values(MessageChannelVisibility);
const threadVisibilityByThreadId: {
[key: string]: MessageChannelVisibility;
} = threadVisibility.reduce((threadVisibilityAcc, threadVisibility) => {
if (threadVisibility.accountOwnerId === workspaceMemberId) {
threadVisibilityAcc[threadVisibility.id] =
MessageChannelVisibility.SHARE_EVERYTHING;
} = {};
return threadVisibilityAcc;
}
for (const { id: threadId, messageChannelId } of threadChannelRows) {
if (!messageChannelId) continue;
threadVisibilityAcc[threadVisibility.id] =
const channelVisibility = channelVisibilityMap.get(messageChannelId);
if (!channelVisibility) continue;
threadVisibilityByThreadId[threadId] =
visibilityValues[
Math.max(
visibilityValues.indexOf(threadVisibility.visibility),
visibilityValues.indexOf(channelVisibility),
visibilityValues.indexOf(
threadVisibilityAcc[threadVisibility.id] ??
threadVisibilityByThreadId[threadId] ??
MessageChannelVisibility.METADATA,
),
)
];
return threadVisibilityAcc;
}, {});
}
return threadVisibilityByThreadId;
},
@@ -1,13 +1,17 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { GetMessagesService } from 'src/engine/core-modules/messaging/services/get-messages.service';
import { TimelineMessagingService } from 'src/engine/core-modules/messaging/services/timeline-messaging.service';
import { TimelineMessagingResolver } from 'src/engine/core-modules/messaging/timeline-messaging.resolver';
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { UserModule } from 'src/engine/core-modules/user/user.module';
import { ConnectedAccountEntity } from 'src/engine/metadata-modules/connected-account/entities/connected-account.entity';
import { MessageChannelEntity } from 'src/engine/metadata-modules/message-channel/entities/message-channel.entity';
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { ConnectedAccountModule } from 'src/modules/connected-account/connected-account.module';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
@Module({
imports: [
@@ -16,6 +20,11 @@ import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permi
ConnectedAccountModule,
FeatureFlagModule,
PermissionsModule,
TypeOrmModule.forFeature([
MessageChannelEntity,
ConnectedAccountEntity,
UserWorkspaceEntity,
]),
],
exports: [],
providers: [
@@ -1,9 +1,9 @@
import { FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED } from 'twenty-shared/constants';
import { isDefined } from 'twenty-shared/utils';
import { MessageChannelVisibility } from 'twenty-shared/types';
import { type TimelineThreadDTO } from 'src/engine/core-modules/messaging/dtos/timeline-thread.dto';
import { extractParticipantSummary } from 'src/engine/core-modules/messaging/utils/extract-participant-summary.util';
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';
export const formatThreads = (