* feat: extract core booking audit infrastructure from PR 25125 This PR contains only the core booking audit infrastructure changes from PR 25125, excluding integration changes with booking flows. Included: - All packages/features/booking-audit/* (core audit services, actions, repository) - packages/features/di/containers/BookingAuditViewerService.container.ts - packages/features/tasker/tasker.ts (audit task types) - packages/features/bookings/lib/types/actor.ts (actor types for audit) - packages/features/bookings/repositories/BookingRepository.ts (getFromRescheduleUid method) - apps/web/modules/booking/logs/views/booking-logs-view.tsx (UI for viewing audit logs) - apps/web/public/static/locales/en/common.json (translations) Excluded (integration changes): - packages/trpc/server/* (tRPC handlers) - packages/features/ee/round-robin/* (round-robin integration) - packages/features/bookings/lib/handleCancelBooking.ts - packages/features/bookings/lib/handleConfirmation.ts - packages/features/bookings/lib/onBookingEvents/BookingEventHandlerService.ts - packages/features/bookings/lib/service/RegularBookingService.ts - apps/api/v2/* (API v2 integration) Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fix: make booking audit interfaces backwards-compatible with main - Add queueAudit method back to BookingAuditProducerService interface for backwards compatibility - Implement queueAudit method in BookingAuditTaskerProducerService - Make userTimeZone parameter optional in BookingAuditViewerService - Add BookingAuditTaskProducerActionData type for legacy queueAudit method - Use any generics in BookingAuditActionServiceRegistry (matching PR 25125) - Fix type assertions in BookingAuditTaskConsumer Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fix switch eslint and ts * feat: enhance BookingAuditViewerService with logging and type improvements - Added ISimpleLogger dependency to BookingAuditViewerService for better error handling. - Updated actor type in enriched audit logs to use AuditActorType for improved type safety. - Replaced console.error with logger for error reporting when no rescheduled log is found. --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
114 lines
4.0 KiB
TypeScript
114 lines
4.0 KiB
TypeScript
import { z } from "zod";
|
|
|
|
import type { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
|
import { StringChangeSchema, NumberChangeSchema } from "../common/changeSchemas";
|
|
import { AuditActionServiceHelper } from "./AuditActionServiceHelper";
|
|
import type { IAuditActionService, TranslationWithParams } from "./IAuditActionService";
|
|
|
|
/**
|
|
* Reassignment Audit Action Service
|
|
* Handles REASSIGNMENT action with per-action versioning
|
|
*/
|
|
|
|
// Module-level because it is passed to IAuditActionService type outside the class scope
|
|
const fieldsSchemaV1 = z.object({
|
|
assignedToId: NumberChangeSchema,
|
|
assignedById: NumberChangeSchema,
|
|
reassignmentReason: StringChangeSchema,
|
|
reassignmentType: z.enum(["manual", "roundRobin"]),
|
|
userPrimaryEmail: StringChangeSchema.optional(),
|
|
title: StringChangeSchema.optional(),
|
|
});
|
|
|
|
export class ReassignmentAuditActionService
|
|
implements IAuditActionService<typeof fieldsSchemaV1, typeof fieldsSchemaV1> {
|
|
readonly VERSION = 1;
|
|
public static readonly TYPE = "REASSIGNMENT" as const;
|
|
private static dataSchemaV1 = z.object({
|
|
version: z.literal(1),
|
|
fields: fieldsSchemaV1,
|
|
});
|
|
private static fieldsSchemaV1 = fieldsSchemaV1;
|
|
public static readonly latestFieldsSchema = fieldsSchemaV1;
|
|
// Union of all versions
|
|
public static readonly storedDataSchema = ReassignmentAuditActionService.dataSchemaV1;
|
|
// Union of all versions
|
|
public static readonly storedFieldsSchema = ReassignmentAuditActionService.fieldsSchemaV1;
|
|
private helper: AuditActionServiceHelper<
|
|
typeof ReassignmentAuditActionService.latestFieldsSchema,
|
|
typeof ReassignmentAuditActionService.storedDataSchema
|
|
>;
|
|
|
|
constructor(private userRepository: UserRepository) {
|
|
this.helper = new AuditActionServiceHelper({
|
|
latestVersion: this.VERSION,
|
|
latestFieldsSchema: ReassignmentAuditActionService.latestFieldsSchema,
|
|
storedDataSchema: ReassignmentAuditActionService.storedDataSchema,
|
|
});
|
|
}
|
|
|
|
getVersionedData(fields: unknown) {
|
|
return this.helper.getVersionedData(fields);
|
|
}
|
|
|
|
parseStored(data: unknown) {
|
|
return this.helper.parseStored(data);
|
|
}
|
|
|
|
getVersion(data: unknown): number {
|
|
return this.helper.getVersion(data);
|
|
}
|
|
|
|
migrateToLatest(data: unknown) {
|
|
// V1-only: validate and return as-is (no migration needed)
|
|
const validated = fieldsSchemaV1.parse(data);
|
|
return { isMigrated: false, latestData: validated };
|
|
}
|
|
|
|
async getDisplayTitle({ storedData }: { storedData: { version: number; fields: z.infer<typeof fieldsSchemaV1> }; userTimeZone: string }): Promise<TranslationWithParams> {
|
|
const { fields } = storedData;
|
|
const user = await this.userRepository.findById({ id: fields.assignedToId.new });
|
|
const reassignedToName = user?.name || "Unknown";
|
|
return {
|
|
key: "booking_audit_action.booking_reassigned_to_host",
|
|
params: { host: reassignedToName },
|
|
};
|
|
}
|
|
|
|
getDisplayJson({
|
|
storedData,
|
|
}: {
|
|
storedData: { version: number; fields: z.infer<typeof fieldsSchemaV1> };
|
|
userTimeZone: string;
|
|
}): ReassignmentAuditDisplayData {
|
|
const { fields } = storedData;
|
|
return {
|
|
newAssignedToId: fields.assignedToId.new,
|
|
reassignmentReason: fields.reassignmentReason.new ?? null,
|
|
};
|
|
}
|
|
|
|
getDisplayFields(storedData: { version: number; fields: z.infer<typeof fieldsSchemaV1> }): Array<{
|
|
labelKey: string;
|
|
valueKey: string;
|
|
}> {
|
|
const { fields } = storedData;
|
|
|
|
const typeTranslationKey = `booking_audit_action.assignmentType_${fields.reassignmentType}`;
|
|
|
|
return [
|
|
{
|
|
labelKey: "booking_audit_action.type",
|
|
valueKey: typeTranslationKey,
|
|
}
|
|
];
|
|
}
|
|
}
|
|
|
|
export type ReassignmentAuditData = z.infer<typeof fieldsSchemaV1>;
|
|
|
|
export type ReassignmentAuditDisplayData = {
|
|
newAssignedToId: number;
|
|
reassignmentReason: string | null;
|
|
};
|