fix: filter Google Workspace credentials by current user (#26216)

* fix: filter Google Workspace credentials by current user

Add userId filter to getUsersFromGWorkspace credential query and add
unit tests for all googleWorkspace handler functions. Also fix OAuth2Client
import to use google-auth-library for proper type compatibility

* refactor(tests): improve googleWorkspace handler test structure

Move dynamic imports to beforeEach block to reduce repetition
and improve test readability

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
This commit is contained in:
Pedro Castro
2026-01-08 14:44:50 +00:00
committed by GitHub
co-authored by Keith Williams
parent f629b61be7
commit f8dcc6b13d
2 changed files with 166 additions and 3 deletions
@@ -0,0 +1,162 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import type { TrpcSessionUser } from "../../../types";
// Mock prisma
vi.mock("@calcom/prisma", () => ({
prisma: {
credential: {
findFirst: vi.fn(),
deleteMany: vi.fn(),
},
},
}));
// Mock getAppKeysFromSlug
vi.mock("@calcom/app-store/_utils/getAppKeysFromSlug", () => ({
default: vi.fn(),
}));
// Factory function to create mock user context
const createMockContext = (overrides: Partial<TrpcSessionUser> = {}) => ({
ctx: {
user: {
id: 1,
email: "test@example.com",
username: "testuser",
...overrides,
} as NonNullable<TrpcSessionUser>,
},
});
// Factory function to create mock credential matching Prisma's Credential model
const createMockCredential = (overrides: Record<string, unknown> = {}) => ({
id: 1,
type: "google_workspace_directory",
key: {
refresh_token: "mock_refresh_token",
access_token: "mock_access_token",
},
userId: 1,
teamId: null,
appId: null,
subscriptionId: null,
paymentStatus: null,
billingCycleStart: null,
invalid: false,
delegationCredentialId: null,
...overrides,
});
describe("googleWorkspace.handler", () => {
let prisma: Awaited<typeof import("@calcom/prisma")>["prisma"];
let getAppKeysFromSlug: Awaited<typeof import("@calcom/app-store/_utils/getAppKeysFromSlug")>["default"];
let handlers: typeof import("./googleWorkspace.handler");
beforeEach(async () => {
vi.clearAllMocks();
({ prisma } = await import("@calcom/prisma"));
getAppKeysFromSlug = (await import("@calcom/app-store/_utils/getAppKeysFromSlug")).default;
handlers = await import("./googleWorkspace.handler");
});
describe("checkForGWorkspace", () => {
it("queries credentials filtered by current user id", async () => {
const mockContext = createMockContext({ id: 42 });
vi.mocked(prisma.credential.findFirst).mockResolvedValue(null);
await handlers.checkForGWorkspace(mockContext);
expect(prisma.credential.findFirst).toHaveBeenCalledWith({
where: {
type: "google_workspace_directory",
userId: 42,
},
});
});
it("returns credential id when found", async () => {
const mockCredential = createMockCredential({ id: 123 });
vi.mocked(prisma.credential.findFirst).mockResolvedValue(mockCredential);
const result = await handlers.checkForGWorkspace(createMockContext());
expect(result).toEqual({ id: 123 });
});
it("returns undefined id when no credential found", async () => {
vi.mocked(prisma.credential.findFirst).mockResolvedValue(null);
const result = await handlers.checkForGWorkspace(createMockContext());
expect(result).toEqual({ id: undefined });
});
});
describe("getUsersFromGWorkspace", () => {
it("queries credentials filtered by current user id", async () => {
const mockContext = createMockContext({ id: 99 });
vi.mocked(getAppKeysFromSlug).mockResolvedValue({
client_id: "mock_client_id",
client_secret: "mock_client_secret",
});
vi.mocked(prisma.credential.findFirst).mockResolvedValue(null);
await expect(handlers.getUsersFromGWorkspace(mockContext)).rejects.toThrow(
"No workspace credentials found"
);
expect(prisma.credential.findFirst).toHaveBeenCalledWith({
where: {
type: "google_workspace_directory",
userId: 99,
},
});
});
it("throws error when Google client_id is missing", async () => {
vi.mocked(getAppKeysFromSlug).mockResolvedValue({});
await expect(handlers.getUsersFromGWorkspace(createMockContext())).rejects.toThrow(
"Google client_id missing."
);
});
it("throws error when no credentials found for user", async () => {
vi.mocked(getAppKeysFromSlug).mockResolvedValue({
client_id: "mock_client_id",
client_secret: "mock_client_secret",
});
vi.mocked(prisma.credential.findFirst).mockResolvedValue(null);
await expect(handlers.getUsersFromGWorkspace(createMockContext())).rejects.toThrow(
"No workspace credentials found"
);
});
});
describe("removeCurrentGoogleWorkspaceConnection", () => {
it("deletes credentials filtered by current user id", async () => {
const mockContext = createMockContext({ id: 55 });
vi.mocked(prisma.credential.deleteMany).mockResolvedValue({ count: 1 });
await handlers.removeCurrentGoogleWorkspaceConnection(mockContext);
expect(prisma.credential.deleteMany).toHaveBeenCalledWith({
where: {
type: "google_workspace_directory",
userId: 55,
},
});
});
it("returns count of deleted credentials", async () => {
vi.mocked(prisma.credential.deleteMany).mockResolvedValue({ count: 2 });
const result = await handlers.removeCurrentGoogleWorkspaceConnection(createMockContext());
expect(result).toEqual({ deleted: 2 });
});
});
});
@@ -1,5 +1,5 @@
import { admin_directory_v1 } from "@googleapis/admin";
import { OAuth2Client } from "googleapis-common";
import { OAuth2Client } from "google-auth-library";
import { z } from "zod";
import getAppKeysFromSlug from "@calcom/app-store/_utils/getAppKeysFromSlug";
@@ -33,7 +33,7 @@ export const checkForGWorkspace = async ({ ctx }: CheckForGCalOptions) => {
return { id: gWorkspacePresent?.id };
};
export const getUsersFromGWorkspace = async ({}: CheckForGCalOptions) => {
export const getUsersFromGWorkspace = async ({ ctx }: CheckForGCalOptions) => {
const { client_id, client_secret } = await getAppKeysFromSlug("google-calendar");
if (!client_id || typeof client_id !== "string") throw new Error("Google client_id missing.");
if (!client_secret || typeof client_secret !== "string") throw new Error("Google client_secret missing.");
@@ -41,6 +41,7 @@ export const getUsersFromGWorkspace = async ({}: CheckForGCalOptions) => {
const hasExistingCredentials = await prisma.credential.findFirst({
where: {
type: "google_workspace_directory",
userId: ctx.user.id,
},
});
if (!hasExistingCredentials) {
@@ -56,7 +57,7 @@ export const getUsersFromGWorkspace = async ({}: CheckForGCalOptions) => {
// Create a new instance of the Admin SDK directory API
const directory = new admin_directory_v1.Admin({
auth: oAuth2Client as any,
auth: oAuth2Client,
});
const { data } = await directory.users.list({
maxResults: 200, // Up this if we ever need to get more than 200 users