5622 add a syncemail onboarding step (#5689)

- add sync email onboarding step
- refactor calendar and email visibility enums
- add a new table `keyValuePair` in `core` schema
- add a new resolved boolean field `skipSyncEmail` in current user




https://github.com/twentyhq/twenty/assets/29927851/de791475-5bfe-47f9-8e90-76c349fba56f
This commit is contained in:
martmull
2024-06-05 18:16:53 +02:00
committed by GitHub
parent fda0d2a170
commit 9f6a6c3282
92 changed files with 2707 additions and 1246 deletions

View File

@ -27,6 +27,7 @@ import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repos
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-channel.workspace-entity';
import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
import { UserStateModule } from 'src/engine/core-modules/user-state/user-state.module';
import { AuthResolver } from './auth.resolver';
@ -63,6 +64,7 @@ const jwtModule = JwtModule.registerAsync({
]),
HttpModule,
UserWorkspaceModule,
UserStateModule,
],
controllers: [
GoogleAuthController,

View File

@ -15,6 +15,10 @@ import { GoogleAPIsRequest } from 'src/engine/core-modules/auth/strategies/googl
import { GoogleAPIsService } from 'src/engine/core-modules/auth/services/google-apis.service';
import { TokenService } from 'src/engine/core-modules/auth/services/token.service';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { UserStateService } from 'src/engine/core-modules/user-state/user-state.service';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository';
@Controller('auth/google-apis')
export class GoogleAPIsAuthController {
@ -22,6 +26,9 @@ export class GoogleAPIsAuthController {
private readonly googleAPIsService: GoogleAPIsService,
private readonly tokenService: TokenService,
private readonly environmentService: EnvironmentService,
private readonly userStateService: UserStateService,
@InjectObjectMetadataRepository(WorkspaceMemberWorkspaceEntity)
private readonly workspaceMemberService: WorkspaceMemberRepository,
) {}
@Get()
@ -39,7 +46,15 @@ export class GoogleAPIsAuthController {
) {
const { user } = req;
const { email, accessToken, refreshToken, transientToken } = user;
const {
email,
accessToken,
refreshToken,
transientToken,
redirectLocation,
calendarVisibility,
messageVisibility,
} = user;
const { workspaceMemberId, workspaceId } =
await this.tokenService.verifyTransientToken(transientToken);
@ -62,10 +77,25 @@ export class GoogleAPIsAuthController {
workspaceId: workspaceId,
accessToken,
refreshToken,
calendarVisibility,
messageVisibility,
});
const userId = (
await this.workspaceMemberService.find(workspaceMemberId, workspaceId)
)?.userId;
if (userId) {
await this.userStateService.skipSyncEmailOnboardingStep(
userId,
workspaceId,
);
}
return res.redirect(
`${this.environmentService.get('FRONT_BASE_URL')}/settings/accounts`,
`${this.environmentService.get('FRONT_BASE_URL')}${
redirectLocation || '/settings/accounts'
}`,
);
}
}

View File

@ -12,10 +12,26 @@ export class GoogleAPIsOauthGuard extends AuthGuard('google-apis') {
async canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
const transientToken = request.query.transientToken;
const redirectLocation = request.query.redirectLocation;
const calendarVisibility = request.query.calendarVisibility;
const messageVisibility = request.query.messageVisibility;
if (transientToken && typeof transientToken === 'string') {
request.params.transientToken = transientToken;
}
if (redirectLocation && typeof redirectLocation === 'string') {
request.params.redirectLocation = redirectLocation;
}
if (calendarVisibility && typeof calendarVisibility === 'string') {
request.params.calendarVisibility = calendarVisibility;
}
if (messageVisibility && typeof messageVisibility === 'string') {
request.params.messageVisibility = messageVisibility;
}
const activate = (await super.canActivate(context)) as boolean;
return activate;

View File

@ -58,8 +58,16 @@ export class GoogleAPIsService {
workspaceId: string;
accessToken: string;
refreshToken: string;
calendarVisibility: CalendarChannelVisibility | undefined;
messageVisibility: MessageChannelVisibility | undefined;
}) {
const { handle, workspaceId, workspaceMemberId } = input;
const {
handle,
workspaceId,
workspaceMemberId,
calendarVisibility,
messageVisibility,
} = input;
const dataSourceMetadata =
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
@ -104,7 +112,8 @@ export class GoogleAPIsService {
connectedAccountId: newOrExistingConnectedAccountId,
type: MessageChannelType.EMAIL,
handle,
visibility: MessageChannelVisibility.SHARE_EVERYTHING,
visibility:
messageVisibility || MessageChannelVisibility.SHARE_EVERYTHING,
},
workspaceId,
manager,
@ -116,7 +125,9 @@ export class GoogleAPIsService {
id: v4(),
connectedAccountId: newOrExistingConnectedAccountId,
handle,
visibility: CalendarChannelVisibility.SHARE_EVERYTHING,
visibility:
calendarVisibility ||
CalendarChannelVisibility.SHARE_EVERYTHING,
},
workspaceId,
manager,

View File

@ -5,6 +5,8 @@ import { Strategy, VerifyCallback } from 'passport-google-oauth20';
import { Request } from 'express';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { CalendarChannelVisibility } from 'src/modules/calendar/standard-objects/calendar-channel.workspace-entity';
import { MessageChannelVisibility } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
export type GoogleAPIsRequest = Omit<
Request,
@ -19,6 +21,9 @@ export type GoogleAPIsRequest = Omit<
accessToken: string;
refreshToken: string;
transientToken: string;
redirectLocation?: string;
calendarVisibility?: CalendarChannelVisibility;
messageVisibility?: MessageChannelVisibility;
};
};
@ -64,6 +69,9 @@ export class GoogleAPIsStrategy extends PassportStrategy(
prompt: 'consent',
state: JSON.stringify({
transientToken: req.params.transientToken,
redirectLocation: req.params.redirectLocation,
calendarVisibility: req.params.calendarVisibility,
messageVisibility: req.params.messageVisibility,
}),
};
@ -92,6 +100,9 @@ export class GoogleAPIsStrategy extends PassportStrategy(
accessToken,
refreshToken,
transientToken: state.transientToken,
redirectLocation: state.redirectLocation,
calendarVisibility: state.calendarVisibility,
messageVisibility: state.messageVisibility,
};
done(null, user);