Fix Google API Availability and a edge case (#18150)
First run shows a real customer account, and the second run is my account with all the permissions. <img width="1490" height="386" alt="SCR-20260221-bmfd" src="https://github.com/user-attachments/assets/1e2bd269-67ab-450d-9bf3-3c4872b90773" /> Simulated edge case if user has access to neither <img width="343" height="162" alt="SCR-20260221-bloo" src="https://github.com/user-attachments/assets/a697302b-e8b0-432e-bd15-f689da06dc0e" /> --------- Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
parent
98e73791a4
commit
e7f958c9cf
+106
@@ -211,6 +211,112 @@ describe('GoogleApisServiceAvailabilityService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should return messaging unavailable when Google returns generic precondition check failed error', async () => {
|
||||
mockTwentyConfigService.get.mockImplementation((key) => {
|
||||
if (key === 'AUTH_GOOGLE_CLIENT_ID') return 'client-id';
|
||||
if (key === 'AUTH_GOOGLE_CLIENT_SECRET') return 'client-secret';
|
||||
if (key === 'MESSAGING_PROVIDER_GMAIL_ENABLED') return true;
|
||||
if (key === 'CALENDAR_PROVIDER_GOOGLE_ENABLED') return true;
|
||||
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const preconditionCheckFailedError = {
|
||||
response: {
|
||||
status: 400,
|
||||
data: {
|
||||
error: {
|
||||
code: 400,
|
||||
message: 'Precondition check failed.',
|
||||
errors: [
|
||||
{
|
||||
message: 'Precondition check failed.',
|
||||
domain: 'global',
|
||||
reason: 'failedPrecondition',
|
||||
},
|
||||
],
|
||||
status: 'FAILED_PRECONDITION',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const mockGmailClient = {
|
||||
users: {
|
||||
getProfile: jest.fn().mockRejectedValue(preconditionCheckFailedError),
|
||||
},
|
||||
};
|
||||
|
||||
const mockCalendarClient = {
|
||||
events: {
|
||||
list: jest.fn().mockResolvedValue({ data: {} }),
|
||||
},
|
||||
};
|
||||
|
||||
(google.gmail as jest.Mock).mockReturnValue(mockGmailClient);
|
||||
(google.calendar as jest.Mock).mockReturnValue(mockCalendarClient);
|
||||
|
||||
const result = await service.checkServicesAvailability('access-token');
|
||||
|
||||
expect(result).toEqual({
|
||||
isMessagingAvailable: false,
|
||||
isCalendarAvailable: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return Calendar unavailable when Google returns generic precondition check failed error', async () => {
|
||||
mockTwentyConfigService.get.mockImplementation((key) => {
|
||||
if (key === 'AUTH_GOOGLE_CLIENT_ID') return 'client-id';
|
||||
if (key === 'AUTH_GOOGLE_CLIENT_SECRET') return 'client-secret';
|
||||
if (key === 'MESSAGING_PROVIDER_GMAIL_ENABLED') return true;
|
||||
if (key === 'CALENDAR_PROVIDER_GOOGLE_ENABLED') return true;
|
||||
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const preconditionCheckFailedError = {
|
||||
response: {
|
||||
status: 400,
|
||||
data: {
|
||||
error: {
|
||||
code: 400,
|
||||
message: 'Precondition check failed.',
|
||||
errors: [
|
||||
{
|
||||
message: 'Precondition check failed.',
|
||||
domain: 'global',
|
||||
reason: 'failedPrecondition',
|
||||
},
|
||||
],
|
||||
status: 'FAILED_PRECONDITION',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const mockGmailClient = {
|
||||
users: {
|
||||
getProfile: jest.fn().mockResolvedValue({ data: {} }),
|
||||
},
|
||||
};
|
||||
|
||||
const mockCalendarClient = {
|
||||
events: {
|
||||
list: jest.fn().mockRejectedValue(preconditionCheckFailedError),
|
||||
},
|
||||
};
|
||||
|
||||
(google.gmail as jest.Mock).mockReturnValue(mockGmailClient);
|
||||
(google.calendar as jest.Mock).mockReturnValue(mockCalendarClient);
|
||||
|
||||
const result = await service.checkServicesAvailability('access-token');
|
||||
|
||||
expect(result).toEqual({
|
||||
isMessagingAvailable: true,
|
||||
isCalendarAvailable: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw error for non-service-availability related errors', async () => {
|
||||
mockTwentyConfigService.get.mockImplementation((key) => {
|
||||
if (key === 'AUTH_GOOGLE_CLIENT_ID') return 'client-id';
|
||||
|
||||
+12
-3
@@ -125,9 +125,18 @@ export class GoogleApisServiceAvailabilityService {
|
||||
}
|
||||
|
||||
const isFailedPrecondition = firstError.reason === 'failedPrecondition';
|
||||
const isServiceNotEnabled =
|
||||
firstError.message?.includes('service not enabled') ?? false;
|
||||
|
||||
return isFailedPrecondition && isServiceNotEnabled;
|
||||
const isServiceNotEnabled =
|
||||
firstError.message?.toLowerCase()?.includes('service not enabled') ??
|
||||
false;
|
||||
|
||||
const isPreconditionCheckFailed =
|
||||
firstError.message
|
||||
?.toLowerCase()
|
||||
?.includes('precondition check failed') ?? false;
|
||||
|
||||
return (
|
||||
isFailedPrecondition && (isServiceNotEnabled || isPreconditionCheckFailed)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +118,13 @@ export class GoogleAPIsService {
|
||||
input.accessToken,
|
||||
);
|
||||
|
||||
if (!isMessagingAvailable && !isCalendarAvailable) {
|
||||
throw new AuthException(
|
||||
'Unable to connect: Your Google account does not have access to Gmail or Calendar. Please contact your workspace administrator.',
|
||||
AuthExceptionCode.INSUFFICIENT_SCOPES,
|
||||
);
|
||||
}
|
||||
|
||||
const authContext = buildSystemAuthContext(workspaceId);
|
||||
|
||||
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
|
||||
Reference in New Issue
Block a user