refactor: apply biome formatting to packages/trpc (#27928)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
f8a93414a5
commit
21d28c9747
@@ -56,8 +56,8 @@ export const trpc: CreateTRPCNext<AppRouter, NextPageContext, null> = createTRPC
|
||||
typeof window !== "undefined"
|
||||
? "/api/trpc"
|
||||
: process.env.VERCEL_URL
|
||||
? `https://${process.env.VERCEL_URL}/api/trpc`
|
||||
: `${process.env.NEXT_PUBLIC_WEBAPP_URL}/api/trpc`;
|
||||
? `https://${process.env.VERCEL_URL}/api/trpc`
|
||||
: `${process.env.NEXT_PUBLIC_WEBAPP_URL}/api/trpc`;
|
||||
|
||||
/**
|
||||
* If you want to use SSR, you need to use the server's full URL
|
||||
|
||||
@@ -22,23 +22,22 @@ export type CreateInnerContextOptions = {
|
||||
sourceIp?: string;
|
||||
session?: Session | null;
|
||||
locale: string;
|
||||
user?:
|
||||
| Omit<
|
||||
PrismaUser,
|
||||
| "locale"
|
||||
| "twoFactorSecret"
|
||||
| "emailVerified"
|
||||
| "password"
|
||||
| "identityProviderId"
|
||||
| "invitedTo"
|
||||
| "allowDynamicBooking"
|
||||
| "verified"
|
||||
> & {
|
||||
locale: Exclude<PrismaUser["locale"], null>;
|
||||
credentials?: Credential[];
|
||||
selectedCalendars?: Partial<SelectedCalendar>[];
|
||||
rawAvatar?: string;
|
||||
};
|
||||
user?: Omit<
|
||||
PrismaUser,
|
||||
| "locale"
|
||||
| "twoFactorSecret"
|
||||
| "emailVerified"
|
||||
| "password"
|
||||
| "identityProviderId"
|
||||
| "invitedTo"
|
||||
| "allowDynamicBooking"
|
||||
| "verified"
|
||||
> & {
|
||||
locale: Exclude<PrismaUser["locale"], null>;
|
||||
credentials?: Credential[];
|
||||
selectedCalendars?: Partial<SelectedCalendar>[];
|
||||
rawAvatar?: string;
|
||||
};
|
||||
i18n?: Awaited<ReturnType<typeof serverSideTranslations>>;
|
||||
} & Partial<CreateContextOptions>;
|
||||
|
||||
|
||||
@@ -45,10 +45,10 @@ export async function getUserFromSession(ctx: TRPCContextInner, session: Maybe<S
|
||||
safeStringify({ user, userFromDb, upId })
|
||||
);
|
||||
|
||||
const { email, username, id, uuid } = user;
|
||||
if (!email || !id) {
|
||||
return null; // should we return null here?
|
||||
}
|
||||
const { email, username, id, uuid } = user;
|
||||
if (!email || !id) {
|
||||
return null; // should we return null here?
|
||||
}
|
||||
|
||||
const userMetaData = userMetadata.parse(user.metadata || {});
|
||||
const orgMetadata = teamMetadataSchema.parse(user.profile?.organization?.metadata || {});
|
||||
@@ -71,20 +71,20 @@ export async function getUserFromSession(ctx: TRPCContextInner, session: Maybe<S
|
||||
requestedSlug: orgMetadata?.requestedSlug ?? null,
|
||||
};
|
||||
|
||||
return {
|
||||
...user,
|
||||
avatar: `${WEBAPP_URL}/${user.username}/avatar.png${organization.id ? `?orgId=${organization.id}` : ""}`,
|
||||
// TODO: OrgNewSchema - later - We could consolidate the props in user.profile?.organization as organization is a profile thing now.
|
||||
organization,
|
||||
organizationId: organization.id,
|
||||
id,
|
||||
uuid,
|
||||
email,
|
||||
username,
|
||||
locale,
|
||||
defaultBookerLayouts: userMetaData?.defaultBookerLayouts || null,
|
||||
requiresBookerEmailVerification: user.requiresBookerEmailVerification,
|
||||
};
|
||||
return {
|
||||
...user,
|
||||
avatar: `${WEBAPP_URL}/${user.username}/avatar.png${organization.id ? `?orgId=${organization.id}` : ""}`,
|
||||
// TODO: OrgNewSchema - later - We could consolidate the props in user.profile?.organization as organization is a profile thing now.
|
||||
organization,
|
||||
organizationId: organization.id,
|
||||
id,
|
||||
uuid,
|
||||
email,
|
||||
username,
|
||||
locale,
|
||||
defaultBookerLayouts: userMetaData?.defaultBookerLayouts || null,
|
||||
requiresBookerEmailVerification: user.requiresBookerEmailVerification,
|
||||
};
|
||||
}
|
||||
|
||||
export type UserFromSession = Awaited<ReturnType<typeof getUserFromSession>>;
|
||||
|
||||
@@ -134,7 +134,8 @@ describe("Feature Opt-In PBAC Procedures", () => {
|
||||
permissions.forEach(({ permission, operation }) => {
|
||||
it(`should use ${permission} for ${operation} operations`, async () => {
|
||||
createTeamPbacProcedure(permission);
|
||||
const middleware = mockAuthedProcedure.use.mock.calls[mockAuthedProcedure.use.mock.calls.length - 1][0];
|
||||
const middleware =
|
||||
mockAuthedProcedure.use.mock.calls[mockAuthedProcedure.use.mock.calls.length - 1][0];
|
||||
|
||||
mockCheckPermission.mockResolvedValue(true);
|
||||
|
||||
@@ -298,7 +299,8 @@ describe("Feature Opt-In PBAC Procedures", () => {
|
||||
permissions.forEach(({ permission, operation }) => {
|
||||
it(`should use ${permission} for ${operation} operations`, async () => {
|
||||
createOrgPbacProcedure(permission);
|
||||
const middleware = mockAuthedProcedure.use.mock.calls[mockAuthedProcedure.use.mock.calls.length - 1][0];
|
||||
const middleware =
|
||||
mockAuthedProcedure.use.mock.calls[mockAuthedProcedure.use.mock.calls.length - 1][0];
|
||||
|
||||
const mockCtx = {
|
||||
user: {
|
||||
|
||||
@@ -27,7 +27,7 @@ const getHandler = async <
|
||||
T extends {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
default: Function;
|
||||
}
|
||||
},
|
||||
>(
|
||||
/**
|
||||
* The name of the handler in cache. It has to be unique across all routes
|
||||
|
||||
+7
-4
@@ -407,7 +407,9 @@ describe("entityPrismaWhereClause Integration Tests", () => {
|
||||
ctx: { prisma, user },
|
||||
input: { id: "test" },
|
||||
});
|
||||
} catch { void 0; }
|
||||
} catch {
|
||||
void 0;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
whereClause = (prisma.app_RoutingForms_Form.deleteMany as any).mock.calls[0][0].where;
|
||||
@@ -440,15 +442,16 @@ describe("entityPrismaWhereClause Integration Tests", () => {
|
||||
input: { id: "test" },
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const formQueryWhere = (prisma.app_RoutingForms_Form.findFirst as any).mock.calls[0][0].where
|
||||
.AND[0];
|
||||
const formQueryWhere = (prisma.app_RoutingForms_Form.findFirst as any).mock.calls[0][0].where.AND[0];
|
||||
|
||||
try {
|
||||
await deleteFormHandler({
|
||||
ctx: { prisma, user },
|
||||
input: { id: "test" },
|
||||
});
|
||||
} catch { void 0; }
|
||||
} catch {
|
||||
void 0;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const deleteFormWhere = (prisma.app_RoutingForms_Form.deleteMany as any).mock.calls[0][0].where;
|
||||
|
||||
|
||||
@@ -363,8 +363,7 @@ export const formMutationHandler = async ({ ctx, input }: FormMutationHandlerOpt
|
||||
});
|
||||
}
|
||||
|
||||
let fields: NonNullable<typeof fieldsParsed.data>,
|
||||
routes: NonNullable<typeof routesParsed.data>;
|
||||
let fields: NonNullable<typeof fieldsParsed.data>, routes: NonNullable<typeof routesParsed.data>;
|
||||
if (shouldConnect) {
|
||||
routes = [
|
||||
// This connected route would automatically link the fields
|
||||
|
||||
@@ -67,7 +67,13 @@ export const teamsAndUserProfilesQuery = async ({ ctx, input }: TeamsAndUserProf
|
||||
throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" });
|
||||
}
|
||||
|
||||
let teamsData: typeof user.teams extends (infer T)[] ? (T & { team: T extends { team: infer U } ? U & { metadata: ReturnType<typeof teamMetadataSchema.parse> } : never })[] : never;
|
||||
let teamsData: typeof user.teams extends (infer T)[]
|
||||
? (T & {
|
||||
team: T extends { team: infer U }
|
||||
? U & { metadata: ReturnType<typeof teamMetadataSchema.parse> }
|
||||
: never;
|
||||
})[]
|
||||
: never;
|
||||
|
||||
if (input?.includeOrg) {
|
||||
teamsData = user.teams
|
||||
|
||||
+6
-4
@@ -5,7 +5,9 @@ export type TUserEmailVerificationRequiredSchema = {
|
||||
email: string;
|
||||
};
|
||||
|
||||
export const ZUserEmailVerificationRequiredSchema: z.ZodType<TUserEmailVerificationRequiredSchema> = z.object({
|
||||
userSessionEmail: z.string().optional(),
|
||||
email: z.string(),
|
||||
});
|
||||
export const ZUserEmailVerificationRequiredSchema: z.ZodType<TUserEmailVerificationRequiredSchema> = z.object(
|
||||
{
|
||||
userSessionEmail: z.string().optional(),
|
||||
email: z.string(),
|
||||
}
|
||||
);
|
||||
|
||||
@@ -13,14 +13,16 @@ export type TEventInputSchemaInput = {
|
||||
|
||||
export type TEventInputSchema = GetPublicEventInput;
|
||||
|
||||
export const ZEventInputSchema: z.ZodType<TEventInputSchema, z.ZodTypeDef, TEventInputSchemaInput> = z.object({
|
||||
username: z.string(),
|
||||
eventSlug: z.string(),
|
||||
isTeamEvent: z.boolean().optional(),
|
||||
org: z.string().nullable(),
|
||||
/**
|
||||
* Informs that the event request has been sent from a page that was reached by a redirect from non-org link(i.e. app.cal.com/username redirected to acme.cal.com/username)
|
||||
* Based on this decision like whether to allow unpublished organization's event to be served or not can be made.
|
||||
*/
|
||||
fromRedirectOfNonOrgLink: z.boolean().optional().default(false),
|
||||
});
|
||||
export const ZEventInputSchema: z.ZodType<TEventInputSchema, z.ZodTypeDef, TEventInputSchemaInput> = z.object(
|
||||
{
|
||||
username: z.string(),
|
||||
eventSlug: z.string(),
|
||||
isTeamEvent: z.boolean().optional(),
|
||||
org: z.string().nullable(),
|
||||
/**
|
||||
* Informs that the event request has been sent from a page that was reached by a redirect from non-org link(i.e. app.cal.com/username redirected to acme.cal.com/username)
|
||||
* Based on this decision like whether to allow unpublished organization's event to be served or not can be made.
|
||||
*/
|
||||
fromRedirectOfNonOrgLink: z.boolean().optional().default(false),
|
||||
}
|
||||
);
|
||||
|
||||
@@ -23,12 +23,10 @@ export const watchlistRouter = router({
|
||||
const { deleteWatchlistEntryHandler: handler } = await import("./delete.handler");
|
||||
return handler(opts);
|
||||
}),
|
||||
bulkDelete: authedAdminProcedure
|
||||
.input(ZBulkDeleteWatchlistEntriesInputSchema)
|
||||
.mutation(async (opts) => {
|
||||
const { bulkDeleteWatchlistEntriesHandler: handler } = await import("./bulkDelete.handler");
|
||||
return handler(opts);
|
||||
}),
|
||||
bulkDelete: authedAdminProcedure.input(ZBulkDeleteWatchlistEntriesInputSchema).mutation(async (opts) => {
|
||||
const { bulkDeleteWatchlistEntriesHandler: handler } = await import("./bulkDelete.handler");
|
||||
return handler(opts);
|
||||
}),
|
||||
getDetails: authedAdminProcedure.input(ZGetWatchlistEntryDetailsInputSchema).query(async (opts) => {
|
||||
const { getWatchlistEntryDetailsHandler: handler } = await import("./getDetails.handler");
|
||||
return handler(opts);
|
||||
|
||||
@@ -12,4 +12,4 @@ export const ZListCallsInputSchema = z.object({
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type TListCallsInputSchema = z.infer<typeof ZListCallsInputSchema>;
|
||||
export type TListCallsInputSchema = z.infer<typeof ZListCallsInputSchema>;
|
||||
|
||||
@@ -12,4 +12,4 @@ export const listVoicesHandler = async ({ ctx }: ListVoicesHandlerOptions) => {
|
||||
const aiService = createDefaultAIPhoneServiceProvider();
|
||||
|
||||
return await aiService.listVoices();
|
||||
};
|
||||
};
|
||||
|
||||
@@ -232,4 +232,3 @@ describe("appByIdHandler", () => {
|
||||
expect(result).not.toHaveProperty("key");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -68,10 +68,13 @@ export const listLocalHandler = async ({ ctx, input }: ListLocalOptions) => {
|
||||
// it is important to avoid string to string comparisons as much as we can
|
||||
if (keysSchema !== undefined) {
|
||||
// TODO: Why don't we parse with schema here? Not doing it makes default() not work in schema.
|
||||
Object.values(keysSchema.keyof()._def.values).reduce((keysObject, key) => {
|
||||
keys[key as string] = "";
|
||||
return keysObject;
|
||||
}, {} as Record<string, string>);
|
||||
Object.values(keysSchema.keyof()._def.values).reduce(
|
||||
(keysObject, key) => {
|
||||
keys[key as string] = "";
|
||||
return keysObject;
|
||||
},
|
||||
{} as Record<string, string>
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -17,10 +17,7 @@ type CreateAttributeSyncOptions = {
|
||||
input: ZCreateAttributeSyncSchema;
|
||||
};
|
||||
|
||||
const createAttributeSyncHandler = async ({
|
||||
ctx,
|
||||
input,
|
||||
}: CreateAttributeSyncOptions) => {
|
||||
const createAttributeSyncHandler = async ({ ctx, input }: CreateAttributeSyncOptions) => {
|
||||
const org = ctx.user.organization;
|
||||
|
||||
if (!org?.id) {
|
||||
@@ -33,10 +30,7 @@ const createAttributeSyncHandler = async ({
|
||||
const integrationAttributeSyncService = getIntegrationAttributeSyncService();
|
||||
|
||||
try {
|
||||
return await integrationAttributeSyncService.createAttributeSync(
|
||||
input,
|
||||
org.id
|
||||
);
|
||||
return await integrationAttributeSyncService.createAttributeSync(input, org.id);
|
||||
} catch (error) {
|
||||
if (error instanceof CredentialNotFoundError) {
|
||||
throw new TRPCError({
|
||||
|
||||
@@ -16,10 +16,7 @@ type UpdateAttributeSyncOptions = {
|
||||
input: ZUpdateAttributeSyncSchema;
|
||||
};
|
||||
|
||||
const updateAttributeSyncHandler = async ({
|
||||
ctx,
|
||||
input,
|
||||
}: UpdateAttributeSyncOptions) => {
|
||||
const updateAttributeSyncHandler = async ({ ctx, input }: UpdateAttributeSyncOptions) => {
|
||||
const org = ctx.user.organization;
|
||||
|
||||
if (!org?.id) {
|
||||
@@ -32,8 +29,7 @@ const updateAttributeSyncHandler = async ({
|
||||
const integrationAttributeSyncService = getIntegrationAttributeSyncService();
|
||||
|
||||
// Verify the sync exists and belongs to the user's organization
|
||||
const integrationAttributeSync =
|
||||
await integrationAttributeSyncService.getById(input.id);
|
||||
const integrationAttributeSync = await integrationAttributeSyncService.getById(input.id);
|
||||
|
||||
if (!integrationAttributeSync) throw new TRPCError({ code: "NOT_FOUND" });
|
||||
|
||||
|
||||
@@ -103,7 +103,9 @@ const editAttributesHandler = async ({ input, ctx }: GetOptions) => {
|
||||
await validateOptionsBelongToAttribute(options, attributes.id);
|
||||
|
||||
await prisma.$transaction(async (tx) => {
|
||||
const updateOptions = options.filter((option): option is typeof option & { id: string } => option.id !== undefined && option.id !== "");
|
||||
const updateOptions = options.filter(
|
||||
(option): option is typeof option & { id: string } => option.id !== undefined && option.id !== ""
|
||||
);
|
||||
const updatedOptionsIds = updateOptions.map((option) => option.id);
|
||||
// We need to delete all options that are not present in this UpdateOptions.id (as they have been deleted)
|
||||
await tx.attributeOption.deleteMany({
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Ensures that contains has no non-existent sub-options
|
||||
*/
|
||||
export function getOptionsWithValidContains<
|
||||
T extends { id?: string; value: string; contains?: string[]; isGroup?: boolean }
|
||||
T extends { id?: string; value: string; contains?: string[]; isGroup?: boolean },
|
||||
>(options: T[]): T[] {
|
||||
return options
|
||||
.filter((obj, index, self) => index === self.findIndex((t) => t.value === obj.value))
|
||||
|
||||
+5
-3
@@ -14,9 +14,11 @@ vi.mock("@calcom/lib/hasEditPermissionForUser", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("@calcom/features/schedules/repositories/ScheduleRepository", () => ({
|
||||
ScheduleRepository: vi.fn().mockImplementation(function() { return {
|
||||
getDefaultScheduleId: vi.fn().mockResolvedValue(DEFAULT_SCHEDULE_ID),
|
||||
}; }),
|
||||
ScheduleRepository: vi.fn().mockImplementation(function () {
|
||||
return {
|
||||
getDefaultScheduleId: vi.fn().mockResolvedValue(DEFAULT_SCHEDULE_ID),
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
import { hasReadPermissionsForUserId } from "@calcom/lib/hasEditPermissionForUser";
|
||||
|
||||
+4
-1
@@ -20,7 +20,10 @@ type GetOptions = {
|
||||
export const getAllSchedulesByUserIdHandler = async ({ ctx, input }: GetOptions) => {
|
||||
const { user } = ctx;
|
||||
|
||||
const isCurrentUserPartOfTeam = await hasReadPermissionsForUserId({ memberId: input?.userId, userId: user.id });
|
||||
const isCurrentUserPartOfTeam = await hasReadPermissionsForUserId({
|
||||
memberId: input?.userId,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
const isCurrentUserOwner = input?.userId === user.id;
|
||||
|
||||
|
||||
@@ -137,7 +137,9 @@ async function getAllCredentialsIncludeServiceAccountKey({
|
||||
}) {
|
||||
const credentials = await getUsersCredentialsIncludeServiceAccountKey(user);
|
||||
|
||||
let conferenceCredential: Awaited<ReturnType<typeof CredentialRepository.findFirstByIdWithKeyAndUser>> | undefined;
|
||||
let conferenceCredential:
|
||||
| Awaited<ReturnType<typeof CredentialRepository.findFirstByIdWithKeyAndUser>>
|
||||
| undefined;
|
||||
|
||||
if (conferenceCredentialId) {
|
||||
// Validate that the credential is accessible before fetching it
|
||||
|
||||
@@ -225,9 +225,7 @@ describe("getBookings - integration", () => {
|
||||
});
|
||||
|
||||
const testBookingIds = [booking1.id, booking2.id, booking3.id];
|
||||
const relevantBookingIds = result.bookings
|
||||
.map((b) => b.id)
|
||||
.filter((id) => testBookingIds.includes(id));
|
||||
const relevantBookingIds = result.bookings.map((b) => b.id).filter((id) => testBookingIds.includes(id));
|
||||
|
||||
const uniqueIds = new Set(relevantBookingIds);
|
||||
expect(relevantBookingIds.length).toBe(uniqueIds.size);
|
||||
|
||||
@@ -4,61 +4,64 @@ import type { PrismaClient } from "@calcom/prisma/client";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
||||
import { getBookingHistoryViewerService } from "@calcom/features/booking-audit/di/BookingHistoryViewerService.container";
|
||||
import { BookingAuditErrorCode, BookingAuditPermissionError } from "@calcom/features/booking-audit/lib/service/BookingAuditAccessService";
|
||||
import {
|
||||
BookingAuditErrorCode,
|
||||
BookingAuditPermissionError,
|
||||
} from "@calcom/features/booking-audit/lib/service/BookingAuditAccessService";
|
||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||
|
||||
import type { TrpcSessionUser } from "../../../types";
|
||||
import type { TGetBookingHistoryInputSchema } from "./getBookingHistory.schema";
|
||||
|
||||
type GetBookingHistoryOptions = {
|
||||
ctx: {
|
||||
user: NonNullable<TrpcSessionUser>;
|
||||
prisma: PrismaClient;
|
||||
};
|
||||
input: TGetBookingHistoryInputSchema;
|
||||
ctx: {
|
||||
user: NonNullable<TrpcSessionUser>;
|
||||
prisma: PrismaClient;
|
||||
};
|
||||
input: TGetBookingHistoryInputSchema;
|
||||
};
|
||||
|
||||
const getErrorMessage = (code: BookingAuditErrorCode, t: TFunction): string => {
|
||||
switch (code) {
|
||||
case BookingAuditErrorCode.ORGANIZATION_ID_REQUIRED:
|
||||
return t("audit_logs_organization_required");
|
||||
case BookingAuditErrorCode.BOOKING_NOT_FOUND_OR_PERMISSION_DENIED:
|
||||
return t("audit_logs_booking_not_found_or_permission_denied");
|
||||
case BookingAuditErrorCode.BOOKING_HAS_NO_OWNER:
|
||||
return t("audit_logs_booking_has_no_owner");
|
||||
case BookingAuditErrorCode.OWNER_NOT_IN_ORGANIZATION:
|
||||
return t("audit_logs_owner_not_in_organization");
|
||||
case BookingAuditErrorCode.PERMISSION_DENIED:
|
||||
return t("audit_logs_permission_denied");
|
||||
default:
|
||||
return t("audit_logs_permission_check_error");
|
||||
}
|
||||
switch (code) {
|
||||
case BookingAuditErrorCode.ORGANIZATION_ID_REQUIRED:
|
||||
return t("audit_logs_organization_required");
|
||||
case BookingAuditErrorCode.BOOKING_NOT_FOUND_OR_PERMISSION_DENIED:
|
||||
return t("audit_logs_booking_not_found_or_permission_denied");
|
||||
case BookingAuditErrorCode.BOOKING_HAS_NO_OWNER:
|
||||
return t("audit_logs_booking_has_no_owner");
|
||||
case BookingAuditErrorCode.OWNER_NOT_IN_ORGANIZATION:
|
||||
return t("audit_logs_owner_not_in_organization");
|
||||
case BookingAuditErrorCode.PERMISSION_DENIED:
|
||||
return t("audit_logs_permission_denied");
|
||||
default:
|
||||
return t("audit_logs_permission_check_error");
|
||||
}
|
||||
};
|
||||
|
||||
export const getBookingHistoryHandler = async ({ ctx, input }: GetBookingHistoryOptions) => {
|
||||
const { user } = ctx;
|
||||
const { bookingUid } = input;
|
||||
const { user } = ctx;
|
||||
const { bookingUid } = input;
|
||||
|
||||
const t = await getTranslation(user.locale ?? "en", "common");
|
||||
const bookingHistoryViewerService = getBookingHistoryViewerService();
|
||||
const t = await getTranslation(user.locale ?? "en", "common");
|
||||
const bookingHistoryViewerService = getBookingHistoryViewerService();
|
||||
|
||||
try {
|
||||
const result = await bookingHistoryViewerService.getHistoryForBooking({
|
||||
bookingUid,
|
||||
userId: user.id,
|
||||
userEmail: user.email,
|
||||
userTimeZone: user.timeZone,
|
||||
organizationId: user.organizationId,
|
||||
});
|
||||
try {
|
||||
const result = await bookingHistoryViewerService.getHistoryForBooking({
|
||||
bookingUid,
|
||||
userId: user.id,
|
||||
userEmail: user.email,
|
||||
userTimeZone: user.timeZone,
|
||||
organizationId: user.organizationId,
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (error instanceof BookingAuditPermissionError) {
|
||||
throw new TRPCError({
|
||||
code: "FORBIDDEN",
|
||||
message: getErrorMessage(error.code, t),
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (error instanceof BookingAuditPermissionError) {
|
||||
throw new TRPCError({
|
||||
code: "FORBIDDEN",
|
||||
message: getErrorMessage(error.code, t),
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -58,9 +58,15 @@ describe("reportBookingHandler", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
vi.mocked(BookingRepository).mockImplementation(function() { return mockBookingRepo; });
|
||||
vi.mocked(PrismaBookingReportRepository).mockImplementation(function() { return mockReportRepo; });
|
||||
vi.mocked(BookingAccessService).mockImplementation(function() { return mockBookingAccessService; });
|
||||
vi.mocked(BookingRepository).mockImplementation(function () {
|
||||
return mockBookingRepo;
|
||||
});
|
||||
vi.mocked(PrismaBookingReportRepository).mockImplementation(function () {
|
||||
return mockReportRepo;
|
||||
});
|
||||
vi.mocked(BookingAccessService).mockImplementation(function () {
|
||||
return mockBookingAccessService;
|
||||
});
|
||||
mockReportRepo.createReport.mockResolvedValue({ id: "new-report" });
|
||||
});
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ export default async function handler({
|
||||
|
||||
return ensureNoServiceAccountKey({
|
||||
...delegation,
|
||||
serviceAccountClientId: serviceAccountKey.success ? serviceAccountKey.data?.client_id ?? null : null,
|
||||
serviceAccountClientId: serviceAccountKey.success ? (serviceAccountKey.data?.client_id ?? null) : null,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
+10
-10
@@ -56,9 +56,9 @@ describe("toggleDelegationCredentialEnabled - Security Fix", () => {
|
||||
|
||||
vi.mocked(DelegationCredentialRepository.findById).mockResolvedValue(mockCredential);
|
||||
|
||||
await expect(
|
||||
toggleDelegationCredentialEnabled(userWithoutOrg, input)
|
||||
).rejects.toThrow("You must be part of an organization to toggle a delegation credential");
|
||||
await expect(toggleDelegationCredentialEnabled(userWithoutOrg, input)).rejects.toThrow(
|
||||
"You must be part of an organization to toggle a delegation credential"
|
||||
);
|
||||
|
||||
expect(DelegationCredentialRepository.updateById).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -84,9 +84,9 @@ describe("toggleDelegationCredentialEnabled - Security Fix", () => {
|
||||
|
||||
vi.mocked(DelegationCredentialRepository.findById).mockResolvedValue(org2Credential);
|
||||
|
||||
await expect(
|
||||
toggleDelegationCredentialEnabled(userFromOrg1, input)
|
||||
).rejects.toThrow("Delegation credential not found");
|
||||
await expect(toggleDelegationCredentialEnabled(userFromOrg1, input)).rejects.toThrow(
|
||||
"Delegation credential not found"
|
||||
);
|
||||
|
||||
expect(DelegationCredentialRepository.updateById).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -146,8 +146,8 @@ describe("toggleDelegationCredentialEnabled - Security Fix", () => {
|
||||
|
||||
vi.mocked(DelegationCredentialRepository.findById).mockResolvedValue(null);
|
||||
|
||||
await expect(
|
||||
toggleDelegationCredentialEnabled(user, input)
|
||||
).rejects.toThrow("Delegation credential not found");
|
||||
await expect(toggleDelegationCredentialEnabled(user, input)).rejects.toThrow(
|
||||
"Delegation credential not found"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,9 +37,11 @@ vi.mock("@calcom/features/profile/repositories/ProfileRepository", () => ({
|
||||
const mockFilterTeamsByEventTypeReadPermission = vi.fn();
|
||||
|
||||
vi.mock("../teamAccessUseCase", () => ({
|
||||
TeamAccessUseCase: vi.fn().mockImplementation(function() { return {
|
||||
filterTeamsByEventTypeReadPermission: mockFilterTeamsByEventTypeReadPermission,
|
||||
}; }),
|
||||
TeamAccessUseCase: vi.fn().mockImplementation(function () {
|
||||
return {
|
||||
filterTeamsByEventTypeReadPermission: mockFilterTeamsByEventTypeReadPermission,
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@calcom/features/ee/organizations/lib/getBookerUrlServer", () => ({
|
||||
|
||||
@@ -44,7 +44,9 @@ describe("createEventPbacProcedure", () => {
|
||||
checkPermission: vi.fn(),
|
||||
};
|
||||
|
||||
vi.mocked(PermissionCheckService).mockImplementation(function() { return mockPermissionCheckService as any; });
|
||||
vi.mocked(PermissionCheckService).mockImplementation(function () {
|
||||
return mockPermissionCheckService as any;
|
||||
});
|
||||
});
|
||||
|
||||
describe("personal events", () => {
|
||||
|
||||
@@ -16,10 +16,13 @@ export const filterQuerySchemaStrict: z.ZodType<TFilterQuerySchemaStrict> = z.ob
|
||||
schedulingTypes: z.nativeEnum(SchedulingType).array().optional(),
|
||||
});
|
||||
|
||||
export type TEventTypeInputSchema = {
|
||||
filters?: TFilterQuerySchemaStrict;
|
||||
forRoutingForms?: boolean;
|
||||
} | null | undefined;
|
||||
export type TEventTypeInputSchema =
|
||||
| {
|
||||
filters?: TFilterQuerySchemaStrict;
|
||||
forRoutingForms?: boolean;
|
||||
}
|
||||
| null
|
||||
| undefined;
|
||||
|
||||
export const ZEventTypeInputSchema: z.ZodType<TEventTypeInputSchema> = z
|
||||
.object({
|
||||
@@ -46,7 +49,11 @@ export type TGetEventTypesFromGroupSchema = {
|
||||
searchQuery?: string;
|
||||
};
|
||||
|
||||
export const ZGetEventTypesFromGroupSchema: z.ZodType<TGetEventTypesFromGroupSchema, z.ZodTypeDef, TGetEventTypesFromGroupSchemaInput> = z.object({
|
||||
export const ZGetEventTypesFromGroupSchema: z.ZodType<
|
||||
TGetEventTypesFromGroupSchema,
|
||||
z.ZodTypeDef,
|
||||
TGetEventTypesFromGroupSchemaInput
|
||||
> = z.object({
|
||||
filters: filterQuerySchemaStrict.optional(),
|
||||
forRoutingForms: z.boolean().optional(),
|
||||
cursor: z.number().nullish(),
|
||||
|
||||
@@ -83,10 +83,13 @@ export const getUserEventGroups = async ({ ctx, input }: GetByViewerOptions) =>
|
||||
});
|
||||
|
||||
const teamPermissionsArray = await Promise.all(teamPermissionChecks);
|
||||
const teamPermissions = teamPermissionsArray.reduce((acc, item) => {
|
||||
acc[item.teamId] = item.permissions;
|
||||
return acc;
|
||||
}, {} as Record<number, { canCreateEventType: boolean }>);
|
||||
const teamPermissions = teamPermissionsArray.reduce(
|
||||
(acc, item) => {
|
||||
acc[item.teamId] = item.permissions;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<number, { canCreateEventType: boolean }>
|
||||
);
|
||||
|
||||
return {
|
||||
eventTypeGroups: filteredEventTypeGroups,
|
||||
|
||||
@@ -49,8 +49,7 @@ describe("duplicateHandler", () => {
|
||||
await expect(duplicateHandler({ ctx, input })).rejects.toThrow(
|
||||
new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
message:
|
||||
"Error duplicating event type PrismaClientKnownRequestError: Unique constraint failed",
|
||||
message: "Error duplicating event type PrismaClientKnownRequestError: Unique constraint failed",
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -226,14 +226,13 @@ export const duplicateHandler = async ({ ctx, input }: DuplicateOptions) => {
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
|
||||
|
||||
if (Array.isArray(error.meta?.target) && error.meta?.target.includes("slug")) {
|
||||
throw new TRPCError({
|
||||
code: "CONFLICT",
|
||||
message: "duplicate_event_slug_conflict",
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
throw new TRPCError({
|
||||
code: "CONFLICT",
|
||||
message: "Unique constraint violation while creating a duplicate event.",
|
||||
|
||||
@@ -25,9 +25,7 @@ const findCredentialIdForLocationType = (
|
||||
locationType: string,
|
||||
credentials: { id: number; type: string; appId: string | null }[]
|
||||
): number | null => {
|
||||
const appMeta = Object.values(appStoreMetadata).find(
|
||||
(app) => app.appData?.location?.type === locationType
|
||||
);
|
||||
const appMeta = Object.values(appStoreMetadata).find((app) => app.appData?.location?.type === locationType);
|
||||
if (!appMeta) return null;
|
||||
|
||||
const matchingCredential = credentials.find(
|
||||
|
||||
@@ -6,9 +6,11 @@ import { TeamAccessUseCase } from "./teamAccessUseCase";
|
||||
|
||||
// Mock the PermissionCheckService
|
||||
vi.mock("@calcom/features/pbac/services/permission-check.service", () => ({
|
||||
PermissionCheckService: vi.fn().mockImplementation(function() { return {
|
||||
checkPermission: vi.fn(),
|
||||
}; }),
|
||||
PermissionCheckService: vi.fn().mockImplementation(function () {
|
||||
return {
|
||||
checkPermission: vi.fn(),
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("TeamAccessUseCase", () => {
|
||||
|
||||
@@ -251,18 +251,21 @@ export function ensureUniqueBookingFields(fields: TUpdateInputSchema["bookingFie
|
||||
return;
|
||||
}
|
||||
|
||||
fields.reduce((discoveredFields, field) => {
|
||||
if (discoveredFields[field.name]) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: `Duplicate booking field name: ${field.name}`,
|
||||
});
|
||||
}
|
||||
fields.reduce(
|
||||
(discoveredFields, field) => {
|
||||
if (discoveredFields[field.name]) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: `Duplicate booking field name: ${field.name}`,
|
||||
});
|
||||
}
|
||||
|
||||
discoveredFields[field.name] = true;
|
||||
discoveredFields[field.name] = true;
|
||||
|
||||
return discoveredFields;
|
||||
}, {} as Record<string, true>);
|
||||
return discoveredFields;
|
||||
},
|
||||
{} as Record<string, true>
|
||||
);
|
||||
}
|
||||
|
||||
export function ensureEmailOrPhoneNumberIsPresent(fields: TUpdateInputSchema["bookingFields"]) {
|
||||
|
||||
@@ -36,4 +36,3 @@ export const holidaysRouter = router({
|
||||
return checkConflictsHandler({ ctx, input });
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -6,4 +6,3 @@ export const ZCheckConflictsSchema = z.object({
|
||||
});
|
||||
|
||||
export type TCheckConflictsSchema = z.infer<typeof ZCheckConflictsSchema>;
|
||||
|
||||
|
||||
@@ -3,4 +3,3 @@ import { z } from "zod";
|
||||
export const ZGetUserSettingsSchema = z.object({}).optional();
|
||||
|
||||
export type TGetUserSettingsSchema = z.infer<typeof ZGetUserSettingsSchema>;
|
||||
|
||||
|
||||
@@ -6,4 +6,3 @@ export const ZToggleHolidaySchema = z.object({
|
||||
});
|
||||
|
||||
export type TToggleHolidaySchema = z.infer<typeof ZToggleHolidaySchema>;
|
||||
|
||||
|
||||
@@ -7,4 +7,3 @@ export const ZUpdateSettingsSchema = z.object({
|
||||
});
|
||||
|
||||
export type TUpdateSettingsSchema = z.infer<typeof ZUpdateSettingsSchema>;
|
||||
|
||||
|
||||
@@ -13,9 +13,8 @@ type MeOptions = {
|
||||
export const platformMeHandler = async ({ ctx }: MeOptions) => {
|
||||
const { user: sessionUser } = ctx;
|
||||
|
||||
const allUserEnrichedProfiles = await ProfileRepository.findAllProfilesForUserIncludingMovedUser(
|
||||
sessionUser
|
||||
);
|
||||
const allUserEnrichedProfiles =
|
||||
await ProfileRepository.findAllProfilesForUserIncludingMovedUser(sessionUser);
|
||||
|
||||
const mainProfile =
|
||||
allUserEnrichedProfiles.find((profile) => sessionUser.movedToProfileId === profile.id) ||
|
||||
|
||||
@@ -84,7 +84,11 @@ export type TUpdateProfileInputSchema = {
|
||||
}[];
|
||||
};
|
||||
|
||||
export const ZUpdateProfileInputSchema: z.ZodType<TUpdateProfileInputSchema, z.ZodTypeDef, TUpdateProfileInputSchemaInput> = z.object({
|
||||
export const ZUpdateProfileInputSchema: z.ZodType<
|
||||
TUpdateProfileInputSchema,
|
||||
z.ZodTypeDef,
|
||||
TUpdateProfileInputSchemaInput
|
||||
> = z.object({
|
||||
username: z.string().optional(),
|
||||
name: z.string().max(FULL_NAME_LENGTH_MAX_LIMIT).optional(),
|
||||
email: z.string().optional(),
|
||||
|
||||
@@ -18,7 +18,11 @@ export type TCreateClientInputSchema = {
|
||||
enablePkce: boolean;
|
||||
};
|
||||
|
||||
export const ZCreateClientInputSchema: z.ZodType<TCreateClientInputSchema, z.ZodTypeDef, TCreateClientInputSchemaInput> = z.object({
|
||||
export const ZCreateClientInputSchema: z.ZodType<
|
||||
TCreateClientInputSchema,
|
||||
z.ZodTypeDef,
|
||||
TCreateClientInputSchemaInput
|
||||
> = z.object({
|
||||
name: z.string(),
|
||||
purpose: z.string().min(1),
|
||||
redirectUri: z.string(),
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
||||
import { sendOAuthClientApprovedNotification, sendOAuthClientRejectedNotification } from "@calcom/emails/oauth-email-service";
|
||||
import {
|
||||
sendOAuthClientApprovedNotification,
|
||||
sendOAuthClientRejectedNotification,
|
||||
} from "@calcom/emails/oauth-email-service";
|
||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||
import { OAuthClientRepository } from "@calcom/features/oauth/repositories/OAuthClientRepository";
|
||||
import type { PrismaClient } from "@calcom/prisma";
|
||||
@@ -31,10 +34,7 @@ type UpdateClientOutput = {
|
||||
rejectionReason: string | null;
|
||||
};
|
||||
|
||||
const updateClientHandler = async ({
|
||||
ctx,
|
||||
input,
|
||||
}: UpdateClientOptions): Promise<UpdateClientOutput> => {
|
||||
const updateClientHandler = async ({ ctx, input }: UpdateClientOptions): Promise<UpdateClientOutput> => {
|
||||
const {
|
||||
clientId,
|
||||
status: requestedStatus,
|
||||
@@ -72,7 +72,10 @@ const updateClientHandler = async ({
|
||||
}
|
||||
|
||||
if (isUpdatingFields && !isAdmin && !isOwner) {
|
||||
throw new TRPCError({ code: "FORBIDDEN", message: "You do not have permission to update this OAuth client" });
|
||||
throw new TRPCError({
|
||||
code: "FORBIDDEN",
|
||||
message: "You do not have permission to update this OAuth client",
|
||||
});
|
||||
}
|
||||
|
||||
const shouldTriggerReapprovalForOwnerEdit = triggersReapprovalForOwnerEdit({
|
||||
@@ -188,7 +191,7 @@ function triggersReapprovalForOwnerEdit(params: {
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
proposedUpdates.websiteUrl !== undefined &&
|
||||
toNullableString(proposedUpdates.websiteUrl) !== toNullableString(currentClient.websiteUrl)
|
||||
@@ -196,7 +199,10 @@ function triggersReapprovalForOwnerEdit(params: {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (proposedUpdates.redirectUri !== undefined && proposedUpdates.redirectUri !== currentClient.redirectUri) {
|
||||
if (
|
||||
proposedUpdates.redirectUri !== undefined &&
|
||||
proposedUpdates.redirectUri !== currentClient.redirectUri
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,29 +2,31 @@ import { z } from "zod";
|
||||
|
||||
import { OAuthClientStatus } from "@calcom/prisma/enums";
|
||||
|
||||
export const ZUpdateClientInputSchema = z.object({
|
||||
clientId: z.string(),
|
||||
status: z.nativeEnum(OAuthClientStatus).optional(),
|
||||
rejectionReason: z.string().min(1).optional(),
|
||||
name: z.string().min(1).optional(),
|
||||
purpose: z.string().min(1).optional(),
|
||||
redirectUri: z.string().url().optional(),
|
||||
logo: z.preprocess(
|
||||
(value) => (typeof value === "string" && value.trim() === "" ? null : value),
|
||||
z.string().nullable().optional()
|
||||
),
|
||||
websiteUrl: z.preprocess(
|
||||
(value) => (typeof value === "string" && value.trim() === "" ? null : value),
|
||||
z.string().url().nullable().optional()
|
||||
),
|
||||
}).superRefine((val, ctx) => {
|
||||
if (val.status === OAuthClientStatus.REJECTED && !val.rejectionReason) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ["rejectionReason"],
|
||||
message: "Rejection reason is required",
|
||||
});
|
||||
}
|
||||
});
|
||||
export const ZUpdateClientInputSchema = z
|
||||
.object({
|
||||
clientId: z.string(),
|
||||
status: z.nativeEnum(OAuthClientStatus).optional(),
|
||||
rejectionReason: z.string().min(1).optional(),
|
||||
name: z.string().min(1).optional(),
|
||||
purpose: z.string().min(1).optional(),
|
||||
redirectUri: z.string().url().optional(),
|
||||
logo: z.preprocess(
|
||||
(value) => (typeof value === "string" && value.trim() === "" ? null : value),
|
||||
z.string().nullable().optional()
|
||||
),
|
||||
websiteUrl: z.preprocess(
|
||||
(value) => (typeof value === "string" && value.trim() === "" ? null : value),
|
||||
z.string().url().nullable().optional()
|
||||
),
|
||||
})
|
||||
.superRefine((val, ctx) => {
|
||||
if (val.status === OAuthClientStatus.REJECTED && !val.rejectionReason) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ["rejectionReason"],
|
||||
message: "Rejection reason is required",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export type TUpdateClientInputSchema = z.infer<typeof ZUpdateClientInputSchema>;
|
||||
|
||||
@@ -2,7 +2,10 @@ import { lookup } from "node:dns";
|
||||
|
||||
import { getOrgFullOrigin } from "@calcom/ee/organizations/lib/orgDomains";
|
||||
import { isNotACompanyEmail } from "@calcom/ee/organizations/lib/server/orgCreationUtils";
|
||||
import { sendAdminOrganizationNotification, sendOrganizationCreationEmail } from "@calcom/emails/organization-email-service";
|
||||
import {
|
||||
sendAdminOrganizationNotification,
|
||||
sendOrganizationCreationEmail,
|
||||
} from "@calcom/emails/organization-email-service";
|
||||
import { sendEmailVerification } from "@calcom/features/auth/lib/verifyEmail";
|
||||
import { getOrganizationRepository } from "@calcom/features/ee/organizations/di/OrganizationRepository.container";
|
||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||
|
||||
+3
-1
@@ -45,7 +45,9 @@ describe("deleteWatchlistEntryHandler", () => {
|
||||
);
|
||||
|
||||
vi.mocked(getOrganizationWatchlistOperationsService).mockReturnValue(mockService as never);
|
||||
vi.mocked(WatchlistRepository).mockImplementation(function() { return mockWatchlistRepo as never; });
|
||||
vi.mocked(WatchlistRepository).mockImplementation(function () {
|
||||
return mockWatchlistRepo as never;
|
||||
});
|
||||
});
|
||||
|
||||
describe("access control", () => {
|
||||
|
||||
+59
-91
@@ -2,11 +2,7 @@ import prismock from "@calcom/testing/lib/__mocks__/prisma";
|
||||
import { intentToCreateOrgHandler } from "./intentToCreateOrg.handler";
|
||||
import { LicenseKeySingleton } from "@calcom/ee/common/server/LicenseKeyService";
|
||||
import { OrganizationPaymentService } from "@calcom/features/ee/organizations/lib/OrganizationPaymentService";
|
||||
import {
|
||||
BillingPeriod,
|
||||
UserPermissionRole,
|
||||
CreationSource,
|
||||
} from "@calcom/prisma/enums";
|
||||
import { BillingPeriod, UserPermissionRole, CreationSource } from "@calcom/prisma/enums";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { describe, expect, it, vi, beforeEach } from "vitest";
|
||||
|
||||
@@ -40,29 +36,21 @@ vi.mock("@calcom/features/ee/teams/repositories/TeamRepository", () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock(
|
||||
"@calcom/trpc/server/routers/viewer/organizations/createTeams.handler",
|
||||
() => ({
|
||||
createTeamsHandler: vi.fn().mockResolvedValue({}),
|
||||
})
|
||||
);
|
||||
vi.mock("@calcom/trpc/server/routers/viewer/organizations/createTeams.handler", () => ({
|
||||
createTeamsHandler: vi.fn().mockResolvedValue({}),
|
||||
}));
|
||||
|
||||
vi.mock(
|
||||
"@calcom/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler",
|
||||
() => ({
|
||||
inviteMembersWithNoInviterPermissionCheck: vi.fn().mockResolvedValue({}),
|
||||
})
|
||||
);
|
||||
vi.mock("@calcom/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler", () => ({
|
||||
inviteMembersWithNoInviterPermissionCheck: vi.fn().mockResolvedValue({}),
|
||||
}));
|
||||
|
||||
vi.mock("@calcom/lib/server/i18n", () => ({
|
||||
getTranslation: vi
|
||||
.fn()
|
||||
.mockImplementation(async (locale: string, namespace: string) => {
|
||||
const t = (key: string) => key;
|
||||
t.locale = locale;
|
||||
t.namespace = namespace;
|
||||
return t;
|
||||
}),
|
||||
getTranslation: vi.fn().mockImplementation(async (locale: string, namespace: string) => {
|
||||
const t = (key: string) => key;
|
||||
t.locale = locale;
|
||||
t.namespace = namespace;
|
||||
return t;
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@calcom/lib/domainManager/organization", () => ({
|
||||
@@ -116,27 +104,25 @@ describe("intentToCreateOrgHandler", () => {
|
||||
|
||||
vi.mocked(OrganizationPaymentService).mockImplementation(function () {
|
||||
return {
|
||||
createOrganizationOnboarding: vi
|
||||
.fn()
|
||||
.mockImplementation(async (data: any) => {
|
||||
return await prismock.organizationOnboarding.create({
|
||||
data: {
|
||||
id: "onboarding-123",
|
||||
name: data.name,
|
||||
slug: data.slug,
|
||||
orgOwnerEmail: data.orgOwnerEmail,
|
||||
seats: data.seats ?? 10,
|
||||
pricePerSeat: data.pricePerSeat ?? 15,
|
||||
billingPeriod: data.billingPeriod ?? BillingPeriod.MONTHLY,
|
||||
isComplete: false,
|
||||
stripeCustomerId: null,
|
||||
createdById: data.createdByUserId,
|
||||
teams: data.teams ?? [],
|
||||
invitedMembers: data.invitedMembers ?? [],
|
||||
isPlatform: data.isPlatform ?? false,
|
||||
},
|
||||
});
|
||||
}),
|
||||
createOrganizationOnboarding: vi.fn().mockImplementation(async (data: any) => {
|
||||
return await prismock.organizationOnboarding.create({
|
||||
data: {
|
||||
id: "onboarding-123",
|
||||
name: data.name,
|
||||
slug: data.slug,
|
||||
orgOwnerEmail: data.orgOwnerEmail,
|
||||
seats: data.seats ?? 10,
|
||||
pricePerSeat: data.pricePerSeat ?? 15,
|
||||
billingPeriod: data.billingPeriod ?? BillingPeriod.MONTHLY,
|
||||
isComplete: false,
|
||||
stripeCustomerId: null,
|
||||
createdById: data.createdByUserId,
|
||||
teams: data.teams ?? [],
|
||||
invitedMembers: data.invitedMembers ?? [],
|
||||
isPlatform: data.isPlatform ?? false,
|
||||
},
|
||||
});
|
||||
}),
|
||||
createPaymentIntent: vi.fn().mockResolvedValue({
|
||||
checkoutUrl: "https://stripe.com/checkout/session",
|
||||
organizationOnboarding: {},
|
||||
@@ -198,25 +184,20 @@ describe("intentToCreateOrgHandler", () => {
|
||||
organizationOnboardingId: expect.any(String),
|
||||
checkoutUrl: null,
|
||||
organizationId: null, // Not created yet - handover flow
|
||||
handoverUrl: expect.stringContaining(
|
||||
"/settings/organizations/new/resume?onboardingId="
|
||||
),
|
||||
handoverUrl: expect.stringContaining("/settings/organizations/new/resume?onboardingId="),
|
||||
});
|
||||
|
||||
// Verify organization onboarding was created
|
||||
const organizationOnboarding =
|
||||
await prismock.organizationOnboarding.findFirst({
|
||||
where: {
|
||||
slug: mockInput.slug,
|
||||
},
|
||||
});
|
||||
const organizationOnboarding = await prismock.organizationOnboarding.findFirst({
|
||||
where: {
|
||||
slug: mockInput.slug,
|
||||
},
|
||||
});
|
||||
|
||||
expect(organizationOnboarding).toBeDefined();
|
||||
expect(organizationOnboarding?.name).toBe(mockInput.name);
|
||||
expect(organizationOnboarding?.slug).toBe(mockInput.slug);
|
||||
expect(organizationOnboarding?.orgOwnerEmail).toBe(
|
||||
mockInput.orgOwnerEmail
|
||||
);
|
||||
expect(organizationOnboarding?.orgOwnerEmail).toBe(mockInput.orgOwnerEmail);
|
||||
});
|
||||
|
||||
it("should allow user to create org for themselves", async () => {
|
||||
@@ -434,25 +415,20 @@ describe("intentToCreateOrgHandler", () => {
|
||||
organizationOnboardingId: expect.any(String),
|
||||
checkoutUrl: null,
|
||||
organizationId: null, // Not created yet - handover flow
|
||||
handoverUrl: expect.stringContaining(
|
||||
"/settings/organizations/new/resume?onboardingId="
|
||||
),
|
||||
handoverUrl: expect.stringContaining("/settings/organizations/new/resume?onboardingId="),
|
||||
});
|
||||
|
||||
// Verify organization onboarding was created
|
||||
const organizationOnboarding =
|
||||
await prismock.organizationOnboarding.findFirst({
|
||||
where: {
|
||||
slug: mockInput.slug,
|
||||
},
|
||||
});
|
||||
const organizationOnboarding = await prismock.organizationOnboarding.findFirst({
|
||||
where: {
|
||||
slug: mockInput.slug,
|
||||
},
|
||||
});
|
||||
|
||||
expect(organizationOnboarding).toBeDefined();
|
||||
expect(organizationOnboarding?.name).toBe(mockInput.name);
|
||||
expect(organizationOnboarding?.slug).toBe(mockInput.slug);
|
||||
expect(organizationOnboarding?.orgOwnerEmail).toBe(
|
||||
mockInput.orgOwnerEmail
|
||||
);
|
||||
expect(organizationOnboarding?.orgOwnerEmail).toBe(mockInput.orgOwnerEmail);
|
||||
});
|
||||
|
||||
it("should handle teams and invites in the request", async () => {
|
||||
@@ -492,20 +468,15 @@ describe("intentToCreateOrgHandler", () => {
|
||||
|
||||
expect(result.organizationOnboardingId).toBeDefined();
|
||||
|
||||
const organizationOnboarding =
|
||||
await prismock.organizationOnboarding.findFirst({
|
||||
where: {
|
||||
slug: mockInput.slug,
|
||||
},
|
||||
});
|
||||
const organizationOnboarding = await prismock.organizationOnboarding.findFirst({
|
||||
where: {
|
||||
slug: mockInput.slug,
|
||||
},
|
||||
});
|
||||
|
||||
expect(organizationOnboarding).toBeDefined();
|
||||
expect(organizationOnboarding?.teams).toEqual(
|
||||
inputWithTeamsAndInvites.teams
|
||||
);
|
||||
expect(organizationOnboarding?.invitedMembers).toEqual(
|
||||
inputWithTeamsAndInvites.invitedMembers
|
||||
);
|
||||
expect(organizationOnboarding?.teams).toEqual(inputWithTeamsAndInvites.teams);
|
||||
expect(organizationOnboarding?.invitedMembers).toEqual(inputWithTeamsAndInvites.invitedMembers);
|
||||
});
|
||||
|
||||
it("should preserve teamName, teamId, and role in invites payload", async () => {
|
||||
@@ -551,17 +522,14 @@ describe("intentToCreateOrgHandler", () => {
|
||||
|
||||
expect(result.organizationOnboardingId).toBeDefined();
|
||||
|
||||
const organizationOnboarding =
|
||||
await prismock.organizationOnboarding.findFirst({
|
||||
where: {
|
||||
slug: mockInput.slug,
|
||||
},
|
||||
});
|
||||
const organizationOnboarding = await prismock.organizationOnboarding.findFirst({
|
||||
where: {
|
||||
slug: mockInput.slug,
|
||||
},
|
||||
});
|
||||
|
||||
expect(organizationOnboarding).toBeDefined();
|
||||
expect(organizationOnboarding?.teams).toEqual(
|
||||
inputWithTeamsAndInvites.teams
|
||||
);
|
||||
expect(organizationOnboarding?.teams).toEqual(inputWithTeamsAndInvites.teams);
|
||||
|
||||
// Verify invitedMembers are stored with all fields including teamName, teamId, and role
|
||||
expect(organizationOnboarding?.invitedMembers).toBeDefined();
|
||||
|
||||
@@ -15,9 +15,11 @@ vi.mock("@calcom/prisma", () => ({
|
||||
// Mock FeaturesRepository
|
||||
const mockCheckIfTeamHasFeature = vi.fn();
|
||||
vi.mock("@calcom/features/flags/features.repository", () => ({
|
||||
FeaturesRepository: vi.fn().mockImplementation(function() { return {
|
||||
checkIfTeamHasFeature: mockCheckIfTeamHasFeature,
|
||||
}; }),
|
||||
FeaturesRepository: vi.fn().mockImplementation(function () {
|
||||
return {
|
||||
checkIfTeamHasFeature: mockCheckIfTeamHasFeature,
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
// Mock PBAC permissions
|
||||
@@ -31,14 +33,16 @@ vi.mock("@calcom/features/pbac/lib/resource-permissions", () => ({
|
||||
// Mock PermissionCheckService
|
||||
const mockCheckPermission = vi.fn().mockResolvedValue(true);
|
||||
vi.mock("@calcom/features/pbac/services/permission-check.service", () => ({
|
||||
PermissionCheckService: vi.fn().mockImplementation(function() { return {
|
||||
checkPermission: mockCheckPermission,
|
||||
}; }),
|
||||
PermissionCheckService: vi.fn().mockImplementation(function () {
|
||||
return {
|
||||
checkPermission: mockCheckPermission,
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
// Mock UserRepository
|
||||
vi.mock("@calcom/features/users/repositories/UserRepository", () => ({
|
||||
UserRepository: vi.fn().mockImplementation(function() {
|
||||
UserRepository: vi.fn().mockImplementation(function () {
|
||||
return {
|
||||
enrichUserWithItsProfile: vi.fn().mockImplementation(({ user }) => user),
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ export type TListMembersSchema = {
|
||||
limit: number;
|
||||
offset: number;
|
||||
searchTerm?: string;
|
||||
expand?: ("attributes")[];
|
||||
expand?: "attributes"[];
|
||||
filters?: { id: string; value: z.infer<typeof ZFilterValue> }[];
|
||||
oAuthClientId?: string;
|
||||
};
|
||||
|
||||
@@ -93,7 +93,7 @@ const updateOrganizationSettings = async ({
|
||||
if (input.hasOwnProperty("disableAutofillOnBookingPage")) {
|
||||
data.disableAutofillOnBookingPage = input.disableAutofillOnBookingPage;
|
||||
}
|
||||
|
||||
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (input.hasOwnProperty("orgAutoJoinOnSignup")) {
|
||||
data.orgAutoJoinOnSignup = input.orgAutoJoinOnSignup;
|
||||
|
||||
@@ -98,43 +98,44 @@ export type TUpdateInputSchema = {
|
||||
disableAttendeeNewEventEmail?: boolean;
|
||||
};
|
||||
|
||||
export const ZUpdateInputSchema: z.ZodType<TUpdateInputSchema, z.ZodTypeDef, TUpdateInputSchemaInput> = z.object({
|
||||
name: z.string().optional(),
|
||||
orgId: z
|
||||
.string()
|
||||
.regex(/^\d+$/)
|
||||
.transform((id) => parseInt(id))
|
||||
.or(z.number())
|
||||
.optional(),
|
||||
bio: z.string().optional(),
|
||||
logoUrl: ssrfSafeOptionalUrl,
|
||||
calVideoLogo: ssrfSafeOptionalUrl.transform((v) => v || null),
|
||||
banner: ssrfSafeOptionalUrl,
|
||||
slug: z.string().optional(),
|
||||
hideBranding: z.boolean().optional(),
|
||||
hideBookATeamMember: z.boolean().optional(),
|
||||
brandColor: z.string().optional(),
|
||||
darkBrandColor: z.string().optional(),
|
||||
theme: z.string().optional().nullable(),
|
||||
timeZone: timeZoneSchema.optional(),
|
||||
weekStart: z.string().optional(),
|
||||
timeFormat: z.number().optional(),
|
||||
metadata: teamMetadataStrictSchema.unwrap().optional(),
|
||||
lockEventTypeCreation: z.boolean().optional(),
|
||||
lockEventTypeCreationOptions: z.enum(["DELETE", "HIDE"]).optional(),
|
||||
adminGetsNoSlotsNotification: z.boolean().optional(),
|
||||
allowSEOIndexing: z.boolean().optional(),
|
||||
orgProfileRedirectsToVerifiedDomain: z.boolean().optional(),
|
||||
disablePhoneOnlySMSNotifications: z.boolean().optional(),
|
||||
disableAutofillOnBookingPage: z.boolean().optional(),
|
||||
orgAutoJoinOnSignup: z.boolean().optional(),
|
||||
disableAttendeeConfirmationEmail: z.boolean().optional(),
|
||||
disableAttendeeCancellationEmail: z.boolean().optional(),
|
||||
disableAttendeeRescheduledEmail: z.boolean().optional(),
|
||||
disableAttendeeRequestEmail: z.boolean().optional(),
|
||||
disableAttendeeReassignedEmail: z.boolean().optional(),
|
||||
disableAttendeeAwaitingPaymentEmail: z.boolean().optional(),
|
||||
disableAttendeeRescheduleRequestEmail: z.boolean().optional(),
|
||||
disableAttendeeLocationChangeEmail: z.boolean().optional(),
|
||||
disableAttendeeNewEventEmail: z.boolean().optional(),
|
||||
});
|
||||
export const ZUpdateInputSchema: z.ZodType<TUpdateInputSchema, z.ZodTypeDef, TUpdateInputSchemaInput> =
|
||||
z.object({
|
||||
name: z.string().optional(),
|
||||
orgId: z
|
||||
.string()
|
||||
.regex(/^\d+$/)
|
||||
.transform((id) => parseInt(id))
|
||||
.or(z.number())
|
||||
.optional(),
|
||||
bio: z.string().optional(),
|
||||
logoUrl: ssrfSafeOptionalUrl,
|
||||
calVideoLogo: ssrfSafeOptionalUrl.transform((v) => v || null),
|
||||
banner: ssrfSafeOptionalUrl,
|
||||
slug: z.string().optional(),
|
||||
hideBranding: z.boolean().optional(),
|
||||
hideBookATeamMember: z.boolean().optional(),
|
||||
brandColor: z.string().optional(),
|
||||
darkBrandColor: z.string().optional(),
|
||||
theme: z.string().optional().nullable(),
|
||||
timeZone: timeZoneSchema.optional(),
|
||||
weekStart: z.string().optional(),
|
||||
timeFormat: z.number().optional(),
|
||||
metadata: teamMetadataStrictSchema.unwrap().optional(),
|
||||
lockEventTypeCreation: z.boolean().optional(),
|
||||
lockEventTypeCreationOptions: z.enum(["DELETE", "HIDE"]).optional(),
|
||||
adminGetsNoSlotsNotification: z.boolean().optional(),
|
||||
allowSEOIndexing: z.boolean().optional(),
|
||||
orgProfileRedirectsToVerifiedDomain: z.boolean().optional(),
|
||||
disablePhoneOnlySMSNotifications: z.boolean().optional(),
|
||||
disableAutofillOnBookingPage: z.boolean().optional(),
|
||||
orgAutoJoinOnSignup: z.boolean().optional(),
|
||||
disableAttendeeConfirmationEmail: z.boolean().optional(),
|
||||
disableAttendeeCancellationEmail: z.boolean().optional(),
|
||||
disableAttendeeRescheduledEmail: z.boolean().optional(),
|
||||
disableAttendeeRequestEmail: z.boolean().optional(),
|
||||
disableAttendeeReassignedEmail: z.boolean().optional(),
|
||||
disableAttendeeAwaitingPaymentEmail: z.boolean().optional(),
|
||||
disableAttendeeRescheduleRequestEmail: z.boolean().optional(),
|
||||
disableAttendeeLocationChangeEmail: z.boolean().optional(),
|
||||
disableAttendeeNewEventEmail: z.boolean().optional(),
|
||||
});
|
||||
|
||||
@@ -58,10 +58,7 @@ export const chargeCardHandler = async ({ ctx, input }: ChargeCardHandlerOptions
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
message.includes("User ID is required") ||
|
||||
message.includes("No payment credential found")
|
||||
) {
|
||||
if (message.includes("User ID is required") || message.includes("No payment credential found")) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message,
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ async function getEnrichedSerializableForm<
|
||||
} | null;
|
||||
metadata: unknown;
|
||||
} | null;
|
||||
}
|
||||
},
|
||||
>({ form, prisma }: { prisma: PrismaClient; form: TForm }) {
|
||||
const formWithUserInfoProfile = {
|
||||
...form,
|
||||
|
||||
@@ -13,9 +13,11 @@ const dynamicImportHandler = async () => await import("./reserveSlot.handler");
|
||||
// The repository instance method is used to check for an existing reservation by someone else.
|
||||
// To keep this unit test isolated from the database layer, we stub this to always resolve falsey.
|
||||
vi.mock("@calcom/features/selectedSlots/repositories/PrismaSelectedSlotRepository", () => ({
|
||||
PrismaSelectedSlotRepository: vi.fn().mockImplementation(function() { return {
|
||||
findReservedByOthers: vi.fn().mockResolvedValue(null),
|
||||
}; }),
|
||||
PrismaSelectedSlotRepository: vi.fn().mockImplementation(function () {
|
||||
return {
|
||||
findReservedByOthers: vi.fn().mockResolvedValue(null),
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
// A tiny helper to build a canned handler context with stubbed Prisma methods.
|
||||
|
||||
@@ -1280,7 +1280,7 @@ export class AvailableSlotsService {
|
||||
|
||||
const restrictionTimezone = eventType.useBookerTimezone
|
||||
? input.timeZone
|
||||
: restrictionSchedule.timeZone ?? "UTC";
|
||||
: (restrictionSchedule.timeZone ?? "UTC");
|
||||
const eventLength = input.duration || eventType.length;
|
||||
|
||||
const restrictionAvailability = restrictionSchedule.availability.map((rule) => ({
|
||||
|
||||
@@ -51,7 +51,7 @@ vi.mock("@calcom/features/flags/features.repository");
|
||||
vi.mock("@calcom/features/membership/repositories/MembershipRepository");
|
||||
vi.mock("@calcom/features/pbac/services/permission.service", () => {
|
||||
return {
|
||||
PermissionService: vi.fn().mockImplementation(function() {
|
||||
PermissionService: vi.fn().mockImplementation(function () {
|
||||
return {
|
||||
validatePermission: vi.fn().mockReturnValue({ isValid: true }),
|
||||
validatePermissions: vi.fn().mockReturnValue({ isValid: true }),
|
||||
@@ -62,7 +62,7 @@ vi.mock("@calcom/features/pbac/services/permission.service", () => {
|
||||
|
||||
vi.mock("@calcom/features/pbac/services/permission-check.service", () => {
|
||||
return {
|
||||
PermissionCheckService: vi.fn().mockImplementation(function() {
|
||||
PermissionCheckService: vi.fn().mockImplementation(function () {
|
||||
return {
|
||||
checkPermission: vi.fn().mockResolvedValue(true),
|
||||
checkPermissions: vi.fn().mockResolvedValue(true),
|
||||
|
||||
@@ -51,18 +51,21 @@ function getOrgConnectionInfoGroupedByUsernameOrEmail({
|
||||
team: Pick<TeamWithParent, "parentId" | "id">;
|
||||
isOrg: boolean;
|
||||
}) {
|
||||
return uniqueInvitations.reduce((acc, invitation) => {
|
||||
return {
|
||||
...acc,
|
||||
[invitation.usernameOrEmail]: getOrgConnectionInfo({
|
||||
orgVerified: orgState.orgVerified,
|
||||
orgAutoAcceptDomain: orgState.autoAcceptEmailDomain,
|
||||
email: invitation.usernameOrEmail,
|
||||
team,
|
||||
isOrg: isOrg,
|
||||
}),
|
||||
};
|
||||
}, {} as Record<string, ReturnType<typeof getOrgConnectionInfo>>);
|
||||
return uniqueInvitations.reduce(
|
||||
(acc, invitation) => {
|
||||
return {
|
||||
...acc,
|
||||
[invitation.usernameOrEmail]: getOrgConnectionInfo({
|
||||
orgVerified: orgState.orgVerified,
|
||||
orgAutoAcceptDomain: orgState.autoAcceptEmailDomain,
|
||||
email: invitation.usernameOrEmail,
|
||||
team,
|
||||
isOrg: isOrg,
|
||||
}),
|
||||
};
|
||||
},
|
||||
{} as Record<string, ReturnType<typeof getOrgConnectionInfo>>
|
||||
);
|
||||
}
|
||||
|
||||
function getInvitationsForNewUsers({
|
||||
@@ -267,8 +270,8 @@ const inviteMembers = async ({ ctx, input }: InviteMemberOptions) => {
|
||||
|
||||
// Check if invitations are blocked due to unpaid invoices
|
||||
const dueInvoiceService = new DueInvoiceService();
|
||||
const inviteeEmails = (typeof usernameOrEmail === "string" ? [usernameOrEmail] : usernameOrEmail).map((u) =>
|
||||
typeof u === "string" ? u : u.email
|
||||
const inviteeEmails = (typeof usernameOrEmail === "string" ? [usernameOrEmail] : usernameOrEmail).map(
|
||||
(u) => (typeof u === "string" ? u : u.email)
|
||||
);
|
||||
const canInvite = await dueInvoiceService.canInviteToTeam({
|
||||
teamId: team.id,
|
||||
|
||||
@@ -7,7 +7,8 @@ export type TInviteMemberByTokenSchemaInputSchema = {
|
||||
creationSource: CreationSource;
|
||||
};
|
||||
|
||||
export const ZInviteMemberByTokenSchemaInputSchema: z.ZodType<TInviteMemberByTokenSchemaInputSchema> = z.object({
|
||||
token: z.string(),
|
||||
creationSource: z.nativeEnum(CreationSource),
|
||||
});
|
||||
export const ZInviteMemberByTokenSchemaInputSchema: z.ZodType<TInviteMemberByTokenSchemaInputSchema> =
|
||||
z.object({
|
||||
token: z.string(),
|
||||
creationSource: z.nativeEnum(CreationSource),
|
||||
});
|
||||
|
||||
@@ -96,10 +96,13 @@ export const listMembersHandler = async ({ ctx, input }: ListMembersHandlerOptio
|
||||
const roleManager = await RoleManagementFactory.getInstance().createRoleManager(organizationId);
|
||||
if (roleManager.isPBACEnabled) {
|
||||
const roles = await roleManager.getTeamRoles(teamId);
|
||||
customRoles = roles.reduce((acc, role) => {
|
||||
acc[role.id] = role;
|
||||
return acc;
|
||||
}, {} as { [key: string]: { id: string; name: string } });
|
||||
customRoles = roles.reduce(
|
||||
(acc, role) => {
|
||||
acc[role.id] = role;
|
||||
return acc;
|
||||
},
|
||||
{} as { [key: string]: { id: string; name: string } }
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// PBAC not enabled or error occurred, continue with traditional roles
|
||||
|
||||
@@ -16,7 +16,11 @@ export type TListMembersInputSchema = {
|
||||
cursor?: number | null;
|
||||
};
|
||||
|
||||
export const ZListMembersInputSchema: z.ZodType<TListMembersInputSchema, z.ZodTypeDef, TListMembersInputSchemaInput> = z.object({
|
||||
export const ZListMembersInputSchema: z.ZodType<
|
||||
TListMembersInputSchema,
|
||||
z.ZodTypeDef,
|
||||
TListMembersInputSchemaInput
|
||||
> = z.object({
|
||||
teamId: z.number(),
|
||||
limit: z.number().default(10),
|
||||
searchTerm: z.string().optional(),
|
||||
|
||||
+2
-5
@@ -14,7 +14,6 @@ import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
||||
|
||||
import type { TGetManagedEventUsersToReassignInputSchema } from "./getManagedEventUsersToReassign.schema";
|
||||
|
||||
|
||||
type GetManagedEventUsersToReassignOptions = {
|
||||
ctx: {
|
||||
user: NonNullable<TrpcSessionUser>;
|
||||
@@ -40,7 +39,6 @@ async function getManagedEventUsersFromDB({
|
||||
limit?: number;
|
||||
excludeUserId?: number;
|
||||
}) {
|
||||
|
||||
const eventTypeRepository = new EventTypeRepository(prisma);
|
||||
const { totalCount, items, hasMore, nextCursor } = await eventTypeRepository.listChildEventTypes({
|
||||
parentEventTypeId,
|
||||
@@ -80,7 +78,7 @@ export const getManagedEventUsersToReassign = async ({
|
||||
const eventTypeRepository = new EventTypeRepository(prisma);
|
||||
|
||||
const booking = await bookingRepository.findByIdForTargetEventTypeSearch(bookingId);
|
||||
|
||||
|
||||
if (!booking) {
|
||||
throw new Error(`Booking ${bookingId} not found`);
|
||||
}
|
||||
@@ -90,7 +88,7 @@ export const getManagedEventUsersToReassign = async ({
|
||||
}
|
||||
|
||||
const childEventType = await eventTypeRepository.findByIdWithParent(booking.eventTypeId);
|
||||
|
||||
|
||||
if (!childEventType) {
|
||||
throw new Error(`Event type ${booking.eventTypeId} not found`);
|
||||
}
|
||||
@@ -152,4 +150,3 @@ export const getManagedEventUsersToReassign = async ({
|
||||
};
|
||||
|
||||
export default getManagedEventUsersToReassign;
|
||||
|
||||
|
||||
-1
@@ -10,4 +10,3 @@ export const ZGetManagedEventUsersToReassignInputSchema = z.object({
|
||||
export type TGetManagedEventUsersToReassignInputSchema = z.infer<
|
||||
typeof ZGetManagedEventUsersToReassignInputSchema
|
||||
>;
|
||||
|
||||
|
||||
-1
@@ -42,4 +42,3 @@ export const managedEventManualReassignHandler = async ({
|
||||
};
|
||||
|
||||
export default managedEventManualReassignHandler;
|
||||
|
||||
|
||||
-1
@@ -12,4 +12,3 @@ export const ZManagedEventManualReassignInputSchema: z.ZodType<TManagedEventManu
|
||||
teamMemberId: z.number().int(),
|
||||
reassignReason: z.string().optional(),
|
||||
});
|
||||
|
||||
|
||||
-1
@@ -42,4 +42,3 @@ export const managedEventReassignHandler = async ({ ctx, input }: ManagedEventRe
|
||||
};
|
||||
|
||||
export default managedEventReassignHandler;
|
||||
|
||||
|
||||
@@ -7,4 +7,3 @@ export type TManagedEventReassignInputSchema = {
|
||||
export const ZManagedEventReassignInputSchema: z.ZodType<TManagedEventReassignInputSchema> = z.object({
|
||||
bookingId: z.number().int(),
|
||||
});
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ export type TPublishInputSchema = {
|
||||
teamId: number;
|
||||
};
|
||||
|
||||
export const ZPublishInputSchema: z.ZodType<TPublishInputSchema, z.ZodTypeDef, TPublishInputRawSchema> = z.object({
|
||||
teamId: z.coerce.number(),
|
||||
});
|
||||
export const ZPublishInputSchema: z.ZodType<TPublishInputSchema, z.ZodTypeDef, TPublishInputRawSchema> =
|
||||
z.object({
|
||||
teamId: z.coerce.number(),
|
||||
});
|
||||
|
||||
@@ -130,9 +130,7 @@ describe("removeHostsFromEventTypesHandler", () => {
|
||||
});
|
||||
|
||||
// Mock that only userId 101 is a team member, 102 is not
|
||||
(MembershipRepository.findAcceptedMembershipsByUserIdsInTeam as any).mockResolvedValue([
|
||||
{ userId: 101 },
|
||||
]);
|
||||
(MembershipRepository.findAcceptedMembershipsByUserIdsInTeam as any).mockResolvedValue([{ userId: 101 }]);
|
||||
|
||||
const mockDeleteResult = { count: 1 };
|
||||
(prisma.host.deleteMany as any).mockResolvedValue(mockDeleteResult);
|
||||
@@ -284,9 +282,7 @@ describe("removeHostsFromEventTypesHandler", () => {
|
||||
});
|
||||
|
||||
// User 999 is a valid team member
|
||||
(MembershipRepository.findAcceptedMembershipsByUserIdsInTeam as any).mockResolvedValue([
|
||||
{ userId: 999 },
|
||||
]);
|
||||
(MembershipRepository.findAcceptedMembershipsByUserIdsInTeam as any).mockResolvedValue([{ userId: 999 }]);
|
||||
|
||||
// But they're not a host on any of the event types
|
||||
const mockDeleteResult = { count: 0 };
|
||||
|
||||
@@ -14,7 +14,11 @@ export type TRemoveMemberInputSchema = {
|
||||
isOrg: boolean;
|
||||
};
|
||||
|
||||
export const ZRemoveMemberInputSchema: z.ZodType<TRemoveMemberInputSchema, z.ZodTypeDef, TRemoveMemberInputSchemaInput> = z.object({
|
||||
export const ZRemoveMemberInputSchema: z.ZodType<
|
||||
TRemoveMemberInputSchema,
|
||||
z.ZodTypeDef,
|
||||
TRemoveMemberInputSchemaInput
|
||||
> = z.object({
|
||||
teamIds: z.array(z.number()),
|
||||
memberIds: z.array(z.number()),
|
||||
isOrg: z.boolean().default(false),
|
||||
|
||||
+3
-1
@@ -16,7 +16,9 @@ export class RemoveMemberServiceFactory {
|
||||
const isPBACEnabled = await featuresRepository.checkIfTeamHasFeature(teamId, "pbac");
|
||||
|
||||
const teamRepository = new TeamRepository(prisma);
|
||||
const service = isPBACEnabled ? new PBACRemoveMemberService() : new LegacyRemoveMemberService(teamRepository);
|
||||
const service = isPBACEnabled
|
||||
? new PBACRemoveMemberService()
|
||||
: new LegacyRemoveMemberService(teamRepository);
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
+3
-1
@@ -36,7 +36,9 @@ describe("PBACRemoveMemberService", () => {
|
||||
getTeamIdsWithPermission: vi.fn(),
|
||||
};
|
||||
|
||||
vi.mocked(PermissionCheckService).mockImplementation(function() { return mockPermissionCheckService as any; });
|
||||
vi.mocked(PermissionCheckService).mockImplementation(function () {
|
||||
return mockPermissionCheckService as any;
|
||||
});
|
||||
|
||||
service = new PBACRemoveMemberService();
|
||||
});
|
||||
|
||||
+3
-1
@@ -22,7 +22,9 @@ describe("RemoveMemberServiceFactory", () => {
|
||||
checkIfTeamHasFeature: vi.fn(),
|
||||
};
|
||||
|
||||
vi.mocked(FeaturesRepository).mockImplementation(function() { return mockFeaturesRepository as any; });
|
||||
vi.mocked(FeaturesRepository).mockImplementation(function () {
|
||||
return mockFeaturesRepository as any;
|
||||
});
|
||||
});
|
||||
|
||||
describe("Service Creation", () => {
|
||||
|
||||
@@ -16,7 +16,11 @@ export type TResendInvitationInputSchema = {
|
||||
isOrg: boolean;
|
||||
};
|
||||
|
||||
export const ZResendInvitationInputSchema: z.ZodType<TResendInvitationInputSchema, z.ZodTypeDef, TResendInvitationInputSchemaInput> = z.object({
|
||||
export const ZResendInvitationInputSchema: z.ZodType<
|
||||
TResendInvitationInputSchema,
|
||||
z.ZodTypeDef,
|
||||
TResendInvitationInputSchemaInput
|
||||
> = z.object({
|
||||
teamId: z.number(),
|
||||
email: emailSchema,
|
||||
language: z.string(),
|
||||
|
||||
+7
-5
@@ -6,8 +6,10 @@ export type TRoundRobinManualReassignInputSchema = {
|
||||
reassignReason?: string;
|
||||
};
|
||||
|
||||
export const ZRoundRobinManualReassignInputSchema: z.ZodType<TRoundRobinManualReassignInputSchema> = z.object({
|
||||
bookingId: z.number(),
|
||||
teamMemberId: z.number(),
|
||||
reassignReason: z.string().optional(),
|
||||
});
|
||||
export const ZRoundRobinManualReassignInputSchema: z.ZodType<TRoundRobinManualReassignInputSchema> = z.object(
|
||||
{
|
||||
bookingId: z.number(),
|
||||
teamMemberId: z.number(),
|
||||
reassignReason: z.string().optional(),
|
||||
}
|
||||
);
|
||||
|
||||
@@ -26,15 +26,19 @@ vi.mock("@calcom/prisma", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("@calcom/features/pbac/services/permission-check.service", () => ({
|
||||
PermissionCheckService: vi.fn().mockImplementation(function() { return {
|
||||
checkPermission: vi.fn(),
|
||||
}; }),
|
||||
PermissionCheckService: vi.fn().mockImplementation(function () {
|
||||
return {
|
||||
checkPermission: vi.fn(),
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@calcom/features/ee/teams/repositories/TeamRepository", () => ({
|
||||
TeamRepository: vi.fn().mockImplementation(function() { return {
|
||||
isSlugAvailableForUpdate: vi.fn().mockResolvedValue(true),
|
||||
}; }),
|
||||
TeamRepository: vi.fn().mockImplementation(function () {
|
||||
return {
|
||||
isSlugAvailableForUpdate: vi.fn().mockResolvedValue(true),
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@calcom/lib/server/avatar", () => ({
|
||||
|
||||
@@ -9,13 +9,14 @@ export type TUpdateInternalNotesPresetsInputSchema = {
|
||||
}[];
|
||||
};
|
||||
|
||||
export const ZUpdateInternalNotesPresetsInputSchema: z.ZodType<TUpdateInternalNotesPresetsInputSchema> = z.object({
|
||||
teamId: z.number(),
|
||||
presets: z.array(
|
||||
z.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
cancellationReason: z.string().optional(),
|
||||
})
|
||||
),
|
||||
});
|
||||
export const ZUpdateInternalNotesPresetsInputSchema: z.ZodType<TUpdateInternalNotesPresetsInputSchema> =
|
||||
z.object({
|
||||
teamId: z.number(),
|
||||
presets: z.array(
|
||||
z.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
cancellationReason: z.string().optional(),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
@@ -28,9 +28,11 @@ vi.mock("@calcom/prisma", () => ({
|
||||
const mockCheckPermission = vi.fn();
|
||||
|
||||
vi.mock("@calcom/features/pbac/services/permission-check.service", () => ({
|
||||
PermissionCheckService: vi.fn().mockImplementation(function() { return {
|
||||
checkPermission: mockCheckPermission,
|
||||
}; }),
|
||||
PermissionCheckService: vi.fn().mockImplementation(function () {
|
||||
return {
|
||||
checkPermission: mockCheckPermission,
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../../../procedures/authedProcedure", () => ({
|
||||
|
||||
@@ -298,4 +298,4 @@ describe("sendVerificationCodeHandler", () => {
|
||||
expect(sendVerificationCode).toHaveBeenCalledWith(mockInput.phoneNumber);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user