perf(onboarding): compute invite suggestions on-demand (#21696)
## Summary Follow-up to #21640. In production, invite suggestions took ~1 minute to appear because `FetchOnboardingInviteSuggestionsJob` ran on the shared `calendarQueue` behind heavy calendar-sync jobs. - **Drop the background job entirely.** `getInviteSuggestions` now resolves the connected account from the authenticated `@AuthUserWorkspaceId()` and computes suggestions on demand: cache-first, with a bounded calendar fetch + cache write on a miss. Removes the Google/Microsoft enqueues, the `shouldComputeInviteSuggestions` threading through the auth controllers, and the now-unused `shouldComputeInviteSuggestionsOnConnect` / `isOnboardingConnectAccountPending` helpers. - **Prefetch one step earlier.** New `usePrefetchInviteSuggestions` hook fires the query from `CreateProfile` so the server cache is warm by the time the invite step renders. `InviteTeam` switches from `network-only` → `cache-first`. If the profile step is skipped, the invite step still computes on-demand (~1–3s, no queue) — no more minute-long waits. No GraphQL schema change. ## Test plan - [ ] Connect Google calendar in onboarding → invite step renders prefilled teammates with no perceivable wait - [ ] Connect Microsoft calendar in onboarding → same - [ ] Onboard with workspace name already set so profile step is skipped → invite step still prefills (just with a brief on-demand fetch instead of 1 min) - [ ] Connect a non-work-email account → invite step renders empty form (no suggestions) - [ ] `npx nx typecheck twenty-server` ✅ - [ ] `npx nx lint:diff-with-main twenty-server` ✅ - [ ] `npx nx lint:diff-with-main twenty-front` ✅ (changed files clean) - [ ] `google-apis.service.spec.ts` + `microsoft-apis.service.spec.ts` pass https://claude.ai/code/session_019MyY3bfAEij4AwSXMCLtWY --- _Generated by [Claude Code](https://claude.ai/code/session_019MyY3bfAEij4AwSXMCLtWY)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21696?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
import { useQuery } from '@apollo/client/react';
|
||||
|
||||
import { GetInviteSuggestionsDocument } from '~/generated-metadata/graphql';
|
||||
|
||||
export const usePrefetchInviteSuggestions = () => {
|
||||
useQuery(GetInviteSuggestionsDocument, {
|
||||
fetchPolicy: 'cache-first',
|
||||
});
|
||||
};
|
||||
@@ -12,6 +12,7 @@ import { Title } from '@/auth/components/Title';
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceMembersState } from '@/auth/states/currentWorkspaceMembersState';
|
||||
import { usePrefetchInviteSuggestions } from '@/onboarding/hooks/usePrefetchInviteSuggestions';
|
||||
import { useSetNextOnboardingStatus } from '@/onboarding/hooks/useSetNextOnboardingStatus';
|
||||
import { useUpdateWorkspaceMemberSettings } from '@/settings/profile/hooks/useUpdateWorkspaceMemberSettings';
|
||||
import { WorkspaceMemberPictureUploader } from '@/settings/workspace-member/components/WorkspaceMemberPictureUploader';
|
||||
@@ -69,6 +70,9 @@ type Form = z.infer<typeof validationSchema>;
|
||||
export const CreateProfile = () => {
|
||||
const { t } = useLingui();
|
||||
const setNextOnboardingStatus = useSetNextOnboardingStatus();
|
||||
|
||||
usePrefetchInviteSuggestions();
|
||||
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const currentWorkspaceMember = useAtomStateValue(currentWorkspaceMemberState);
|
||||
const setCurrentUser = useSetAtomState(currentUserState);
|
||||
|
||||
@@ -96,7 +96,7 @@ export const InviteTeam = () => {
|
||||
const { data: inviteSuggestionsData } = useQuery(
|
||||
GetInviteSuggestionsDocument,
|
||||
{
|
||||
fetchPolicy: 'network-only',
|
||||
fetchPolicy: 'cache-first',
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
-7
@@ -98,12 +98,6 @@ export class GoogleAPIsAuthController {
|
||||
|
||||
const handle = emails[0].value.toLowerCase();
|
||||
|
||||
const shouldComputeInviteSuggestions =
|
||||
await this.onboardingService.shouldComputeInviteSuggestionsOnConnect({
|
||||
userId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const connectedAccountId =
|
||||
await this.googleAPIsService.refreshGoogleRefreshToken({
|
||||
handle,
|
||||
@@ -115,7 +109,6 @@ export class GoogleAPIsAuthController {
|
||||
calendarVisibility,
|
||||
messageVisibility,
|
||||
skipMessageChannelConfiguration,
|
||||
shouldComputeInviteSuggestions,
|
||||
});
|
||||
|
||||
if (userId) {
|
||||
|
||||
-7
@@ -105,12 +105,6 @@ export class MicrosoftAPIsAuthController {
|
||||
|
||||
const handle = emails[0].value.toLowerCase();
|
||||
|
||||
const shouldComputeInviteSuggestions =
|
||||
await this.onboardingService.shouldComputeInviteSuggestionsOnConnect({
|
||||
userId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const connectedAccountId =
|
||||
await this.microsoftAPIsService.refreshMicrosoftRefreshToken({
|
||||
handle,
|
||||
@@ -122,7 +116,6 @@ export class MicrosoftAPIsAuthController {
|
||||
calendarVisibility,
|
||||
messageVisibility,
|
||||
skipMessageChannelConfiguration,
|
||||
shouldComputeInviteSuggestions,
|
||||
});
|
||||
|
||||
if (userId) {
|
||||
|
||||
@@ -47,10 +47,6 @@ import {
|
||||
MessagingMessageListFetchJob,
|
||||
type MessagingMessageListFetchJobData,
|
||||
} from 'src/modules/messaging/message-import-manager/jobs/messaging-message-list-fetch.job';
|
||||
import {
|
||||
FetchOnboardingInviteSuggestionsJob,
|
||||
type FetchOnboardingInviteSuggestionsJobData,
|
||||
} from 'src/modules/onboarding-invite-suggestions/jobs/fetch-onboarding-invite-suggestions.job';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@Injectable()
|
||||
@@ -93,7 +89,6 @@ export class GoogleAPIsService {
|
||||
calendarVisibility: CalendarChannelVisibility | undefined;
|
||||
messageVisibility: MessageChannelVisibility | undefined;
|
||||
skipMessageChannelConfiguration?: boolean;
|
||||
shouldComputeInviteSuggestions?: boolean;
|
||||
}): Promise<string> {
|
||||
const {
|
||||
handle,
|
||||
@@ -103,7 +98,6 @@ export class GoogleAPIsService {
|
||||
calendarVisibility,
|
||||
messageVisibility,
|
||||
skipMessageChannelConfiguration,
|
||||
shouldComputeInviteSuggestions,
|
||||
} = input;
|
||||
|
||||
const isCalendarEnabled = this.twentyConfigService.get(
|
||||
@@ -353,23 +347,6 @@ export class GoogleAPIsService {
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
shouldComputeInviteSuggestions &&
|
||||
isCalendarEnabled &&
|
||||
isCalendarAvailable
|
||||
) {
|
||||
void this.calendarQueueService
|
||||
.add<FetchOnboardingInviteSuggestionsJobData>(
|
||||
FetchOnboardingInviteSuggestionsJob.name,
|
||||
{
|
||||
workspaceId,
|
||||
userId,
|
||||
connectedAccountId: newOrExistingConnectedAccountId,
|
||||
},
|
||||
)
|
||||
.catch(() => undefined);
|
||||
}
|
||||
|
||||
return newOrExistingConnectedAccountId;
|
||||
},
|
||||
authContext,
|
||||
|
||||
-22
@@ -39,10 +39,6 @@ import {
|
||||
import { CalendarChannelSyncStatusService } from 'src/modules/calendar/common/services/calendar-channel-sync-status.service';
|
||||
import { EmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/services/email-alias-manager.service';
|
||||
import { AccountsToReconnectService } from 'src/modules/connected-account/services/accounts-to-reconnect.service';
|
||||
import {
|
||||
FetchOnboardingInviteSuggestionsJob,
|
||||
type FetchOnboardingInviteSuggestionsJobData,
|
||||
} from 'src/modules/onboarding-invite-suggestions/jobs/fetch-onboarding-invite-suggestions.job';
|
||||
|
||||
import { MessageChannelSyncStatusService } from 'src/modules/messaging/common/services/message-channel-sync-status.service';
|
||||
import {
|
||||
@@ -89,7 +85,6 @@ export class MicrosoftAPIsService {
|
||||
calendarVisibility: CalendarChannelVisibility | undefined;
|
||||
messageVisibility: MessageChannelVisibility | undefined;
|
||||
skipMessageChannelConfiguration?: boolean;
|
||||
shouldComputeInviteSuggestions?: boolean;
|
||||
}): Promise<string> {
|
||||
const {
|
||||
handle,
|
||||
@@ -99,7 +94,6 @@ export class MicrosoftAPIsService {
|
||||
calendarVisibility,
|
||||
messageVisibility,
|
||||
skipMessageChannelConfiguration,
|
||||
shouldComputeInviteSuggestions,
|
||||
} = input;
|
||||
|
||||
const scopes = getMicrosoftApisOauthScopes();
|
||||
@@ -316,22 +310,6 @@ export class MicrosoftAPIsService {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
shouldComputeInviteSuggestions &&
|
||||
syncableCalendarChannels.length > 0
|
||||
) {
|
||||
void this.calendarQueueService
|
||||
.add<FetchOnboardingInviteSuggestionsJobData>(
|
||||
FetchOnboardingInviteSuggestionsJob.name,
|
||||
{
|
||||
workspaceId,
|
||||
userId,
|
||||
connectedAccountId: newOrExistingConnectedAccountId,
|
||||
},
|
||||
)
|
||||
.catch(() => undefined);
|
||||
}
|
||||
}
|
||||
|
||||
return newOrExistingConnectedAccountId;
|
||||
|
||||
@@ -42,7 +42,6 @@ import { CalendarEventParticipantManagerModule } from 'src/modules/calendar/cale
|
||||
import { CalendarModule } from 'src/modules/calendar/calendar.module';
|
||||
import { AutoCompaniesAndContactsCreationJobModule } from 'src/modules/contact-creation-manager/jobs/auto-companies-and-contacts-creation-job.module';
|
||||
import { MessagingModule } from 'src/modules/messaging/messaging.module';
|
||||
import { OnboardingInviteSuggestionsModule } from 'src/modules/onboarding-invite-suggestions/onboarding-invite-suggestions.module';
|
||||
import { TimelineJobModule } from 'src/modules/timeline/jobs/timeline-job.module';
|
||||
import { TimelineActivityModule } from 'src/modules/timeline/timeline-activity.module';
|
||||
import { WorkflowModule } from 'src/modules/workflow/workflow.module';
|
||||
@@ -67,7 +66,6 @@ import { WorkflowModule } from 'src/modules/workflow/workflow.module';
|
||||
MessagingModule,
|
||||
CalendarModule,
|
||||
CalendarEventParticipantManagerModule,
|
||||
OnboardingInviteSuggestionsModule,
|
||||
TimelineActivityModule,
|
||||
StripeModule,
|
||||
FeatureFlagModule,
|
||||
|
||||
@@ -11,6 +11,7 @@ import { type AuthContextUser } from 'src/engine/core-modules/auth/types/auth-co
|
||||
import { OnboardingInviteSuggestionsService } from 'src/modules/onboarding-invite-suggestions/services/onboarding-invite-suggestions.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator';
|
||||
import { AuthUserWorkspaceId } from 'src/engine/decorators/auth/auth-user-workspace-id.decorator';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard';
|
||||
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
|
||||
@@ -31,10 +32,12 @@ export class OnboardingResolver {
|
||||
async getInviteSuggestions(
|
||||
@AuthUser() user: AuthContextUser,
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
@AuthUserWorkspaceId() userWorkspaceId: string,
|
||||
): Promise<InviteSuggestionDTO[]> {
|
||||
return this.onboardingInviteSuggestionsService.getCachedSuggestions({
|
||||
return this.onboardingInviteSuggestionsService.getOrComputeSuggestions({
|
||||
workspaceId: workspace.id,
|
||||
userId: user.id,
|
||||
userWorkspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -129,43 +129,6 @@ export class OnboardingService {
|
||||
return OnboardingStatus.COMPLETED;
|
||||
}
|
||||
|
||||
async isOnboardingConnectAccountPending({
|
||||
userId,
|
||||
workspaceId,
|
||||
}: {
|
||||
userId: string;
|
||||
workspaceId: string;
|
||||
}): Promise<boolean> {
|
||||
const value = await this.userVarsService.get({
|
||||
userId,
|
||||
workspaceId,
|
||||
key: OnboardingStepKeys.ONBOARDING_CONNECT_ACCOUNT_PENDING,
|
||||
});
|
||||
|
||||
return value === true;
|
||||
}
|
||||
|
||||
async shouldComputeInviteSuggestionsOnConnect({
|
||||
userId,
|
||||
workspaceId,
|
||||
}: {
|
||||
userId?: string;
|
||||
workspaceId: string;
|
||||
}): Promise<boolean> {
|
||||
if (!isDefined(userId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return await this.isOnboardingConnectAccountPending({
|
||||
userId,
|
||||
workspaceId,
|
||||
});
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async setOnboardingConnectAccountPending(
|
||||
{
|
||||
userId,
|
||||
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
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 { OnboardingInviteSuggestionsService } from 'src/modules/onboarding-invite-suggestions/services/onboarding-invite-suggestions.service';
|
||||
|
||||
export type FetchOnboardingInviteSuggestionsJobData = {
|
||||
workspaceId: string;
|
||||
userId: string;
|
||||
connectedAccountId: string;
|
||||
};
|
||||
|
||||
@Processor({
|
||||
queueName: MessageQueue.calendarQueue,
|
||||
})
|
||||
export class FetchOnboardingInviteSuggestionsJob {
|
||||
constructor(
|
||||
private readonly onboardingInviteSuggestionsService: OnboardingInviteSuggestionsService,
|
||||
) {}
|
||||
|
||||
@Process(FetchOnboardingInviteSuggestionsJob.name)
|
||||
async handle(data: FetchOnboardingInviteSuggestionsJobData): Promise<void> {
|
||||
await this.onboardingInviteSuggestionsService.computeAndCacheSuggestions(
|
||||
data,
|
||||
);
|
||||
}
|
||||
}
|
||||
-2
@@ -3,7 +3,6 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { ConnectedAccountEntity } from 'src/engine/metadata-modules/connected-account/entities/connected-account.entity';
|
||||
import { OAuth2ClientManagerModule } from 'src/modules/connected-account/oauth2-client-manager/oauth2-client-manager.module';
|
||||
import { FetchOnboardingInviteSuggestionsJob } from 'src/modules/onboarding-invite-suggestions/jobs/fetch-onboarding-invite-suggestions.job';
|
||||
import { CalendarAttendeesService } from 'src/modules/onboarding-invite-suggestions/services/calendar-attendees.service';
|
||||
import { GoogleCalendarAttendeesService } from 'src/modules/onboarding-invite-suggestions/services/google-calendar-attendees.service';
|
||||
import { MicrosoftCalendarAttendeesService } from 'src/modules/onboarding-invite-suggestions/services/microsoft-calendar-attendees.service';
|
||||
@@ -19,7 +18,6 @@ import { OnboardingInviteSuggestionsService } from 'src/modules/onboarding-invit
|
||||
MicrosoftCalendarAttendeesService,
|
||||
CalendarAttendeesService,
|
||||
OnboardingInviteSuggestionsService,
|
||||
FetchOnboardingInviteSuggestionsJob,
|
||||
],
|
||||
exports: [OnboardingInviteSuggestionsService],
|
||||
})
|
||||
|
||||
+48
-52
@@ -9,20 +9,14 @@ import { CacheStorageService } from 'src/engine/core-modules/cache-storage/servi
|
||||
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
|
||||
import { ConnectedAccountEntity } from 'src/engine/metadata-modules/connected-account/entities/connected-account.entity';
|
||||
import { getDomainNameFromHandle } from 'src/modules/contact-creation-manager/utils/get-domain-name-from-handle.util';
|
||||
import { isGroupEmail } from 'src/modules/messaging/message-import-manager/utils/is-group-email';
|
||||
import { ONBOARDING_INVITE_SUGGESTIONS_CACHE_TTL_MS } from 'src/modules/onboarding-invite-suggestions/constants/onboarding-invite-suggestions-cache-ttl-ms.constant';
|
||||
import { ONBOARDING_INVITE_SUGGESTIONS_MAX_COUNT } from 'src/modules/onboarding-invite-suggestions/constants/onboarding-invite-suggestions-max-count.constant';
|
||||
import { getOnboardingInviteSuggestionsCacheKey } from 'src/modules/onboarding-invite-suggestions/utils/get-onboarding-invite-suggestions-cache-key.util';
|
||||
import { isGroupEmail } from 'src/modules/messaging/message-import-manager/utils/is-group-email';
|
||||
import { CalendarAttendeesService } from 'src/modules/onboarding-invite-suggestions/services/calendar-attendees.service';
|
||||
import { type CalendarAttendee } from 'src/modules/onboarding-invite-suggestions/types/calendar-attendee.type';
|
||||
import { getOnboardingInviteSuggestionsCacheKey } from 'src/modules/onboarding-invite-suggestions/utils/get-onboarding-invite-suggestions-cache-key.util';
|
||||
import { isWorkEmail } from 'src/utils/is-work-email';
|
||||
|
||||
type ComputeAndCacheSuggestionsArgs = {
|
||||
workspaceId: string;
|
||||
userId: string;
|
||||
connectedAccountId: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class OnboardingInviteSuggestionsService {
|
||||
private readonly logger = new Logger(OnboardingInviteSuggestionsService.name);
|
||||
@@ -35,54 +29,63 @@ export class OnboardingInviteSuggestionsService {
|
||||
private readonly cacheStorageService: CacheStorageService,
|
||||
) {}
|
||||
|
||||
async getCachedSuggestions({
|
||||
async getOrComputeSuggestions({
|
||||
workspaceId,
|
||||
userId,
|
||||
userWorkspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
userId: string;
|
||||
userWorkspaceId: string;
|
||||
}): Promise<CalendarAttendee[]> {
|
||||
const cachedSuggestions = await this.cacheStorageService.get<
|
||||
CalendarAttendee[]
|
||||
>(getOnboardingInviteSuggestionsCacheKey(workspaceId, userId));
|
||||
|
||||
return cachedSuggestions ?? [];
|
||||
}
|
||||
|
||||
async computeAndCacheSuggestions({
|
||||
workspaceId,
|
||||
userId,
|
||||
connectedAccountId,
|
||||
}: ComputeAndCacheSuggestionsArgs): Promise<void> {
|
||||
const cacheKey = getOnboardingInviteSuggestionsCacheKey(
|
||||
workspaceId,
|
||||
userId,
|
||||
);
|
||||
|
||||
const connectedAccount = await this.connectedAccountRepository.findOne({
|
||||
where: { id: connectedAccountId, workspaceId },
|
||||
});
|
||||
const cachedSuggestions =
|
||||
await this.cacheStorageService.get<CalendarAttendee[]>(cacheKey);
|
||||
|
||||
if (!isDefined(connectedAccount)) {
|
||||
await this.cacheStorageService.set<CalendarAttendee[]>(
|
||||
cacheKey,
|
||||
[],
|
||||
ONBOARDING_INVITE_SUGGESTIONS_CACHE_TTL_MS,
|
||||
);
|
||||
|
||||
return;
|
||||
if (isDefined(cachedSuggestions)) {
|
||||
return cachedSuggestions;
|
||||
}
|
||||
|
||||
const connectedAccountHandle = connectedAccount.handle.toLowerCase();
|
||||
const suggestions = await this.computeSuggestionsFromConnectedAccount({
|
||||
workspaceId,
|
||||
userWorkspaceId,
|
||||
});
|
||||
|
||||
await this.cacheStorageService.set<CalendarAttendee[]>(
|
||||
cacheKey,
|
||||
suggestions,
|
||||
ONBOARDING_INVITE_SUGGESTIONS_CACHE_TTL_MS,
|
||||
);
|
||||
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
private async computeSuggestionsFromConnectedAccount({
|
||||
workspaceId,
|
||||
userWorkspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
userWorkspaceId: string;
|
||||
}): Promise<CalendarAttendee[]> {
|
||||
const mostRecentlyConnectedAccount =
|
||||
await this.connectedAccountRepository.findOne({
|
||||
where: { userWorkspaceId, workspaceId },
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
|
||||
if (!isDefined(mostRecentlyConnectedAccount)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const connectedAccountHandle =
|
||||
mostRecentlyConnectedAccount.handle.toLowerCase();
|
||||
|
||||
if (!isWorkEmail(connectedAccountHandle)) {
|
||||
await this.cacheStorageService.set<CalendarAttendee[]>(
|
||||
cacheKey,
|
||||
[],
|
||||
ONBOARDING_INVITE_SUGGESTIONS_CACHE_TTL_MS,
|
||||
);
|
||||
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
|
||||
const connectedAccountDomain = getDomainNameFromHandle(
|
||||
@@ -90,7 +93,7 @@ export class OnboardingInviteSuggestionsService {
|
||||
);
|
||||
const ownEmailHandles = new Set<string>([
|
||||
connectedAccountHandle,
|
||||
...(connectedAccount.handleAliases ?? []).map((alias) =>
|
||||
...(mostRecentlyConnectedAccount.handleAliases ?? []).map((alias) =>
|
||||
alias.toLowerCase(),
|
||||
),
|
||||
]);
|
||||
@@ -98,10 +101,9 @@ export class OnboardingInviteSuggestionsService {
|
||||
let attendees: CalendarAttendee[] = [];
|
||||
|
||||
try {
|
||||
attendees =
|
||||
await this.calendarAttendeesService.getRecentAttendees(
|
||||
connectedAccount,
|
||||
);
|
||||
attendees = await this.calendarAttendeesService.getRecentAttendees(
|
||||
mostRecentlyConnectedAccount,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.warn(
|
||||
`Could not compute invite suggestions for workspace ${workspaceId}: ${
|
||||
@@ -150,14 +152,8 @@ export class OnboardingInviteSuggestionsService {
|
||||
eventCountByColleagueEmail.entries(),
|
||||
).sort(([, left], [, right]) => right.eventCount - left.eventCount);
|
||||
|
||||
const suggestions: CalendarAttendee[] = mostFrequentColleaguesFirst
|
||||
return mostFrequentColleaguesFirst
|
||||
.slice(0, ONBOARDING_INVITE_SUGGESTIONS_MAX_COUNT)
|
||||
.map(([email, { displayName }]) => ({ email, displayName }));
|
||||
|
||||
await this.cacheStorageService.set<CalendarAttendee[]>(
|
||||
cacheKey,
|
||||
suggestions,
|
||||
ONBOARDING_INVITE_SUGGESTIONS_CACHE_TTL_MS,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user