* --init * -- * replace old structure with new DI * fix type * -- * moving stuff around * moving stuff around again * minor clean up * -- * resolve conflict * clea up * old schema clean up * further clean up * removing unwanted merged responsibilities * -- * improve DI and SOLID * some type fixes * --1 * clean up --cont * fix DI in facade for test * more fix * fix * checking * o.o * fix import * fix failing test --2 * fix failing test --3 * fix failing test --4 * normalization use * introduce facade container injection * uniform async telemetry spans * further improvements * replace prismock with repo mocks * ensure we don't pass prisma outside of repo * fix import * remove try catch from repo calls * async await fixes * using deps pattern * more clean up and fixes * more clean up * address feedback --1 * separation of concern * clean up * test clean up * feedback --2 * remove extra fetch * remove await * migrate _post test from prismock * fix type * -- * fix tokens path * rename AuditRepo * test --1 * update _post to integration test * fix test * fix test * test fix maybe? * -- * feedback * feedback * fixes * more feedback * NIT * use sentry and logger imports as planned * assertion in test * add missing test case * add tests for controllers and services * NITs * fix domain normalisation
88 lines
2.8 KiB
TypeScript
88 lines
2.8 KiB
TypeScript
import type logger from "@calcom/lib/logger";
|
|
import { WatchlistType } from "@calcom/prisma/enums";
|
|
|
|
import type {
|
|
IGlobalWatchlistRepository,
|
|
IOrganizationWatchlistRepository,
|
|
} from "../interface/IWatchlistRepositories";
|
|
import type { IWatchlistService } from "../interface/IWatchlistService";
|
|
import type { WatchlistEntry, CreateWatchlistEntryData, UpdateWatchlistEntryData } from "../types";
|
|
import { normalizeEmail, normalizeDomain } from "../utils/normalization";
|
|
|
|
type Deps = {
|
|
globalRepo: IGlobalWatchlistRepository;
|
|
orgRepo: IOrganizationWatchlistRepository;
|
|
logger: ReturnType<typeof logger.getSubLogger>;
|
|
};
|
|
|
|
export class WatchlistService implements IWatchlistService {
|
|
private readonly log: ReturnType<typeof logger.getSubLogger>;
|
|
|
|
constructor(private readonly deps: Deps) {
|
|
this.log = deps.logger;
|
|
}
|
|
|
|
async createEntry(data: CreateWatchlistEntryData): Promise<WatchlistEntry> {
|
|
const isGlobal = data.isGlobal ?? false;
|
|
|
|
// Normalize value based on type (service layer responsibility)
|
|
const normalizedValue =
|
|
data.type === WatchlistType.EMAIL ? normalizeEmail(data.value) : normalizeDomain(data.value);
|
|
|
|
const payload = {
|
|
type: data.type,
|
|
value: normalizedValue,
|
|
description: data.description,
|
|
action: data.action,
|
|
source: data.source,
|
|
};
|
|
|
|
// Global path
|
|
if (isGlobal) {
|
|
return this.deps.globalRepo.createEntry(payload);
|
|
}
|
|
|
|
// Org path (validate, then narrow)
|
|
const orgId = data.organizationId;
|
|
if (orgId == null) {
|
|
this.log.error("organizationId required for non-global entry", { type: data.type, value: data.value });
|
|
throw new Error("organizationId is required for organization-scoped entries");
|
|
}
|
|
|
|
return this.deps.orgRepo.createEntry(orgId, payload);
|
|
}
|
|
|
|
async updateEntry(id: string, data: UpdateWatchlistEntryData): Promise<WatchlistEntry> {
|
|
// Normalize value if provided (service layer responsibility)
|
|
const payload = {
|
|
...data,
|
|
...(data.value && {
|
|
value: data.type === WatchlistType.EMAIL ? normalizeEmail(data.value) : normalizeDomain(data.value),
|
|
}),
|
|
};
|
|
|
|
return this.deps.globalRepo.updateEntry(id, payload);
|
|
}
|
|
|
|
async deleteEntry(id: string): Promise<void> {
|
|
return this.deps.globalRepo.deleteEntry(id);
|
|
}
|
|
|
|
async getEntry(id: string): Promise<WatchlistEntry | null> {
|
|
return this.deps.globalRepo.findById(id);
|
|
}
|
|
|
|
/**
|
|
* System admin method to get ALL watchlist entries across the entire system
|
|
* Returns both global entries and all organization-specific entries from every organization
|
|
*/
|
|
async listAllSystemEntries(): Promise<WatchlistEntry[]> {
|
|
const [globalEntries, allOrgEntries] = await Promise.all([
|
|
this.deps.globalRepo.listBlockedEntries(),
|
|
this.deps.orgRepo.listAllOrganizationEntries(),
|
|
]);
|
|
|
|
return [...globalEntries, ...allOrgEntries];
|
|
}
|
|
}
|