Skip to content

Commit 2218a45

Browse files
hariombalharadevin-ai-integration[bot]Udit-takkar
authored
feat: extract core booking audit infrastructure from PR 25125 (calcom#25729)
* 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>
1 parent 4e1d05a commit 2218a45

34 files changed

Lines changed: 1190 additions & 325 deletions

apps/api/v2/src/lib/services/booking-audit-producer.service.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { TaskerService } from "@/lib/services/tasker.service";
2+
import { Logger } from "@/lib/logger.bridge";
23
import { Injectable } from "@nestjs/common";
34

45
import { BookingAuditTaskerProducerService } from "@calcom/platform-libraries/bookings";
56

67
@Injectable()
78
export class BookingAuditProducerService extends BookingAuditTaskerProducerService {
8-
constructor(taskerService: TaskerService) {
9+
constructor(taskerService: TaskerService, bridgeLogger: Logger) {
910
super({
1011
tasker: taskerService.getTasker(),
12+
log: bridgeLogger,
1113
});
1214
}
1315
}

apps/web/modules/booking/logs/views/booking-logs-view.tsx

Lines changed: 170 additions & 97 deletions
Large diffs are not rendered by default.

apps/web/public/static/locales/en/common.json

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4114,8 +4114,25 @@
41144114
"saved": "Saved",
41154115
"booking_history": "Booking History",
41164116
"booking_history_description": "View the history of actions performed on this booking",
4117-
"audit_action": {
4118-
"created": "Created"
4117+
"booking_audit_action": {
4118+
"created": "Created",
4119+
"cancelled": "Cancelled",
4120+
"rescheduled": "Rescheduled {{oldDate}} -> <0>{{newDate}}</0>",
4121+
"rescheduled_from": "Rescheduled <0>{{oldDate}}</0> -> {{newDate}}",
4122+
"accepted": "Accepted",
4123+
"reschedule_requested": "Reschedule Requested",
4124+
"attendee_added": "Attendee Added",
4125+
"host_no_show_updated": "Host No-Show Updated",
4126+
"rejected": "Rejected",
4127+
"attendee_removed": "Attendee Removed",
4128+
"reassignment": "Reassignment",
4129+
"booking_reassigned_to_host": "Booking reassigned to {{host}}",
4130+
"location_changed": "Location Changed",
4131+
"location_changed_from_to": "Location changed from {{fromLocation}} to {{toLocation}}",
4132+
"attendee_no_show_updated": "Attendee No-Show Updated",
4133+
"type": "Assignment Type",
4134+
"assignmentType_manual": "Manual Assignment",
4135+
"assignmentType_roundRobin": "Round Robin Assignment"
41194136
},
41204137
"error_loading_booking_logs": "Error loading booking logs",
41214138
"no_audit_logs_found": "No audit logs found",

packages/features/booking-audit/di/BookingAuditTaskConsumer.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { BOOKING_AUDIT_DI_TOKENS } from "@calcom/features/booking-audit/di/token
33
import { moduleLoader as bookingAuditRepositoryModuleLoader } from "@calcom/features/booking-audit/di/BookingAuditRepository.module";
44
import { moduleLoader as auditActorRepositoryModuleLoader } from "@calcom/features/booking-audit/di/AuditActorRepository.module";
55
import { moduleLoader as featuresRepositoryModuleLoader } from "@calcom/features/di/modules/Features";
6+
import { moduleLoader as userRepositoryModuleLoader } from "@calcom/features/di/modules/User";
67

78
import { createModule, bindModuleToClassOnToken } from "../../di/di";
89

@@ -19,6 +20,7 @@ const loadModule = bindModuleToClassOnToken({
1920
bookingAuditRepository: bookingAuditRepositoryModuleLoader,
2021
auditActorRepository: auditActorRepositoryModuleLoader,
2122
featuresRepository: featuresRepositoryModuleLoader,
23+
userRepository: userRepositoryModuleLoader,
2224
},
2325
});
2426

packages/features/booking-audit/di/BookingAuditTaskerProducerService.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { BookingAuditTaskerProducerService } from "@calcom/features/booking-audit/lib/service/BookingAuditTaskerProducerService";
22
import { BOOKING_AUDIT_DI_TOKENS } from "@calcom/features/booking-audit/di/tokens";
33
import { moduleLoader as taskerModuleLoader } from "@calcom/features/di/shared/services/tasker.service";
4+
import { moduleLoader as loggerModuleLoader } from "@calcom/features/di/shared/services/logger.service";
45

56
import { createModule, bindModuleToClassOnToken } from "../../di/di";
67

@@ -15,6 +16,7 @@ const loadModule = bindModuleToClassOnToken({
1516
classs: BookingAuditTaskerProducerService,
1617
depsMap: {
1718
tasker: taskerModuleLoader,
19+
log: loggerModuleLoader,
1820
},
1921
});
2022

packages/features/booking-audit/di/BookingAuditViewerService.module.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { BookingAuditViewerService } from "@calcom/features/booking-audit/lib/se
22
import { BOOKING_AUDIT_DI_TOKENS } from "@calcom/features/booking-audit/di/tokens";
33
import { moduleLoader as bookingAuditRepositoryModuleLoader } from "@calcom/features/booking-audit/di/BookingAuditRepository.module";
44
import { moduleLoader as userRepositoryModuleLoader } from "@calcom/features/di/modules/User";
5+
import { moduleLoader as bookingRepositoryModuleLoader } from "@calcom/features/di/modules/Booking";
6+
import { moduleLoader as loggerModuleLoader } from "@calcom/features/di/shared/services/logger.service";
57

68
import { createModule, bindModuleToClassOnToken } from "../../di/di";
79

@@ -19,6 +21,8 @@ const loadModule = bindModuleToClassOnToken({
1921
depsMap: {
2022
bookingAuditRepository: bookingAuditRepositoryModuleLoader,
2123
userRepository: userRepositoryModuleLoader,
24+
bookingRepository: bookingRepositoryModuleLoader,
25+
log: loggerModuleLoader,
2226
},
2327
});
2428

packages/features/booking-audit/lib/actions/AcceptedAuditActionService.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import type { BookingStatus } from "@calcom/prisma/enums";
12
import { z } from "zod";
23

3-
import { StringChangeSchema } from "../common/changeSchemas";
4+
import { BookingStatusChangeSchema } from "../common/changeSchemas";
45
import { AuditActionServiceHelper } from "./AuditActionServiceHelper";
56
import type { IAuditActionService, TranslationWithParams } from "./IAuditActionService";
67

@@ -11,7 +12,7 @@ import type { IAuditActionService, TranslationWithParams } from "./IAuditActionS
1112

1213
// Module-level because it is passed to IAuditActionService type outside the class scope
1314
const fieldsSchemaV1 = z.object({
14-
status: StringChangeSchema,
15+
status: BookingStatusChangeSchema,
1516
});
1617

1718
export class AcceptedAuditActionService
@@ -59,11 +60,16 @@ export class AcceptedAuditActionService
5960
return { isMigrated: false, latestData: validated };
6061
}
6162

62-
async getDisplayTitle(): Promise<TranslationWithParams> {
63+
async getDisplayTitle(_: { storedData: { version: number; fields: z.infer<typeof fieldsSchemaV1> }; userTimeZone: string }): Promise<TranslationWithParams> {
6364
return { key: "booking_audit_action.accepted" };
6465
}
6566

66-
getDisplayJson(storedData: { version: number; fields: z.infer<typeof fieldsSchemaV1> }): AcceptedAuditDisplayData {
67+
getDisplayJson({
68+
storedData,
69+
}: {
70+
storedData: { version: number; fields: z.infer<typeof fieldsSchemaV1> };
71+
userTimeZone: string;
72+
}): AcceptedAuditDisplayData {
6773
const { fields } = storedData;
6874
return {
6975
previousStatus: fields.status.old ?? null,
@@ -75,6 +81,6 @@ export class AcceptedAuditActionService
7581
export type AcceptedAuditData = z.infer<typeof fieldsSchemaV1>;
7682

7783
export type AcceptedAuditDisplayData = {
78-
previousStatus: string | null;
79-
newStatus: string | null;
84+
previousStatus: BookingStatus | null;
85+
newStatus: BookingStatus | null;
8086
};

packages/features/booking-audit/lib/actions/AttendeeAddedAuditActionService.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,16 @@ export class AttendeeAddedAuditActionService
5959
return { isMigrated: false, latestData: validated };
6060
}
6161

62-
async getDisplayTitle(): Promise<TranslationWithParams> {
62+
async getDisplayTitle(_: { storedData: { version: number; fields: z.infer<typeof fieldsSchemaV1> }; userTimeZone: string }): Promise<TranslationWithParams> {
6363
return { key: "booking_audit_action.attendee_added" };
6464
}
6565

66-
getDisplayJson(storedData: { version: number; fields: z.infer<typeof fieldsSchemaV1> }): AttendeeAddedAuditDisplayData {
66+
getDisplayJson({
67+
storedData,
68+
}: {
69+
storedData: { version: number; fields: z.infer<typeof fieldsSchemaV1> };
70+
userTimeZone: string;
71+
}): AttendeeAddedAuditDisplayData {
6772
const { fields } = storedData;
6873
const previousAttendeesSet = new Set(fields.attendees.old ?? []);
6974
const addedAttendees = fields.attendees.new.filter(

packages/features/booking-audit/lib/actions/AttendeeNoShowUpdatedAuditActionService.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,16 @@ export class AttendeeNoShowUpdatedAuditActionService
5959
return { isMigrated: false, latestData: validated };
6060
}
6161

62-
async getDisplayTitle(): Promise<TranslationWithParams> {
62+
async getDisplayTitle(_: { storedData: { version: number; fields: z.infer<typeof fieldsSchemaV1> }; userTimeZone: string }): Promise<TranslationWithParams> {
6363
return { key: "booking_audit_action.attendee_no_show_updated" };
6464
}
6565

66-
getDisplayJson(storedData: { version: number; fields: z.infer<typeof fieldsSchemaV1> }): AttendeeNoShowUpdatedAuditDisplayData {
66+
getDisplayJson({
67+
storedData,
68+
}: {
69+
storedData: { version: number; fields: z.infer<typeof fieldsSchemaV1> };
70+
userTimeZone: string;
71+
}): AttendeeNoShowUpdatedAuditDisplayData {
6772
const { fields } = storedData;
6873
return {
6974
noShowAttendee: fields.noShowAttendee.new,

packages/features/booking-audit/lib/actions/AttendeeRemovedAuditActionService.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,18 @@ export class AttendeeRemovedAuditActionService
5959
return { isMigrated: false, latestData: validated };
6060
}
6161

62-
async getDisplayTitle(): Promise<TranslationWithParams> {
62+
async getDisplayTitle(_: { storedData: { version: number; fields: z.infer<typeof fieldsSchemaV1> }; userTimeZone: string }): Promise<TranslationWithParams> {
6363
return { key: "booking_audit_action.attendee_removed" };
6464
}
6565

66-
getDisplayJson(storedData: { version: number; fields: z.infer<typeof fieldsSchemaV1> }): AttendeeRemovedAuditDisplayData {
66+
getDisplayJson({
67+
storedData,
68+
}: {
69+
storedData: { version: number; fields: z.infer<typeof fieldsSchemaV1> };
70+
userTimeZone: string;
71+
}): AttendeeRemovedAuditDisplayData {
6772
const { fields } = storedData;
68-
// Note: fields.attendees stores the state change (old -> new), not the removed attendees directly
69-
// old = attendees before removal, new = remaining attendees after removal
7073
const remainingAttendeesSet = new Set(fields.attendees.new ?? []);
71-
// Compute removed attendees: those in old but not in new (remaining)
7274
const removedAttendees = (fields.attendees.old ?? []).filter(
7375
(email) => !remainingAttendeesSet.has(email)
7476
);

0 commit comments

Comments
 (0)