Files
cal.diy/packages/features/watchlist/lib/service/WatchlistOperationsService.ts
T
a40c3eebd6 feat: group same booking reports together (#26529)
Co-authored-by: unknown <>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-23 16:31:56 +00:00

94 lines
2.7 KiB
TypeScript

import type { PrismaBookingReportRepository } from "@calcom/features/bookingReport/repositories/PrismaBookingReportRepository";
import type { WatchlistEntry } from "@calcom/features/watchlist/lib/repository/IWatchlistRepository";
import type { WatchlistRepository } from "@calcom/features/watchlist/lib/repository/WatchlistRepository";
import { domainRegex, emailRegex } from "@calcom/lib/emailSchema";
import { WatchlistAction, WatchlistType } from "@calcom/prisma/enums";
import { WatchlistErrors } from "../errors/WatchlistErrors";
export interface AddReportsToWatchlistResult {
success: boolean;
message: string;
addedCount: number;
skippedCount: number;
results: Array<{ reportId: string; watchlistId: string }>;
}
export interface CreateWatchlistEntryInput {
type: WatchlistType;
value: string;
description?: string;
userId: number;
}
export interface CreateWatchlistEntryResult {
success: boolean;
entry: WatchlistEntry;
}
export interface DeleteWatchlistEntryInput {
entryId: string;
userId: number;
}
export interface DeleteWatchlistEntryResult {
success: boolean;
message: string;
}
export interface WatchlistOperationsScope {
organizationId: number | null;
isGlobal: boolean;
}
type Deps = {
watchlistRepo: WatchlistRepository;
bookingReportRepo: PrismaBookingReportRepository;
};
export abstract class WatchlistOperationsService {
constructor(protected readonly deps: Deps) {}
protected abstract getScope(): WatchlistOperationsScope;
validateEmailOrDomain(type: WatchlistType, value: string): void {
if (type === WatchlistType.EMAIL && !emailRegex.test(value)) {
throw WatchlistErrors.invalidEmail("Invalid email address format");
}
if (type === WatchlistType.DOMAIN && !domainRegex.test(value)) {
throw WatchlistErrors.invalidDomain("Invalid domain format (e.g., example.com)");
}
}
async createWatchlistEntry(input: CreateWatchlistEntryInput): Promise<CreateWatchlistEntryResult> {
const scope = this.getScope();
this.validateEmailOrDomain(input.type, input.value);
const entry = await this.deps.watchlistRepo.createEntryIfNotExists({
type: input.type,
value: input.value.toLowerCase(),
organizationId: scope.organizationId,
action: WatchlistAction.BLOCK,
description: input.description,
userId: input.userId,
isGlobal: scope.isGlobal,
});
return {
success: true,
entry,
};
}
async deleteWatchlistEntry(input: DeleteWatchlistEntryInput): Promise<DeleteWatchlistEntryResult> {
await this.deps.watchlistRepo.deleteEntry(input.entryId, input.userId);
return {
success: true,
message: "Entry deleted successfully",
};
}
}