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:
Félix Malfait
2026-06-16 21:25:02 +02:00
committed by GitHub
parent e50ec75cd0
commit 35c2a24afb
13 changed files with 66 additions and 180 deletions
@@ -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) {
@@ -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,
@@ -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;