Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a84f5df7f | |||
| 2c144715b4 | |||
| 51fa78c0b4 |
@@ -5,7 +5,7 @@ import { Avatar } from 'twenty-ui/display';
|
||||
import { UndecoratedLink } from 'twenty-ui/navigation';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import { useRedirectToDefaultDomain } from '~/modules/domain-manager/hooks/useRedirectToDefaultDomain';
|
||||
import { useReadDefaultDomainFromConfiguration } from '~/modules/domain-manager/hooks/useReadDefaultDomainFromConfiguration';
|
||||
import { AppPath } from '~/modules/types/AppPath';
|
||||
|
||||
type LogoProps = {
|
||||
primaryLogo?: string | null;
|
||||
@@ -59,8 +59,6 @@ export const Logo = ({
|
||||
const { redirectToDefaultDomain } = useRedirectToDefaultDomain();
|
||||
const defaultPrimaryLogoUrl = `${window.location.origin}/images/icons/android/android-launchericon-192-192.png`;
|
||||
|
||||
const { defaultUrl } = useReadDefaultDomainFromConfiguration();
|
||||
|
||||
const primaryLogoUrl = getImageAbsoluteURI({
|
||||
imageUrl: primaryLogo ?? defaultPrimaryLogoUrl,
|
||||
baseUrl: REACT_APP_SERVER_BASE_URL,
|
||||
@@ -79,7 +77,7 @@ export const Logo = ({
|
||||
<StyledContainer onClick={() => onClick?.()}>
|
||||
{isUsingDefaultLogo ? (
|
||||
<UndecoratedLink
|
||||
to={defaultUrl.toString()}
|
||||
to={AppPath.SignInUp}
|
||||
onClick={redirectToDefaultDomain}
|
||||
>
|
||||
<StyledPrimaryLogo src={primaryLogoUrl} />
|
||||
|
||||
+1
-8
@@ -3,18 +3,15 @@ import {
|
||||
signInUpStepState,
|
||||
} from '@/auth/states/signInUpStepState';
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useAuth } from '@/auth/hooks/useAuth';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { availableWorkspacesState } from '@/auth/states/availableWorkspacesState';
|
||||
import { countAvailableWorkspaces } from '@/auth/utils/availableWorkspacesUtils';
|
||||
|
||||
export const SignInUpGlobalScopeFormEffect = () => {
|
||||
const setSignInUpStep = useSetRecoilState(signInUpStepState);
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const { setAuthTokens, loadCurrentUser } = useAuth();
|
||||
const availableWorkspaces = useRecoilValue(availableWorkspacesState);
|
||||
|
||||
useEffect(() => {
|
||||
const tokenPair = searchParams.get('tokenPair');
|
||||
@@ -23,9 +20,6 @@ export const SignInUpGlobalScopeFormEffect = () => {
|
||||
searchParams.delete('tokenPair');
|
||||
setSearchParams(searchParams);
|
||||
loadCurrentUser();
|
||||
}
|
||||
|
||||
if (countAvailableWorkspaces(availableWorkspaces) > 1) {
|
||||
setSignInUpStep(SignInUpStep.WorkspaceSelection);
|
||||
}
|
||||
}, [
|
||||
@@ -34,7 +28,6 @@ export const SignInUpGlobalScopeFormEffect = () => {
|
||||
setSignInUpStep,
|
||||
loadCurrentUser,
|
||||
setAuthTokens,
|
||||
availableWorkspaces,
|
||||
]);
|
||||
|
||||
return <></>;
|
||||
|
||||
-4
@@ -10,11 +10,7 @@ export const useReadDefaultDomainFromConfiguration = () => {
|
||||
? `${domainConfiguration.defaultSubdomain}.${domainConfiguration.frontDomain}`
|
||||
: domainConfiguration.frontDomain;
|
||||
|
||||
const defaultUrl = new URL(window.location.href);
|
||||
defaultUrl.hostname = defaultDomain;
|
||||
|
||||
return {
|
||||
defaultDomain,
|
||||
defaultUrl,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { useSignInUp } from '@/auth/sign-in-up/hooks/useSignInUp';
|
||||
import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm';
|
||||
import { SignInUpStep } from '@/auth/states/signInUpStepState';
|
||||
import {
|
||||
SignInUpStep,
|
||||
signInUpStepState,
|
||||
} from '@/auth/states/signInUpStepState';
|
||||
import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { Logo } from '@/auth/components/Logo';
|
||||
import { Title } from '@/auth/components/Title';
|
||||
@@ -27,7 +30,6 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { AnimatedEaseIn } from 'twenty-ui/utilities';
|
||||
import { PublicWorkspaceDataOutput } from '~/generated/graphql';
|
||||
import { SignInUpGlobalScopeFormEffect } from '@/auth/sign-in-up/components/internal/SignInUpGlobalScopeFormEffect';
|
||||
import { useAuth } from '@/auth/hooks/useAuth';
|
||||
|
||||
const StandardContent = ({
|
||||
workspacePublicData,
|
||||
@@ -60,6 +62,7 @@ const StandardContent = ({
|
||||
|
||||
export const SignInUp = () => {
|
||||
const { t } = useLingui();
|
||||
const setSignInUpStep = useSetRecoilState(signInUpStepState);
|
||||
|
||||
const { form } = useSignInUpForm();
|
||||
const { signInUpStep } = useSignInUp(form);
|
||||
@@ -70,14 +73,11 @@ export const SignInUp = () => {
|
||||
const isMultiWorkspaceEnabled = useRecoilValue(isMultiWorkspaceEnabledState);
|
||||
const { workspaceInviteHash, workspace: workspaceFromInviteHash } =
|
||||
useWorkspaceFromInviteHash();
|
||||
const { signOut } = useAuth();
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
const onClickOnLogo = () => {
|
||||
if (!isOnAWorkspace && signInUpStep === SignInUpStep.WorkspaceSelection) {
|
||||
signOut();
|
||||
}
|
||||
setSignInUpStep(SignInUpStep.Init);
|
||||
};
|
||||
|
||||
const title = useMemo(() => {
|
||||
|
||||
+6
@@ -165,10 +165,16 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
afterSyncMetadata: [],
|
||||
};
|
||||
|
||||
const commands_060: VersionCommands = {
|
||||
afterSyncMetadata: [],
|
||||
beforeSyncMetadata: [],
|
||||
};
|
||||
|
||||
this.allCommands = {
|
||||
'0.53.0': commands_053,
|
||||
'0.54.0': commands_054,
|
||||
'0.55.0': commands_055,
|
||||
'0.60.0': commands_060,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+18
-24
@@ -399,24 +399,23 @@ export class UserWorkspaceService extends TypeOrmQueryService<UserWorkspace> {
|
||||
displayName: workspace.displayName,
|
||||
workspaceUrls: this.domainManagerService.getWorkspaceUrls(workspace),
|
||||
logo: workspace.logo,
|
||||
sso:
|
||||
workspace.workspaceSSOIdentityProviders?.reduce(
|
||||
(acc, identityProvider) =>
|
||||
acc.concat(
|
||||
identityProvider.status === 'Inactive'
|
||||
? []
|
||||
: [
|
||||
{
|
||||
id: identityProvider.id,
|
||||
name: identityProvider.name,
|
||||
issuer: identityProvider.issuer,
|
||||
type: identityProvider.type,
|
||||
status: identityProvider.status,
|
||||
},
|
||||
],
|
||||
),
|
||||
[] as AvailableWorkspace['sso'],
|
||||
) ?? [],
|
||||
sso: workspace.workspaceSSOIdentityProviders.reduce(
|
||||
(acc, identityProvider) =>
|
||||
acc.concat(
|
||||
identityProvider.status === 'Inactive'
|
||||
? []
|
||||
: [
|
||||
{
|
||||
id: identityProvider.id,
|
||||
name: identityProvider.name,
|
||||
issuer: identityProvider.issuer,
|
||||
type: identityProvider.type,
|
||||
status: identityProvider.status,
|
||||
},
|
||||
],
|
||||
),
|
||||
[] as AvailableWorkspace['sso'],
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -440,12 +439,7 @@ export class UserWorkspaceService extends TypeOrmQueryService<UserWorkspace> {
|
||||
({ workspace, appToken }) => {
|
||||
return {
|
||||
...this.castWorkspaceToAvailableWorkspace(workspace),
|
||||
...(appToken
|
||||
? {
|
||||
personalInviteToken: appToken.value,
|
||||
inviteHash: workspace.inviteHash,
|
||||
}
|
||||
: {}),
|
||||
...(appToken ? { personalInviteToken: appToken.value } : {}),
|
||||
};
|
||||
},
|
||||
),
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ describe('formatGoogleCalendarEvents', () => {
|
||||
expect(formattedEvent.isFullDay).toBe(false);
|
||||
expect(formattedEvent.startsAt).toBe('2023-01-15T14:00:00Z');
|
||||
expect(formattedEvent.endsAt).toBe('2023-01-15T15:00:00Z');
|
||||
expect(formattedEvent.externalId).toBe('event123');
|
||||
expect(formattedEvent.id).toBe('event123');
|
||||
expect(formattedEvent.conferenceSolution).toBe('hangoutsMeet');
|
||||
expect(formattedEvent.conferenceLinkUrl).toBe(
|
||||
'https://meet.google.com/abc-defg-hij',
|
||||
|
||||
+11
-11
@@ -2,17 +2,17 @@ import { calendar_v3 as calendarV3 } from 'googleapis';
|
||||
|
||||
import { sanitizeCalendarEvent } from 'src/modules/calendar/calendar-event-import-manager/drivers/utils/sanitizeCalendarEvent';
|
||||
import { CalendarEventParticipantResponseStatus } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
|
||||
import { CalendarEventWithParticipants } from 'src/modules/calendar/common/types/calendar-event';
|
||||
import { FetchedCalendarEvent } from 'src/modules/calendar/common/types/fetched-calendar-event';
|
||||
|
||||
export const formatGoogleCalendarEvents = (
|
||||
events: calendarV3.Schema$Event[],
|
||||
): CalendarEventWithParticipants[] => {
|
||||
): FetchedCalendarEvent[] => {
|
||||
return events.map(formatGoogleCalendarEvent);
|
||||
};
|
||||
|
||||
const formatGoogleCalendarEvent = (
|
||||
event: calendarV3.Schema$Event,
|
||||
): CalendarEventWithParticipants => {
|
||||
): FetchedCalendarEvent => {
|
||||
const formatResponseStatus = (status: string | null | undefined) => {
|
||||
switch (status) {
|
||||
case 'accepted':
|
||||
@@ -27,15 +27,15 @@ const formatGoogleCalendarEvent = (
|
||||
};
|
||||
|
||||
// Create the event object
|
||||
const calendarEvent: CalendarEventWithParticipants = {
|
||||
const calendarEvent: FetchedCalendarEvent = {
|
||||
title: event.summary ?? '',
|
||||
isCanceled: event.status === 'cancelled',
|
||||
isFullDay: event.start?.dateTime == null,
|
||||
startsAt: event.start?.dateTime ?? event.start?.date ?? null,
|
||||
endsAt: event.end?.dateTime ?? event.end?.date ?? null,
|
||||
externalId: event.id ?? '',
|
||||
externalCreatedAt: event.created ?? null,
|
||||
externalUpdatedAt: event.updated ?? null,
|
||||
startsAt: event.start?.dateTime ?? event.start?.date ?? '',
|
||||
endsAt: event.end?.dateTime ?? event.end?.date ?? '',
|
||||
id: event.id ?? '',
|
||||
externalCreatedAt: event.created ?? '',
|
||||
externalUpdatedAt: event.updated ?? '',
|
||||
description: event.description ?? '',
|
||||
location: event.location ?? '',
|
||||
iCalUID: event.iCalUID ?? '',
|
||||
@@ -54,11 +54,11 @@ const formatGoogleCalendarEvent = (
|
||||
status: event.status ?? '',
|
||||
};
|
||||
|
||||
const propertiesToSanitize: (keyof CalendarEventWithParticipants)[] = [
|
||||
const propertiesToSanitize: (keyof FetchedCalendarEvent)[] = [
|
||||
'title',
|
||||
'startsAt',
|
||||
'endsAt',
|
||||
'externalId',
|
||||
'id',
|
||||
'externalCreatedAt',
|
||||
'externalUpdatedAt',
|
||||
'description',
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ import { Event } from '@microsoft/microsoft-graph-types';
|
||||
|
||||
import { formatMicrosoftCalendarEvents } from 'src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/utils/format-microsoft-calendar-event.util';
|
||||
import { parseMicrosoftCalendarError } from 'src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/utils/parse-microsoft-calendar-error.util';
|
||||
import { CalendarEventWithParticipants } from 'src/modules/calendar/common/types/calendar-event';
|
||||
import { FetchedCalendarEvent } from 'src/modules/calendar/common/types/fetched-calendar-event';
|
||||
import { MicrosoftOAuth2ClientManagerService } from 'src/modules/connected-account/oauth2-client-manager/drivers/microsoft/microsoft-oauth2-client-manager.service';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
|
||||
@@ -20,7 +20,7 @@ export class MicrosoftCalendarImportEventsService {
|
||||
'provider' | 'refreshToken' | 'id'
|
||||
>,
|
||||
changedEventIds: string[],
|
||||
): Promise<CalendarEventWithParticipants[]> {
|
||||
): Promise<FetchedCalendarEvent[]> {
|
||||
try {
|
||||
const microsoftClient =
|
||||
await this.microsoftOAuth2ClientManagerService.getOAuth2Client(
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ describe('formatMicrosoftCalendarEvents', () => {
|
||||
expect(formattedEvent.isFullDay).toBe(false);
|
||||
expect(formattedEvent.startsAt).toBe('2023-01-15T14:00:00Z');
|
||||
expect(formattedEvent.endsAt).toBe('2023-01-15T15:00:00Z');
|
||||
expect(formattedEvent.externalId).toBe('event123');
|
||||
expect(formattedEvent.id).toBe('event123');
|
||||
expect(formattedEvent.conferenceSolution).toBe('teamsForBusiness');
|
||||
expect(formattedEvent.conferenceLinkUrl).toBe(
|
||||
'https://teams.microsoft.com/l/meetup-join/abc123',
|
||||
|
||||
+11
-13
@@ -6,17 +6,15 @@ import {
|
||||
|
||||
import { sanitizeCalendarEvent } from 'src/modules/calendar/calendar-event-import-manager/drivers/utils/sanitizeCalendarEvent';
|
||||
import { CalendarEventParticipantResponseStatus } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
|
||||
import { CalendarEventWithParticipants } from 'src/modules/calendar/common/types/calendar-event';
|
||||
import { FetchedCalendarEvent } from 'src/modules/calendar/common/types/fetched-calendar-event';
|
||||
|
||||
export const formatMicrosoftCalendarEvents = (
|
||||
events: Event[],
|
||||
): CalendarEventWithParticipants[] => {
|
||||
): FetchedCalendarEvent[] => {
|
||||
return events.map(formatMicrosoftCalendarEvent);
|
||||
};
|
||||
|
||||
const formatMicrosoftCalendarEvent = (
|
||||
event: Event,
|
||||
): CalendarEventWithParticipants => {
|
||||
const formatMicrosoftCalendarEvent = (event: Event): FetchedCalendarEvent => {
|
||||
const formatResponseStatus = (
|
||||
status: NullableOption<ResponseType> | undefined,
|
||||
) => {
|
||||
@@ -33,15 +31,15 @@ const formatMicrosoftCalendarEvent = (
|
||||
}
|
||||
};
|
||||
|
||||
const calendarEvent: CalendarEventWithParticipants = {
|
||||
const calendarEvent: FetchedCalendarEvent = {
|
||||
title: event.subject ?? '',
|
||||
isCanceled: !!event.isCancelled,
|
||||
isFullDay: !!event.isAllDay,
|
||||
startsAt: event.start?.dateTime ?? null,
|
||||
endsAt: event.end?.dateTime ?? null,
|
||||
externalId: event.id ?? '',
|
||||
externalCreatedAt: event.createdDateTime ?? null,
|
||||
externalUpdatedAt: event.lastModifiedDateTime ?? null,
|
||||
startsAt: event.start?.dateTime ?? '',
|
||||
endsAt: event.end?.dateTime ?? '',
|
||||
id: event.id ?? '',
|
||||
externalCreatedAt: event.createdDateTime ?? '',
|
||||
externalUpdatedAt: event.lastModifiedDateTime ?? '',
|
||||
description: event.body?.content ?? '',
|
||||
location: event.location?.displayName ?? '',
|
||||
iCalUID: event.iCalUId ?? '',
|
||||
@@ -59,11 +57,11 @@ const formatMicrosoftCalendarEvent = (
|
||||
status: '',
|
||||
};
|
||||
|
||||
const propertiesToSanitize: (keyof CalendarEventWithParticipants)[] = [
|
||||
const propertiesToSanitize: (keyof FetchedCalendarEvent)[] = [
|
||||
'title',
|
||||
'startsAt',
|
||||
'endsAt',
|
||||
'externalId',
|
||||
'id',
|
||||
'externalCreatedAt',
|
||||
'externalUpdatedAt',
|
||||
'description',
|
||||
|
||||
+4
-4
@@ -21,7 +21,7 @@ import { filterEventsAndReturnCancelledEvents } from 'src/modules/calendar/calen
|
||||
import { CalendarChannelSyncStatusService } from 'src/modules/calendar/common/services/calendar-channel-sync-status.service';
|
||||
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel-event-association.workspace-entity';
|
||||
import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import { CalendarEventWithParticipants } from 'src/modules/calendar/common/types/calendar-event';
|
||||
import { FetchedCalendarEvent } from 'src/modules/calendar/common/types/fetched-calendar-event';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
@@ -43,13 +43,13 @@ export class CalendarEventsImportService {
|
||||
calendarChannel: CalendarChannelWorkspaceEntity,
|
||||
connectedAccount: ConnectedAccountWorkspaceEntity,
|
||||
workspaceId: string,
|
||||
fetchedCalendarEvents?: CalendarEventWithParticipants[],
|
||||
fetchedCalendarEvents?: FetchedCalendarEvent[],
|
||||
): Promise<void> {
|
||||
await this.calendarChannelSyncStatusService.markAsCalendarEventsImportOngoing(
|
||||
[calendarChannel.id],
|
||||
);
|
||||
|
||||
let calendarEvents: CalendarEventWithParticipants[] = [];
|
||||
let calendarEvents: FetchedCalendarEvent[] = [];
|
||||
|
||||
try {
|
||||
if (fetchedCalendarEvents) {
|
||||
@@ -103,7 +103,7 @@ export class CalendarEventsImportService {
|
||||
);
|
||||
|
||||
const cancelledEventExternalIds = cancelledEvents.map(
|
||||
(event) => event.externalId,
|
||||
(event) => event.id,
|
||||
);
|
||||
|
||||
const BATCH_SIZE = 1000;
|
||||
|
||||
+2
-2
@@ -8,12 +8,12 @@ import {
|
||||
CalendarEventImportException,
|
||||
CalendarEventImportExceptionCode,
|
||||
} from 'src/modules/calendar/calendar-event-import-manager/exceptions/calendar-event-import.exception';
|
||||
import { CalendarEventWithParticipants } from 'src/modules/calendar/common/types/calendar-event';
|
||||
import { FetchedCalendarEvent } from 'src/modules/calendar/common/types/fetched-calendar-event';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
|
||||
export type GetCalendarEventsResponse = {
|
||||
fullEvents: boolean;
|
||||
calendarEvents?: CalendarEventWithParticipants[];
|
||||
calendarEvents?: FetchedCalendarEvent[];
|
||||
calendarEventIds?: string[];
|
||||
nextSyncCursor: string;
|
||||
};
|
||||
|
||||
+192
-135
@@ -2,35 +2,30 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { Any } from 'typeorm';
|
||||
|
||||
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
|
||||
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 { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
|
||||
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { injectIdsInCalendarEvents } from 'src/modules/calendar/calendar-event-import-manager/utils/inject-ids-in-calendar-events.util';
|
||||
import { CalendarEventParticipantService } from 'src/modules/calendar/calendar-event-participant-manager/services/calendar-event-participant.service';
|
||||
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel-event-association.workspace-entity';
|
||||
import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
|
||||
import { CalendarEventWithParticipants } from 'src/modules/calendar/common/types/calendar-event';
|
||||
import { FetchedCalendarEvent } from 'src/modules/calendar/common/types/fetched-calendar-event';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import {
|
||||
CreateCompanyAndContactJob,
|
||||
CreateCompanyAndContactJobData,
|
||||
} from 'src/modules/contact-creation-manager/jobs/create-company-and-contact.job';
|
||||
|
||||
type FetchedCalendarEventWithDBEvent = {
|
||||
fetchedCalendarEvent: FetchedCalendarEvent;
|
||||
existingCalendarEvent: CalendarEventWorkspaceEntity | null;
|
||||
newlyCreatedCalendarEvent: CalendarEventWorkspaceEntity | null;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class CalendarSaveEventsService {
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly calendarEventParticipantService: CalendarEventParticipantService,
|
||||
@InjectMessageQueue(MessageQueue.contactCreationQueue)
|
||||
private readonly messageQueueService: MessageQueueService,
|
||||
) {}
|
||||
|
||||
public async saveCalendarEventsAndEnqueueContactCreationJob(
|
||||
filteredEvents: CalendarEventWithParticipants[],
|
||||
fetchedCalendarEvents: FetchedCalendarEvent[],
|
||||
calendarChannel: CalendarChannelWorkspaceEntity,
|
||||
connectedAccount: ConnectedAccountWorkspaceEntity,
|
||||
workspaceId: string,
|
||||
@@ -40,158 +35,220 @@ export class CalendarSaveEventsService {
|
||||
'calendarEvent',
|
||||
);
|
||||
|
||||
const existingCalendarEvents = await calendarEventRepository.find({
|
||||
where: {
|
||||
iCalUID: Any(filteredEvents.map((event) => event.iCalUID as string)),
|
||||
},
|
||||
});
|
||||
|
||||
const iCalUIDCalendarEventIdMap = new Map(
|
||||
existingCalendarEvents.map((calendarEvent) => [
|
||||
calendarEvent.iCalUID,
|
||||
calendarEvent.id,
|
||||
]),
|
||||
);
|
||||
|
||||
const calendarEventsWithIds = injectIdsInCalendarEvents(
|
||||
filteredEvents,
|
||||
iCalUIDCalendarEventIdMap,
|
||||
);
|
||||
|
||||
// TODO: When we will be able to add unicity contraint on iCalUID, we will do a INSERT ON CONFLICT DO UPDATE
|
||||
|
||||
const existingEventsICalUIDs = existingCalendarEvents.map(
|
||||
(calendarEvent) => calendarEvent.iCalUID,
|
||||
);
|
||||
|
||||
const eventsToSave = calendarEventsWithIds.filter(
|
||||
(calendarEvent) =>
|
||||
!existingEventsICalUIDs.includes(calendarEvent.iCalUID),
|
||||
);
|
||||
|
||||
const eventsToUpdate = calendarEventsWithIds.filter((calendarEvent) =>
|
||||
existingEventsICalUIDs.includes(calendarEvent.iCalUID),
|
||||
);
|
||||
|
||||
const calendarChannelEventAssociationRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarChannelEventAssociationWorkspaceEntity>(
|
||||
'calendarChannelEventAssociation',
|
||||
);
|
||||
|
||||
const existingCalendarChannelEventAssociations =
|
||||
await calendarChannelEventAssociationRepository.find({
|
||||
where: {
|
||||
eventExternalId: Any(
|
||||
calendarEventsWithIds.map((calendarEvent) => calendarEvent.id),
|
||||
),
|
||||
calendarChannel: {
|
||||
id: calendarChannel.id,
|
||||
},
|
||||
},
|
||||
const existingCalendarEvents = await calendarEventRepository.find({
|
||||
where: {
|
||||
iCalUID: Any(
|
||||
fetchedCalendarEvents.map((event) => event.iCalUID as string),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
const fetchedCalendarEventsWithDBEvents: FetchedCalendarEventWithDBEvent[] =
|
||||
fetchedCalendarEvents.map((event): FetchedCalendarEventWithDBEvent => {
|
||||
const existingEventWithSameiCalUID = existingCalendarEvents.find(
|
||||
(existingEvent) => existingEvent.iCalUID === event.iCalUID,
|
||||
);
|
||||
|
||||
return {
|
||||
fetchedCalendarEvent: event,
|
||||
existingCalendarEvent: existingEventWithSameiCalUID ?? null,
|
||||
newlyCreatedCalendarEvent: null,
|
||||
};
|
||||
});
|
||||
|
||||
const calendarChannelEventAssociationsToSave = calendarEventsWithIds
|
||||
.filter(
|
||||
(calendarEvent) =>
|
||||
!existingCalendarChannelEventAssociations.some(
|
||||
(association) => association.eventExternalId === calendarEvent.id,
|
||||
),
|
||||
)
|
||||
.map((calendarEvent) => ({
|
||||
calendarEventId: calendarEvent.id,
|
||||
eventExternalId: calendarEvent.externalId,
|
||||
calendarChannelId: calendarChannel.id,
|
||||
recurringEventExternalId: calendarEvent.recurringEventExternalId,
|
||||
}));
|
||||
|
||||
const participantsToSave = eventsToSave.flatMap(
|
||||
(event) => event.participants,
|
||||
);
|
||||
|
||||
const participantsToUpdate = eventsToUpdate.flatMap(
|
||||
(event) => event.participants,
|
||||
);
|
||||
|
||||
const workspaceDataSource = await this.twentyORMManager.getDatasource();
|
||||
|
||||
await workspaceDataSource?.transaction(
|
||||
await workspaceDataSource.transaction(
|
||||
async (transactionManager: WorkspaceEntityManager) => {
|
||||
await calendarEventRepository.save(
|
||||
eventsToSave.map(
|
||||
(calendarEvent) =>
|
||||
({
|
||||
id: calendarEvent.id,
|
||||
iCalUID: calendarEvent.iCalUID,
|
||||
title: calendarEvent.title,
|
||||
description: calendarEvent.description,
|
||||
startsAt: calendarEvent.startsAt,
|
||||
endsAt: calendarEvent.endsAt,
|
||||
location: calendarEvent.location,
|
||||
isFullDay: calendarEvent.isFullDay,
|
||||
isCanceled: calendarEvent.isCanceled,
|
||||
conferenceSolution: calendarEvent.conferenceSolution,
|
||||
conferenceLink: {
|
||||
primaryLinkLabel: calendarEvent.conferenceLinkLabel,
|
||||
primaryLinkUrl: calendarEvent.conferenceLinkUrl,
|
||||
},
|
||||
externalCreatedAt: calendarEvent.externalCreatedAt,
|
||||
externalUpdatedAt: calendarEvent.externalUpdatedAt,
|
||||
}) satisfies DeepPartial<CalendarEventWorkspaceEntity>,
|
||||
),
|
||||
const savedCalendarEvents = await calendarEventRepository.save(
|
||||
fetchedCalendarEventsWithDBEvents
|
||||
.filter(
|
||||
({ existingCalendarEvent }) => existingCalendarEvent === null,
|
||||
)
|
||||
.map(
|
||||
({ fetchedCalendarEvent }) =>
|
||||
({
|
||||
iCalUID: fetchedCalendarEvent.iCalUID,
|
||||
title: fetchedCalendarEvent.title,
|
||||
description: fetchedCalendarEvent.description,
|
||||
startsAt: fetchedCalendarEvent.startsAt,
|
||||
endsAt: fetchedCalendarEvent.endsAt,
|
||||
location: fetchedCalendarEvent.location,
|
||||
isFullDay: fetchedCalendarEvent.isFullDay,
|
||||
isCanceled: fetchedCalendarEvent.isCanceled,
|
||||
conferenceSolution: fetchedCalendarEvent.conferenceSolution,
|
||||
conferenceLink: {
|
||||
primaryLinkLabel: fetchedCalendarEvent.conferenceLinkLabel,
|
||||
primaryLinkUrl: fetchedCalendarEvent.conferenceLinkUrl,
|
||||
secondaryLinks: [],
|
||||
},
|
||||
externalCreatedAt: fetchedCalendarEvent.externalCreatedAt,
|
||||
externalUpdatedAt: fetchedCalendarEvent.externalUpdatedAt,
|
||||
}) satisfies Omit<
|
||||
CalendarEventWorkspaceEntity,
|
||||
| 'id'
|
||||
| 'calendarChannelEventAssociations'
|
||||
| 'calendarEventParticipants'
|
||||
| 'createdAt'
|
||||
| 'updatedAt'
|
||||
| 'deletedAt'
|
||||
>,
|
||||
),
|
||||
{},
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
const fetchedCalendarEventsWithDBEventsEnrichedWithSavedEvents: FetchedCalendarEventWithDBEvent[] =
|
||||
fetchedCalendarEventsWithDBEvents.map(
|
||||
({ fetchedCalendarEvent, existingCalendarEvent }) => {
|
||||
const savedCalendarEvent = savedCalendarEvents.find(
|
||||
(savedCalendarEvent) =>
|
||||
savedCalendarEvent.iCalUID === fetchedCalendarEvent.iCalUID,
|
||||
);
|
||||
|
||||
return {
|
||||
fetchedCalendarEvent,
|
||||
existingCalendarEvent: existingCalendarEvent,
|
||||
newlyCreatedCalendarEvent: savedCalendarEvent ?? null,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
await calendarEventRepository.save(
|
||||
eventsToUpdate.map(
|
||||
(calendarEvent) =>
|
||||
({
|
||||
id: calendarEvent.id,
|
||||
iCalUID: calendarEvent.iCalUID,
|
||||
title: calendarEvent.title,
|
||||
description: calendarEvent.description,
|
||||
startsAt: calendarEvent.startsAt,
|
||||
endsAt: calendarEvent.endsAt,
|
||||
location: calendarEvent.location,
|
||||
isFullDay: calendarEvent.isFullDay,
|
||||
isCanceled: calendarEvent.isCanceled,
|
||||
conferenceSolution: calendarEvent.conferenceSolution,
|
||||
fetchedCalendarEventsWithDBEventsEnrichedWithSavedEvents
|
||||
.filter(
|
||||
({ existingCalendarEvent }) => existingCalendarEvent !== null,
|
||||
)
|
||||
.map(({ fetchedCalendarEvent, existingCalendarEvent }) => {
|
||||
if (!existingCalendarEvent) {
|
||||
throw new Error(
|
||||
`Existing calendar event with iCalUID ${fetchedCalendarEvent.iCalUID} not found - should never happen`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
id: existingCalendarEvent.id,
|
||||
iCalUID: fetchedCalendarEvent.iCalUID,
|
||||
title: fetchedCalendarEvent.title,
|
||||
description: fetchedCalendarEvent.description,
|
||||
startsAt: fetchedCalendarEvent.startsAt,
|
||||
endsAt: fetchedCalendarEvent.endsAt,
|
||||
location: fetchedCalendarEvent.location,
|
||||
isFullDay: fetchedCalendarEvent.isFullDay,
|
||||
isCanceled: fetchedCalendarEvent.isCanceled,
|
||||
conferenceSolution: fetchedCalendarEvent.conferenceSolution,
|
||||
conferenceLink: {
|
||||
primaryLinkLabel: calendarEvent.conferenceLinkLabel,
|
||||
primaryLinkUrl: calendarEvent.conferenceLinkUrl,
|
||||
primaryLinkLabel: fetchedCalendarEvent.conferenceLinkLabel,
|
||||
primaryLinkUrl: fetchedCalendarEvent.conferenceLinkUrl,
|
||||
secondaryLinks: [],
|
||||
},
|
||||
externalCreatedAt: calendarEvent.externalCreatedAt,
|
||||
externalUpdatedAt: calendarEvent.externalUpdatedAt,
|
||||
}) satisfies DeepPartial<CalendarEventWorkspaceEntity>,
|
||||
),
|
||||
externalCreatedAt: fetchedCalendarEvent.externalCreatedAt,
|
||||
externalUpdatedAt: fetchedCalendarEvent.externalUpdatedAt,
|
||||
} satisfies Omit<
|
||||
CalendarEventWorkspaceEntity,
|
||||
| 'calendarChannelEventAssociations'
|
||||
| 'calendarEventParticipants'
|
||||
| 'createdAt'
|
||||
| 'updatedAt'
|
||||
| 'deletedAt'
|
||||
>;
|
||||
}),
|
||||
{},
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
const calendarChannelEventAssociationsToSave: Pick<
|
||||
CalendarChannelEventAssociationWorkspaceEntity,
|
||||
| 'calendarEventId'
|
||||
| 'eventExternalId'
|
||||
| 'calendarChannelId'
|
||||
| 'recurringEventExternalId'
|
||||
>[] = fetchedCalendarEventsWithDBEventsEnrichedWithSavedEvents.map(
|
||||
({
|
||||
fetchedCalendarEvent,
|
||||
existingCalendarEvent,
|
||||
newlyCreatedCalendarEvent,
|
||||
}) => {
|
||||
const calendarEventId =
|
||||
existingCalendarEvent?.id ?? newlyCreatedCalendarEvent?.id;
|
||||
|
||||
if (!calendarEventId) {
|
||||
throw new Error(
|
||||
`Calendar event id not found for event with iCalUID ${fetchedCalendarEvent.iCalUID} - should never happen`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
calendarEventId,
|
||||
eventExternalId: fetchedCalendarEvent.id,
|
||||
calendarChannelId: calendarChannel.id,
|
||||
recurringEventExternalId:
|
||||
fetchedCalendarEvent.recurringEventExternalId ?? '',
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
await calendarChannelEventAssociationRepository.save(
|
||||
calendarChannelEventAssociationsToSave,
|
||||
{},
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
const participantsToSave =
|
||||
fetchedCalendarEventsWithDBEventsEnrichedWithSavedEvents
|
||||
.filter(
|
||||
({ newlyCreatedCalendarEvent }) =>
|
||||
newlyCreatedCalendarEvent !== null,
|
||||
)
|
||||
.flatMap(({ newlyCreatedCalendarEvent, fetchedCalendarEvent }) => {
|
||||
if (!newlyCreatedCalendarEvent?.id) {
|
||||
throw new Error(
|
||||
`Newly created calendar event with iCalUID ${fetchedCalendarEvent.iCalUID} not found - should never happen`,
|
||||
);
|
||||
}
|
||||
|
||||
return fetchedCalendarEvent.participants.map((participant) => ({
|
||||
...participant,
|
||||
calendarEventId: newlyCreatedCalendarEvent.id,
|
||||
}));
|
||||
});
|
||||
|
||||
// todo: we should prevent duplicate rows on calendarEventAssociation by creating
|
||||
// an index on calendarChannelId and calendarEventId
|
||||
const participantsToUpdate =
|
||||
fetchedCalendarEventsWithDBEventsEnrichedWithSavedEvents
|
||||
.filter(
|
||||
({ existingCalendarEvent }) => existingCalendarEvent !== null,
|
||||
)
|
||||
.flatMap(({ fetchedCalendarEvent, existingCalendarEvent }) => {
|
||||
if (!existingCalendarEvent?.id) {
|
||||
throw new Error(
|
||||
`Existing calendar event with iCalUID ${fetchedCalendarEvent.iCalUID} not found - should never happen`,
|
||||
);
|
||||
}
|
||||
|
||||
return fetchedCalendarEvent.participants.map((participant) => ({
|
||||
...participant,
|
||||
calendarEventId: existingCalendarEvent.id,
|
||||
}));
|
||||
});
|
||||
|
||||
await this.calendarEventParticipantService.upsertAndDeleteCalendarEventParticipants(
|
||||
participantsToSave,
|
||||
participantsToUpdate,
|
||||
transactionManager,
|
||||
{
|
||||
participantsToSave,
|
||||
participantsToUpdate,
|
||||
transactionManager,
|
||||
calendarChannel,
|
||||
connectedAccount,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (calendarChannel.isContactAutoCreationEnabled) {
|
||||
await this.messageQueueService.add<CreateCompanyAndContactJobData>(
|
||||
CreateCompanyAndContactJob.name,
|
||||
{
|
||||
workspaceId,
|
||||
connectedAccount,
|
||||
contactsToCreate: participantsToSave,
|
||||
source: FieldActorSource.CALENDAR,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
|
||||
|
||||
export const mapCalendarEventsByICalUID = (
|
||||
existingCalendarEvents: CalendarEventWorkspaceEntity[],
|
||||
): Map<string, string> => {
|
||||
return new Map<string, string>(
|
||||
existingCalendarEvents.map((calendarEvent) => [
|
||||
calendarEvent.iCalUID,
|
||||
calendarEvent.id,
|
||||
]),
|
||||
);
|
||||
};
|
||||
+6
-6
@@ -1,13 +1,13 @@
|
||||
import { filterOutBlocklistedEvents } from 'src/modules/calendar/calendar-event-import-manager/utils/filter-out-blocklisted-events.util';
|
||||
import { CalendarEventWithParticipants } from 'src/modules/calendar/common/types/calendar-event';
|
||||
import { FetchedCalendarEvent } from 'src/modules/calendar/common/types/fetched-calendar-event';
|
||||
|
||||
export const filterEventsAndReturnCancelledEvents = (
|
||||
calendarChannelHandles: string[],
|
||||
events: CalendarEventWithParticipants[],
|
||||
events: FetchedCalendarEvent[],
|
||||
blocklist: string[],
|
||||
): {
|
||||
filteredEvents: CalendarEventWithParticipants[];
|
||||
cancelledEvents: CalendarEventWithParticipants[];
|
||||
filteredEvents: FetchedCalendarEvent[];
|
||||
cancelledEvents: FetchedCalendarEvent[];
|
||||
} => {
|
||||
const filteredEvents = filterOutBlocklistedEvents(
|
||||
calendarChannelHandles,
|
||||
@@ -18,8 +18,8 @@ export const filterEventsAndReturnCancelledEvents = (
|
||||
return filteredEvents.reduce(
|
||||
(
|
||||
acc: {
|
||||
filteredEvents: CalendarEventWithParticipants[];
|
||||
cancelledEvents: CalendarEventWithParticipants[];
|
||||
filteredEvents: FetchedCalendarEvent[];
|
||||
cancelledEvents: FetchedCalendarEvent[];
|
||||
},
|
||||
event,
|
||||
) => {
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
import { isEmailBlocklisted } from 'src/modules/blocklist/utils/is-email-blocklisted.util';
|
||||
import { CalendarEventWithParticipants } from 'src/modules/calendar/common/types/calendar-event';
|
||||
import { FetchedCalendarEvent } from 'src/modules/calendar/common/types/fetched-calendar-event';
|
||||
|
||||
export const filterOutBlocklistedEvents = (
|
||||
calendarChannelHandles: string[],
|
||||
events: CalendarEventWithParticipants[],
|
||||
events: FetchedCalendarEvent[],
|
||||
blocklist: string[],
|
||||
) => {
|
||||
return events.filter((event) => {
|
||||
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import {
|
||||
CalendarEventWithParticipants,
|
||||
CalendarEventWithParticipantsAndCalendarEventId,
|
||||
} from 'src/modules/calendar/common/types/calendar-event';
|
||||
|
||||
export const injectIdsInCalendarEvents = (
|
||||
calendarEvents: CalendarEventWithParticipants[],
|
||||
iCalUIDCalendarEventIdMap: Map<string, string>,
|
||||
): CalendarEventWithParticipantsAndCalendarEventId[] => {
|
||||
return calendarEvents.map((calendarEvent) => {
|
||||
const id = iCalUIDCalendarEventIdMap.get(calendarEvent.iCalUID) ?? v4();
|
||||
|
||||
return injectIdInCalendarEvent(calendarEvent, id);
|
||||
});
|
||||
};
|
||||
|
||||
const injectIdInCalendarEvent = (
|
||||
calendarEvent: CalendarEventWithParticipants,
|
||||
id: string,
|
||||
): CalendarEventWithParticipantsAndCalendarEventId => {
|
||||
return {
|
||||
...calendarEvent,
|
||||
id,
|
||||
participants: calendarEvent.participants.map((participant) => ({
|
||||
...participant,
|
||||
calendarEventId: id,
|
||||
})),
|
||||
};
|
||||
};
|
||||
+51
-11
@@ -4,24 +4,51 @@ import { isDefined } from 'class-validator';
|
||||
import differenceWith from 'lodash.differencewith';
|
||||
import { Any } from 'typeorm';
|
||||
|
||||
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
|
||||
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 { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
|
||||
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
|
||||
import { CalendarEventParticipantWithCalendarEventId } from 'src/modules/calendar/common/types/calendar-event';
|
||||
import { FetchedCalendarEventParticipant } from 'src/modules/calendar/common/types/fetched-calendar-event';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import {
|
||||
CreateCompanyAndContactJob,
|
||||
CreateCompanyAndContactJobData,
|
||||
} from 'src/modules/contact-creation-manager/jobs/create-company-and-contact.job';
|
||||
import { MatchParticipantService } from 'src/modules/match-participant/match-participant.service';
|
||||
|
||||
type FetchedCalendarEventParticipantWithCalendarEventId =
|
||||
FetchedCalendarEventParticipant & {
|
||||
calendarEventId: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class CalendarEventParticipantService {
|
||||
constructor(
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
private readonly matchParticipantService: MatchParticipantService<CalendarEventParticipantWorkspaceEntity>,
|
||||
@InjectMessageQueue(MessageQueue.contactCreationQueue)
|
||||
private readonly messageQueueService: MessageQueueService,
|
||||
) {}
|
||||
|
||||
public async upsertAndDeleteCalendarEventParticipants(
|
||||
participantsToSave: CalendarEventParticipantWithCalendarEventId[],
|
||||
participantsToUpdate: CalendarEventParticipantWithCalendarEventId[],
|
||||
transactionManager?: WorkspaceEntityManager,
|
||||
): Promise<void> {
|
||||
public async upsertAndDeleteCalendarEventParticipants({
|
||||
participantsToSave,
|
||||
participantsToUpdate,
|
||||
transactionManager,
|
||||
calendarChannel,
|
||||
connectedAccount,
|
||||
workspaceId,
|
||||
}: {
|
||||
participantsToSave: FetchedCalendarEventParticipantWithCalendarEventId[];
|
||||
participantsToUpdate: FetchedCalendarEventParticipantWithCalendarEventId[];
|
||||
transactionManager?: WorkspaceEntityManager;
|
||||
calendarChannel: CalendarChannelWorkspaceEntity;
|
||||
connectedAccount: ConnectedAccountWorkspaceEntity;
|
||||
workspaceId: string;
|
||||
}): Promise<void> {
|
||||
const calendarEventParticipantRepository =
|
||||
await this.twentyORMManager.getRepository<CalendarEventParticipantWorkspaceEntity>(
|
||||
'calendarEventParticipant',
|
||||
@@ -39,7 +66,10 @@ export class CalendarEventParticipantService {
|
||||
});
|
||||
|
||||
const { calendarEventParticipantsToUpdate, newCalendarEventParticipants } =
|
||||
participantsToUpdate.reduce(
|
||||
participantsToUpdate.reduce<{
|
||||
calendarEventParticipantsToUpdate: FetchedCalendarEventParticipantWithCalendarEventId[];
|
||||
newCalendarEventParticipants: FetchedCalendarEventParticipantWithCalendarEventId[];
|
||||
}>(
|
||||
(acc, calendarEventParticipant) => {
|
||||
const existingCalendarEventParticipant =
|
||||
existingCalendarEventParticipants.find(
|
||||
@@ -61,10 +91,8 @@ export class CalendarEventParticipantService {
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
calendarEventParticipantsToUpdate:
|
||||
[] as CalendarEventParticipantWithCalendarEventId[],
|
||||
newCalendarEventParticipants:
|
||||
[] as CalendarEventParticipantWithCalendarEventId[],
|
||||
calendarEventParticipantsToUpdate: [],
|
||||
newCalendarEventParticipants: [],
|
||||
},
|
||||
);
|
||||
|
||||
@@ -110,6 +138,18 @@ export class CalendarEventParticipantService {
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
if (calendarChannel.isContactAutoCreationEnabled) {
|
||||
await this.messageQueueService.add<CreateCompanyAndContactJobData>(
|
||||
CreateCompanyAndContactJob.name,
|
||||
{
|
||||
workspaceId,
|
||||
connectedAccount,
|
||||
contactsToCreate: savedParticipants,
|
||||
source: FieldActorSource.CALENDAR,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
await this.matchParticipantService.matchParticipants({
|
||||
participants: savedParticipants,
|
||||
objectMetadataName: 'calendarEventParticipant',
|
||||
|
||||
+6
-1
@@ -1,9 +1,9 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-on-delete-action.interface';
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-on-delete-action.interface';
|
||||
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
|
||||
@@ -29,6 +29,11 @@ import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standa
|
||||
@WorkspaceIsSystem()
|
||||
@WorkspaceIsNotAuditLogged()
|
||||
export class CalendarChannelEventAssociationWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
/**
|
||||
* External ID of the calendar event. Comes from the provider's API, is unique per connected account.
|
||||
* Used by the provider to identify the event in their system.
|
||||
* So two External ID can be related to the same event sharing the same iCalUID.
|
||||
*/
|
||||
@WorkspaceField({
|
||||
standardId:
|
||||
CALENDAR_CHANNEL_EVENT_ASSOCIATION_STANDARD_FIELD_IDS.eventExternalId,
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
|
||||
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
|
||||
|
||||
export type CalendarEvent = Omit<
|
||||
CalendarEventWorkspaceEntity,
|
||||
| 'createdAt'
|
||||
| 'updatedAt'
|
||||
| 'deletedAt'
|
||||
| 'calendarChannelEventAssociations'
|
||||
| 'calendarEventParticipants'
|
||||
| 'conferenceLink'
|
||||
| 'id'
|
||||
> & {
|
||||
conferenceLinkLabel: string;
|
||||
conferenceLinkUrl: string;
|
||||
};
|
||||
|
||||
export type CalendarEventParticipant = Omit<
|
||||
CalendarEventParticipantWorkspaceEntity,
|
||||
| 'id'
|
||||
| 'createdAt'
|
||||
| 'updatedAt'
|
||||
| 'deletedAt'
|
||||
| 'personId'
|
||||
| 'workspaceMemberId'
|
||||
| 'person'
|
||||
| 'workspaceMember'
|
||||
| 'calendarEvent'
|
||||
| 'calendarEventId'
|
||||
>;
|
||||
|
||||
export type CalendarEventParticipantWithCalendarEventId =
|
||||
CalendarEventParticipant & {
|
||||
calendarEventId: string;
|
||||
};
|
||||
|
||||
export type CalendarEventWithParticipants = CalendarEvent & {
|
||||
externalId: string;
|
||||
recurringEventExternalId?: string;
|
||||
participants: CalendarEventParticipant[];
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type CalendarEventWithParticipantsAndCalendarEventId = CalendarEvent & {
|
||||
id: string;
|
||||
externalId: string;
|
||||
recurringEventExternalId?: string;
|
||||
participants: CalendarEventParticipantWithCalendarEventId[];
|
||||
status: string;
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
export type FetchedCalendarEventParticipant = {
|
||||
displayName: string;
|
||||
responseStatus: string;
|
||||
handle: string;
|
||||
isOrganizer: boolean;
|
||||
};
|
||||
|
||||
export type FetchedCalendarEvent = {
|
||||
id: string;
|
||||
title: string;
|
||||
iCalUID: string;
|
||||
description: string;
|
||||
startsAt: string;
|
||||
endsAt: string;
|
||||
location: string;
|
||||
isFullDay: boolean;
|
||||
isCanceled: boolean;
|
||||
conferenceLinkLabel: string;
|
||||
conferenceLinkUrl: string;
|
||||
externalCreatedAt: string;
|
||||
externalUpdatedAt: string;
|
||||
conferenceSolution: string;
|
||||
recurringEventExternalId?: string;
|
||||
participants: FetchedCalendarEventParticipant[];
|
||||
status: string;
|
||||
};
|
||||
Reference in New Issue
Block a user