-
Notifications
You must be signed in to change notification settings - Fork 10.2k
feat: implement comprehensive booking audit system #22854
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Add BookingAudit database model with proper indexing - Create Zod validation schemas for all booking audit actions - Implement BookingAuditService with logging methods for lifecycle events - Integrate audit logging into booking creation and cancellation flows - Add comprehensive unit tests with 100% coverage - Include actor metadata tracking and version compatibility - Ensure graceful error handling without breaking booking flows Features: - Tracks booking creation, cancellation, rescheduling, confirmation, rejection - Validates audit data with strict Zod schemas per action type - Captures actor information (user ID, email, name, role) - Supports optional metadata and version for forward compatibility - Provides audit log retrieval functionality Tests: 9/9 passing, verified working in real booking flows
@Vansh5632 is attempting to deploy a commit to the cal Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughAdds a booking audit capability. Introduces BookingAuditService with action-specific loggers (created, rescheduled, cancelled, confirmed, rejected, payment events, attendee changes), retrieval helpers, and actor serialization. Adds Zod-based booking audit schemas and related tests. Integrates audit logging into booking creation/reschedule (createBooking.ts) and cancellation flows (handleCancelBooking.ts); creation code paths updated to pass evt and eventType. Adds Prisma model BookingAudit and migrations creating the table and an index. Provides unit tests for the service and schema validation. Audit logging is best-effort with errors caught and not affecting main flows. Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Graphite Automations"Add consumer team as reviewer" took an action on this PR • (08/01/25)1 reviewer was added to this PR based on Keith Williams's automation. "Add community label" took an action on this PR • (08/01/25)1 label was added to this PR based on Keith Williams's automation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🧹 Nitpick comments (2)
packages/features/bookings/lib/audit/booking-audit-schemas.test.ts (1)
1-91
: Enhance test coverage for more comprehensive validation.The test structure is good, but consider expanding the test coverage:
describe("CANCELLED action schema", () => { it("should validate valid booking cancellation data", () => { // ... existing test }); + + it("should reject invalid booking cancellation data", () => { + const invalidData = { + bookingId: -1, // Invalid + action: BookingAuditAction.CANCELLED, + data: { + eventTypeId: "invalid", // Invalid type + startTime: "invalid-date", // Invalid + endTime: "invalid-date", // Invalid + cancellationReason: "", // Invalid if required + }, + }; + + const schema = getBookingAuditSchema(BookingAuditAction.CANCELLED); + const result = schema.safeParse(invalidData); + + expect(result.success).toBe(false); + }); }); describe("RESCHEDULED action schema", () => { it("should validate valid booking reschedule data", () => { // ... existing test }); + + it("should reject invalid booking reschedule data", () => { + const invalidData = { + bookingId: -1, // Invalid + action: BookingAuditAction.RESCHEDULED, + data: { + eventTypeId: "invalid", // Invalid type + startTime: "invalid-date", // Invalid + endTime: "invalid-date", // Invalid + oldStartTime: "invalid-date", // Invalid + oldEndTime: "invalid-date", // Invalid + organizerChanged: "no", // Invalid type + }, + }; + + const schema = getBookingAuditSchema(BookingAuditAction.RESCHEDULED); + const result = schema.safeParse(invalidData); + + expect(result.success).toBe(false); + }); }); + + describe("Edge cases", () => { + it("should handle unknown action gracefully", () => { + expect(() => getBookingAuditSchema("UNKNOWN_ACTION" as any)).toThrow(); + }); + + it("should validate with minimal required fields", () => { + const minimalData = { + bookingId: 1, + action: BookingAuditAction.CREATED, + data: { + eventTypeId: 1, + eventTypeName: "Test", + organizerId: 123, + startTime: "2025-08-01T10:00:00.000Z", + endTime: "2025-08-01T11:00:00.000Z", + }, + }; + + const schema = getBookingAuditSchema(BookingAuditAction.CREATED); + const result = schema.safeParse(minimalData); + + expect(result.success).toBe(true); + }); + });packages/features/bookings/lib/handleCancelBooking.ts (1)
403-403
: Consider making actor role determination more flexible.Both audit logging calls hardcode the role as "organizer", but
cancelledBy
might represent different types of users (attendees, admins, etc.).Consider determining the role based on the context or making it configurable:
- actor: cancelledBy ? { userId: parseInt(cancelledBy), role: "organizer" } : undefined, + actor: cancelledBy && !isNaN(parseInt(cancelledBy)) ? { + userId: parseInt(cancelledBy), + role: userId === parseInt(cancelledBy) ? "organizer" : "attendee" + } : undefined,Also applies to: 455-455
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
packages/features/bookings/lib/audit/booking-audit-schemas.test.ts
(1 hunks)packages/features/bookings/lib/audit/booking-audit-schemas.ts
(1 hunks)packages/features/bookings/lib/audit/booking-audit-service.test.ts
(1 hunks)packages/features/bookings/lib/audit/booking-audit-service.ts
(1 hunks)packages/features/bookings/lib/handleCancelBooking.ts
(3 hunks)packages/features/bookings/lib/handleNewBooking/createBooking.ts
(4 hunks)packages/prisma/migrations/20250801050105_add_booking_audit_table/migration.sql
(1 hunks)packages/prisma/schema.prisma
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)
**/*.ts
: For Prisma queries, only select data you need; never useinclude
, always useselect
Ensure thecredential.key
field is never returned from tRPC endpoints or APIs
Files:
packages/features/bookings/lib/audit/booking-audit-schemas.test.ts
packages/features/bookings/lib/audit/booking-audit-service.test.ts
packages/features/bookings/lib/handleCancelBooking.ts
packages/features/bookings/lib/handleNewBooking/createBooking.ts
packages/features/bookings/lib/audit/booking-audit-schemas.ts
packages/features/bookings/lib/audit/booking-audit-service.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)
Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js
.utc()
in hot paths like loops
Files:
packages/features/bookings/lib/audit/booking-audit-schemas.test.ts
packages/features/bookings/lib/audit/booking-audit-service.test.ts
packages/features/bookings/lib/handleCancelBooking.ts
packages/features/bookings/lib/handleNewBooking/createBooking.ts
packages/features/bookings/lib/audit/booking-audit-schemas.ts
packages/features/bookings/lib/audit/booking-audit-service.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: eunjae-lee
PR: calcom/cal.com#22702
File: packages/lib/server/service/insightsBooking.ts:120-124
Timestamp: 2025-07-24T08:39:06.185Z
Learning: In the InsightsBookingService (packages/lib/server/service/insightsBooking.ts), the constructor stores null for invalid options or filters but this is handled safely through null checks in buildFilterConditions() and buildAuthorizationConditions() methods. The service uses defensive programming to return safe fallback conditions (null or NOTHING_CONDITION) rather than throwing errors on invalid inputs.
📚 Learning: in the insightsbookingservice (packages/lib/server/service/insightsbooking.ts), the constructor stor...
Learnt from: eunjae-lee
PR: calcom/cal.com#22702
File: packages/lib/server/service/insightsBooking.ts:120-124
Timestamp: 2025-07-24T08:39:06.185Z
Learning: In the InsightsBookingService (packages/lib/server/service/insightsBooking.ts), the constructor stores null for invalid options or filters but this is handled safely through null checks in buildFilterConditions() and buildAuthorizationConditions() methods. The service uses defensive programming to return safe fallback conditions (null or NOTHING_CONDITION) rather than throwing errors on invalid inputs.
Applied to files:
packages/features/bookings/lib/audit/booking-audit-service.test.ts
packages/features/bookings/lib/audit/booking-audit-service.ts
📚 Learning: the office365calendar webhook handler in packages/app-store/office365calendar/api/webhook.ts is spec...
Learnt from: vijayraghav-io
PR: calcom/cal.com#21072
File: packages/app-store/office365calendar/api/webhook.ts:120-123
Timestamp: 2025-07-18T17:57:16.395Z
Learning: The office365calendar webhook handler in packages/app-store/office365calendar/api/webhook.ts is specifically designed for Office365 calendar integration, not as a generic webhook handler. Therefore, it's safe to assume that fetchAvailabilityAndSetCache method will be implemented in the Office365CalendarService, making explicit validation checks unnecessary.
Applied to files:
packages/features/bookings/lib/handleCancelBooking.ts
📚 Learning: in the failedbookingsbyfield component (packages/features/insights/components/failedbookingsbyfield....
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.389Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
Applied to files:
packages/features/bookings/lib/handleNewBooking/createBooking.ts
🧬 Code Graph Analysis (2)
packages/features/bookings/lib/audit/booking-audit-service.test.ts (1)
packages/features/bookings/lib/audit/booking-audit-service.ts (1)
BookingAuditService
(16-399)
packages/features/bookings/lib/handleCancelBooking.ts (1)
packages/features/bookings/lib/audit/booking-audit-service.ts (1)
BookingAuditService
(16-399)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (27)
packages/features/bookings/lib/handleCancelBooking.ts (2)
40-40
: LGTM! Clean integration of audit service.The import addition is well-placed and follows the existing import organization.
397-411
: Excellent non-blocking audit integration.The audit logging implementation follows best practices:
- Wrapped in try-catch blocks to prevent disrupting the main booking flow
- Logs errors appropriately without throwing
- Called after the booking operations are completed
- Uses appropriate service methods with relevant booking data
Also applies to: 450-462
packages/features/bookings/lib/audit/booking-audit-service.test.ts (6)
1-34
: Excellent test setup with proper mocking.The test setup correctly mocks both Prisma client and logger dependencies, with proper cleanup in
beforeEach
. The use ofvi.clearAllMocks()
ensures test isolation.
37-67
: Comprehensive test coverage for booking creation audit.The test properly validates the audit logging for booking creation, checking that Prisma's create method is called with the expected data structure and booking action.
69-96
: Good coverage for booking cancellation audit.Test correctly verifies cancellation audit logging with appropriate mock data and assertions.
98-126
: Thorough testing of booking rescheduling audit.The test includes both original and new timestamps, which properly validates the rescheduling audit data structure.
128-157
: Well-structured audit log retrieval test.Test properly verifies both the returned data and the query parameters passed to Prisma's
findMany
method, including the correct ordering.
159-178
: Critical error handling test ensures robustness.This test is essential - it verifies that audit failures don't break the main booking flow by ensuring the service methods don't throw errors even when database operations fail.
packages/features/bookings/lib/handleNewBooking/createBooking.ts (4)
14-14
: Clean import addition.The BookingAuditService import is appropriately placed with other feature imports.
113-116
: Necessary function signature update for audit logging.The additional parameters (
organizerUser
,evt
,eventType
) are required for the audit logging functionality and are passed appropriately.
191-240
: Comprehensive and well-structured audit logging implementation.The audit logging implementation is excellent:
- Proper placement: Called after successful booking creation within the transaction
- Conditional logic: Correctly differentiates between new bookings and reschedules
- Rich data capture: Includes all relevant booking details for both scenarios
- Actor metadata: Properly constructs actor information from organizer user data
- Error handling: Wrapped in try-catch that doesn't disrupt the main booking flow
- Data mapping: Correctly maps booking and event type data to audit parameters
The reschedule audit includes both old and new timestamps, and the creation audit captures comprehensive booking metadata including payment requirements and recurring event details.
142-144
: Function signature appropriately updated.The
saveBooking
function signature correctly adds the required parameters for audit logging while maintaining backward compatibility through the internal function structure.packages/features/bookings/lib/audit/booking-audit-schemas.ts (7)
6-16
: Comprehensive and well-organized audit action enum.The
BookingAuditAction
enum covers all essential booking lifecycle events and is organized logically. The inclusion of payment and attendee management actions shows good forward-thinking for audit requirements.
21-27
: Well-designed base audit schema.The base schema captures essential common fields with appropriate validation:
- Datetime strings for consistent timestamp handling
- Optional location for flexibility
- Default attendee count with sensible fallback
- Required eventTypeId for proper categorization
32-39
: Comprehensive creation audit schema.The creation schema appropriately extends the base with creation-specific fields like
eventTypeName
,organizerId
, and booking configuration details. Good use of optional fields for non-universal properties.
44-50
: Thorough rescheduling audit schema.The reschedule schema correctly captures both old and new timestamps, essential for understanding the change. Optional fields for reason and organizer changes provide good audit trail details.
102-110
: Flexible and comprehensive actor metadata schema.The actor schema design is excellent:
- Covers various user identification methods (userId, email, name)
- Includes role-based categorization
- Captures technical details (userAgent, ipAddress) for security auditing
- Supports platform integration with
platformClientId
- All fields appropriately optional for flexibility
115-160
: Excellent schema factory pattern implementation.The
getBookingAuditSchema
function provides type-safe validation based on action type:
- Uses literal types for action validation
- Properly maps actions to their corresponding data schemas
- Groups related actions (payment, attendee) efficiently
- Includes fallback with passthrough for extensibility
- Default version and createdAt provide good defaults
165-181
: Complete type exports for TypeScript integration.The exported types provide excellent TypeScript integration, making the schemas usable throughout the codebase with proper type safety.
packages/features/bookings/lib/audit/booking-audit-service.ts (8)
20-64
: Excellent core audit creation method with robust error handling.The
create
method implementation is exemplary:
- Schema validation: Uses the appropriate schema based on action type
- Database persistence: Properly handles Prisma JSON input types
- Error handling: Catches and logs errors without throwing, preventing disruption to main booking flows
- Structured logging: Provides detailed success and error logs with relevant context
- Type safety: Properly casts data to
Prisma.InputJsonValue
This follows the non-blocking design pattern perfectly for audit systems.
69-106
: Comprehensive booking creation audit method.The
logBookingCreated
method properly handles:
- Flexible timestamp input (string or Date)
- Comprehensive booking metadata capture
- Proper actor serialization
- Sensible defaults (attendeeCount = 1)
111-142
: Thorough rescheduling audit implementation.The reschedule method captures both old and new booking details, essential for understanding the full context of the change. The timestamp handling and optional parameters provide good flexibility.
147-170
: Well-designed cancellation audit method.The cancellation method properly handles optional parameters since some booking details might not be available during cancellation scenarios.
315-335
: Robust audit log retrieval with proper error handling.The retrieval method follows the same error handling pattern as the creation method - logs errors and returns safe fallbacks (empty array) rather than throwing exceptions.
347-374
: Efficient multiple booking audit retrieval.The multiple booking query uses proper SQL optimization with
IN
clause and sensible ordering (by bookingId first, then by creation time descending). Good performance consideration.
379-398
: Safe JSON serialization utilities.Both
serializeActor
anddeserializeActor
methods include proper error handling with fallback behaviors, ensuring the audit system remains robust even with malformed data.
250-273
: Smart action determination for payment events.The payment method intelligently maps payment status to appropriate audit actions, showing good design consideration for the audit trail requirements.
packages/features/bookings/lib/audit/booking-audit-schemas.test.ts
Outdated
Show resolved
Hide resolved
packages/prisma/migrations/20250801050105_add_booking_audit_table/migration.sql
Show resolved
Hide resolved
- Add null checks before parseInt(cancelledBy) to prevent TypeScript errors - Fixes TS2345 errors where 'string | undefined' is not assignable to 'string' - Ensures userId is set to NaN when cancelledBy is undefined, maintaining existing logic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (1)
packages/features/bookings/lib/audit/bookingAudit.schemas.ts (1)
95-95
: Consider renaming the 'action' field to avoid confusionThe
attendeeAuditDataSchema
contains an 'action' field that could be confused with the top-level audit action. Consider renaming it to something more specific like 'attendeeAction' or 'operation'.- action: z.enum(["added", "removed"]), + attendeeAction: z.enum(["added", "removed"]),Note: This would require corresponding changes in the BookingAuditService.logAttendeeEvent method.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
packages/features/bookings/lib/audit/BookingAuditService.test.ts
(1 hunks)packages/features/bookings/lib/audit/BookingAuditService.ts
(1 hunks)packages/features/bookings/lib/audit/bookingAudit.schemas.test.ts
(1 hunks)packages/features/bookings/lib/audit/bookingAudit.schemas.ts
(1 hunks)packages/features/bookings/lib/handleCancelBooking.ts
(3 hunks)packages/features/bookings/lib/handleNewBooking/createBooking.ts
(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/features/bookings/lib/handleCancelBooking.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)
**/*.ts
: For Prisma queries, only select data you need; never useinclude
, always useselect
Ensure thecredential.key
field is never returned from tRPC endpoints or APIs
Files:
packages/features/bookings/lib/audit/BookingAuditService.test.ts
packages/features/bookings/lib/audit/bookingAudit.schemas.ts
packages/features/bookings/lib/audit/bookingAudit.schemas.test.ts
packages/features/bookings/lib/handleNewBooking/createBooking.ts
packages/features/bookings/lib/audit/BookingAuditService.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)
Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js
.utc()
in hot paths like loops
Files:
packages/features/bookings/lib/audit/BookingAuditService.test.ts
packages/features/bookings/lib/audit/bookingAudit.schemas.ts
packages/features/bookings/lib/audit/bookingAudit.schemas.test.ts
packages/features/bookings/lib/handleNewBooking/createBooking.ts
packages/features/bookings/lib/audit/BookingAuditService.ts
**/*Service.ts
📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)
Service files must include
Service
suffix, use PascalCase matching exported class, and avoid generic names (e.g.,MembershipService.ts
)
Files:
packages/features/bookings/lib/audit/BookingAuditService.ts
🧬 Code Graph Analysis (4)
packages/features/bookings/lib/audit/BookingAuditService.test.ts (1)
packages/features/bookings/lib/audit/BookingAuditService.ts (1)
BookingAuditService
(16-399)
packages/features/bookings/lib/audit/bookingAudit.schemas.test.ts (1)
packages/features/bookings/lib/audit/bookingAudit.schemas.ts (1)
getBookingAuditSchema
(115-160)
packages/features/bookings/lib/handleNewBooking/createBooking.ts (2)
packages/types/Calendar.d.ts (1)
CalendarEvent
(163-224)packages/features/bookings/lib/audit/BookingAuditService.ts (1)
BookingAuditService
(16-399)
packages/features/bookings/lib/audit/BookingAuditService.ts (1)
packages/features/bookings/lib/audit/bookingAudit.schemas.ts (3)
BookingAuditInput
(173-173)getBookingAuditSchema
(115-160)ActorMetadata
(174-174)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Install dependencies / Yarn install & cache
🔇 Additional comments (1)
packages/features/bookings/lib/handleNewBooking/createBooking.ts (1)
226-226
: Add null check for paymentAppData.priceThe expression
paymentAppData.price > 0
might throw ifprice
is undefined. Consider using a safer check.- paymentRequired: paymentAppData.price > 0, + paymentRequired: typeof paymentAppData.price === "number" && paymentAppData.price > 0,Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/features/bookings/lib/handleCancelBooking.ts
(5 hunks)packages/prisma/migrations/20250811202009_add_index_to_booking_audit/migration.sql
(1 hunks)packages/prisma/schema.prisma
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/features/bookings/lib/handleCancelBooking.ts
- packages/prisma/schema.prisma
🔇 Additional comments (1)
packages/prisma/migrations/20250811202009_add_index_to_booking_audit/migration.sql (1)
2-2
: LGTM: bookingId index is appropriate and complements FK lookupsThis index is a good baseline for frequent WHERE bookingId filters and joins.
@@ -0,0 +1,2 @@ | |||
-- CreateIndex | |||
CREATE INDEX "BookingAudit_bookingId_idx" ON "BookingAudit"("bookingId"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Optional: add composite index for common timeline queries
If your audit retrievals frequently do WHERE bookingId = ? ORDER BY createdAt DESC, a composite index (bookingId, createdAt DESC) will eliminate extra sorts and improve pagination.
Check for such usage to justify the composite index:
If confirmed, add this additional index in a follow-up migration:
-- Postgres: composite index optimizing WHERE bookingId + ORDER BY createdAt DESC
CREATE INDEX CONCURRENTLY IF NOT EXISTS "BookingAudit_bookingId_createdAt_desc_idx"
ON "BookingAudit"("bookingId", "createdAt" DESC);
🏁 Script executed:
#!/bin/bash
# Find BookingAudit queries that filter by bookingId and order by createdAt
rg -n -A3 -i 'BookingAudit|bookingAudit' | rg -n -i 'orderBy.*createdAt|ORDER BY[[:space:]]+"?createdAt' -A3 -B6
Length of output: 114
🏁 Script executed:
#!/bin/bash
set -eux
echo "1. Locate all Prisma BookingAudit queries (findMany/findFirst) with context:"
rg -n "prisma\.bookingAudit\.(findMany|findFirst)" -A5 -B5
echo "2. Look for WHERE bookingId + ORDER BY createdAt in the same snippet:"
rg -n -i "where.*bookingId.*orderBy.*createdAt" -A3 -B3 .
echo "3. Fallback: any ORDER BY createdAt usage (to catch raw SQL or other clients):"
rg -n "orderBy.*createdAt" -A3 -B3 .
Length of output: 2407
Add composite index to optimize BookingAudit pagination
We’ve confirmed that getBookingAuditLogs
uses
prisma.bookingAudit.findMany({
where: { bookingId },
orderBy: { createdAt: "desc" },
…
})
Adding a composite index on (bookingId, createdAt DESC)
will remove the need for an extra sort and speed up pagination.
• Create a new migration (e.g. packages/prisma/migrations/202508XX_add_composite_index_booking_audit/migration.sql
) with:
-- Postgres: composite index optimizing WHERE bookingId + ORDER BY createdAt DESC
CREATE INDEX CONCURRENTLY IF NOT EXISTS "BookingAudit_bookingId_createdAt_desc_idx"
ON "BookingAudit"("bookingId", "createdAt" DESC);
🤖 Prompt for AI Agents
In
packages/prisma/migrations/20250811202009_add_index_to_booking_audit/migration.sql
around line 2, the current migration only creates a single-column index on
bookingId; add a new migration file (e.g.
packages/prisma/migrations/202508XX_add_composite_index_booking_audit/migration.sql)
that creates a composite index on (bookingId, createdAt DESC) using CREATE INDEX
CONCURRENTLY IF NOT EXISTS "BookingAudit_bookingId_createdAt_desc_idx" ON
"BookingAudit"("bookingId", "createdAt" DESC) so the DB can use the index for
the WHERE bookingId + ORDER BY createdAt DESC pattern and speed up pagination,
then run/apply the migration.
💡 Verification agent
🧩 Analysis chain
Prefer CREATE INDEX CONCURRENTLY (Postgres) to avoid write locks on large tables
On production-sized tables, plain CREATE INDEX can block writes. If your provider is PostgreSQL, consider using CONCURRENTLY and IF NOT EXISTS. Prisma migrate will run the migration outside a transaction when it detects non-transactional statements like CONCURRENTLY.
Proposed change:
-CREATE INDEX "BookingAudit_bookingId_idx" ON "BookingAudit"("bookingId");
+-- Postgres only: create index without blocking writes
+CREATE INDEX CONCURRENTLY IF NOT EXISTS "BookingAudit_bookingId_idx" ON "BookingAudit"("bookingId");
Verify provider (should be "postgresql"):
🏁 Script executed:
#!/bin/bash
# Show Prisma provider for datasource `db`
fd -a schema.prisma | while read -r f; do
echo "==> $f"
awk '
/datasource[[:space:]]+db[[:space:]]*\{/ { in=1 }
in && /provider[[:space:]]*=/ { print; in=0 }
' "$f"
done
Length of output: 990
Let’s reliably detect each Prisma schema’s provider with a simpler approach:
🏁 Script executed:
#!/bin/bash
find . -type f -name 'schema.prisma' | while read f; do
echo "==> $f"
# Print the datasource block, then extract the provider line
sed -n '/^datasource/,/^}/p' "$f" | grep -E 'provider\s*=' || echo " ⚠️ no provider found"
done
Length of output: 519
Use CREATE INDEX CONCURRENTLY for PostgreSQL-only migrations
- Verified that
packages/prisma/schema.prisma
uses provider = "postgresql" (the example schema is SQLite). - Update the migration at
packages/prisma/migrations/20250811202009_add_index_to_booking_audit/migration.sql
:
--- a/packages/prisma/migrations/20250811202009_add_index_to_booking_audit/migration.sql
+++ b/packages/prisma/migrations/20250811202009_add_index_to_booking_audit/migration.sql
@@ -1,2 +1,4 @@
- CREATE INDEX "BookingAudit_bookingId_idx" ON "BookingAudit"("bookingId");
+ -- PostgreSQL only: avoid write locks on large tables
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS "BookingAudit_bookingId_idx"
+ ON "BookingAudit"("bookingId");
Prisma Migrate will detect the non‐transactional CONCURRENTLY
statement and run this migration outside a transaction.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
CREATE INDEX "BookingAudit_bookingId_idx" ON "BookingAudit"("bookingId"); | |
-- PostgreSQL only: avoid write locks on large tables | |
CREATE INDEX CONCURRENTLY IF NOT EXISTS "BookingAudit_bookingId_idx" | |
ON "BookingAudit"("bookingId"); |
🤖 Prompt for AI Agents
In
packages/prisma/migrations/20250811202009_add_index_to_booking_audit/migration.sql
around line 2, the migration creates a standard index which can lock large
tables; update the SQL to use PostgreSQL's non-blocking form by replacing the
statement with a CREATE INDEX CONCURRENTLY "BookingAudit_bookingId_idx" ON
"BookingAudit"("bookingId"); and save—note that Prisma Migrate will detect the
CONCURRENTLY statement and run this migration outside a transaction.
What does this PR do?
This PR implements a comprehensive BookingAudit system that automatically logs booking lifecycle events to provide audit trails and analytics for Cal.com bookings.
Key Features:
BookingAudit
database table to track all booking operationsBookingAuditService
for centralized audit loggingDatabase Changes:
BookingAudit
model with fields:id
,bookingId
,action
,actor
,version
,data
,createdAt
Integration Points:
createBooking.ts
for booking creation and reschedule eventshandleCancelBooking.ts
for cancellation eventsDatabase Schema Addition: