Update workspace entities to make all TEXT nullable (#16144)

Follow up on #15926

---------

Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
Co-authored-by: guillim <guigloo@msn.com>
This commit is contained in:
Charles Bochet
2025-11-28 10:50:38 +01:00
committed by GitHub
parent 9620a4b0ba
commit eb362c6d5f
63 changed files with 396 additions and 136 deletions
@@ -7,10 +7,10 @@ import { msg } from '@lingui/core/macro';
import { render } from '@react-email/render';
import { SendApprovedAccessDomainValidation } from 'twenty-emails';
import { SettingsPath } from 'twenty-shared/types';
import { getSettingsPath } from 'twenty-shared/utils';
import { getSettingsPath, isDefined } from 'twenty-shared/utils';
import { Repository } from 'typeorm';
import { ApprovedAccessDomainEntity as ApprovedAccessDomainEntity } from 'src/engine/core-modules/approved-access-domain/approved-access-domain.entity';
import { ApprovedAccessDomainEntity } from 'src/engine/core-modules/approved-access-domain/approved-access-domain.entity';
import {
ApprovedAccessDomainException,
ApprovedAccessDomainExceptionCode,
@@ -70,6 +70,10 @@ export class ApprovedAccessDomainService {
},
});
if (!isDefined(sender.userEmail)) {
throw new Error(`Sender ${sender.id} has an empty userEmail`);
}
const emailTemplate = SendApprovedAccessDomainValidation({
link: link.toString(),
workspace: {
@@ -112,7 +112,7 @@ export class TimelineCalendarEventService {
participant.person?.avatarUrl ||
participant.workspaceMember?.avatarUrl ||
'',
handle: participant.handle,
handle: participant.handle ?? '',
}),
);
@@ -139,15 +139,17 @@ export class TimelineCalendarEventService {
title:
visibility === CalendarChannelVisibility.METADATA
? FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED
: event.title,
: (event.title ?? ''),
description:
visibility === CalendarChannelVisibility.METADATA
? FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED
: event.description,
: (event.description ?? ''),
startsAt: event.startsAt as unknown as Date,
endsAt: event.endsAt as unknown as Date,
participants,
visibility,
location: event.location ?? '',
conferenceSolution: event.conferenceSolution ?? '',
};
});
@@ -31,7 +31,7 @@ export class FileAttachmentListener {
FileDeletionJob.name,
{
workspaceId: payload.workspaceId,
fullPath: event.properties.before.fullPath,
fullPath: event.properties.before.fullPath ?? '',
},
);
}
@@ -35,7 +35,7 @@ export class FileWorkspaceMemberListener {
this.messageQueueService.add<FileDeletionJobData>(FileDeletionJob.name, {
workspaceId: payload.workspaceId,
fullPath: event.properties.before.avatarUrl,
fullPath: event.properties.before.avatarUrl ?? '',
});
}
}
@@ -8,6 +8,7 @@ import {
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { ConnectedAccountProvider } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { AuthGraphqlApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter';
@@ -59,7 +60,7 @@ export class ImapSmtpCaldavResolver {
where: { id, provider: ConnectedAccountProvider.IMAP_SMTP_CALDAV },
});
if (!connectedAccount) {
if (!isDefined(connectedAccount) || !isDefined(connectedAccount?.handle)) {
throw new UserInputError(
`Connected mail account with ID ${id} not found`,
);
@@ -75,8 +75,8 @@ export class TimelineMessagingService {
return {
id: messageThread.id,
subject: firstMessage.subject,
lastMessageBody: lastMessage.text,
subject: firstMessage.subject ?? '',
lastMessageBody: lastMessage.text ?? '',
lastMessageReceivedAt: lastMessage.receivedAt ?? new Date(),
numberOfMessagesInThread: messageThread.messages.length,
};
@@ -1,30 +1,40 @@
import { isDefined } from 'twenty-shared/utils';
import { type TimelineThreadParticipantDTO } from 'src/engine/core-modules/messaging/dtos/timeline-thread-participant.dto';
import { type MessageParticipantWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-participant.workspace-entity';
export const formatThreadParticipant = (
threadParticipant: MessageParticipantWorkspaceEntity,
): TimelineThreadParticipantDTO => ({
personId: threadParticipant.personId,
workspaceMemberId: threadParticipant.workspaceMemberId,
firstName:
threadParticipant.person?.name?.firstName ||
threadParticipant.workspaceMember?.name.firstName ||
'',
lastName:
threadParticipant.person?.name?.lastName ||
threadParticipant.workspaceMember?.name.lastName ||
'',
displayName:
threadParticipant.person?.name?.firstName ||
threadParticipant.person?.name?.lastName ||
threadParticipant.workspaceMember?.name.firstName ||
threadParticipant.workspaceMember?.name.lastName ||
threadParticipant.displayName ||
threadParticipant.handle ||
'',
avatarUrl:
threadParticipant.person?.avatarUrl ||
threadParticipant.workspaceMember?.avatarUrl ||
'',
handle: threadParticipant.handle,
});
): TimelineThreadParticipantDTO => {
if (!isDefined(threadParticipant.handle)) {
throw new Error(
`Thread participant ${threadParticipant.id} has an empty handle`,
);
}
return {
personId: threadParticipant.personId,
workspaceMemberId: threadParticipant.workspaceMemberId,
firstName:
threadParticipant.person?.name?.firstName ||
threadParticipant.workspaceMember?.name.firstName ||
'',
lastName:
threadParticipant.person?.name?.lastName ||
threadParticipant.workspaceMember?.name.lastName ||
'',
displayName:
threadParticipant.person?.name?.firstName ||
threadParticipant.person?.name?.lastName ||
threadParticipant.workspaceMember?.name.firstName ||
threadParticipant.workspaceMember?.name.lastName ||
threadParticipant.displayName ||
threadParticipant.handle ||
'',
avatarUrl:
threadParticipant.person?.avatarUrl ||
threadParticipant.workspaceMember?.avatarUrl ||
'',
handle: threadParticipant.handle,
};
};
@@ -75,6 +75,10 @@ export class WorkspaceMemberTranspiler {
const roles = fromRoleEntitiesToRoleDtos(userWorkspaceRoles);
if (!isDefined(userEmail)) {
throw new Error(`Workspace member ${id} has no userEmail`);
}
return {
id,
name,
@@ -111,6 +115,10 @@ export class WorkspaceMemberTranspiler {
userEmail,
} = workspaceMember;
if (!isDefined(userEmail)) {
throw new Error(`Workspace member ${id} has no userEmail`);
}
const avatarUrl = userWorkspaceId
? this.generateSignedAvatarUrl({
workspaceId: userWorkspaceId,
@@ -4,6 +4,7 @@ import { Args, Mutation, Resolver } from '@nestjs/graphql';
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 { CreateDraftFromWorkflowVersionInput } from 'src/engine/core-modules/workflow/dtos/create-draft-from-workflow-version-input.dto';
import { DuplicateWorkflowInput } from 'src/engine/core-modules/workflow/dtos/duplicate-workflow-input.dto';
import { UpdateWorkflowVersionPositionsInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-version-positions-input.dto';
import { WorkflowVersionDTO } from 'src/engine/core-modules/workflow/dtos/workflow-version.dto';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
@@ -14,7 +15,6 @@ import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
import { WorkflowVersionWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service';
import { DuplicateWorkflowInput } from 'src/engine/core-modules/workflow/dtos/duplicate-workflow-input.dto';
@Resolver()
@UsePipes(ResolverValidationPipe)
@@ -306,6 +306,13 @@ export class WorkspaceInvitationService {
: {},
});
if (!isDefined(sender.userEmail)) {
throw new WorkspaceInvitationException(
'Sender email is missing',
WorkspaceInvitationExceptionCode.EMAIL_MISSING,
);
}
const emailData = {
link: link.toString(),
workspace: {