mirror of
https://github.com/hanzoai/calendar.git
synced 2026-09-24 22:42:24 +00:00
b74dd3227c
* Integrate creation/rescheduling booking audit * fix: add missing hostUserUuid to booking audit test data Co-Authored-By: [email protected] <[email protected]> * fix-ci * feat: enhance booking audit with seat reference - Added support for seat reference in booking audit actions. - Updated localization for booking creation to include seat information. - Modified relevant services to pass attendee seat ID during booking creation. * fix: update test data to match schema requirements - Add seatReferenceUid: null to default mock audit log data - Add seatReferenceUid: null to multiple audit logs test case - Convert SEAT_RESCHEDULED test data to use numeric timestamps instead of ISO strings Co-Authored-By: [email protected] <[email protected]> * Allow nullish seatReferenceUid * feat: enhance booking audit to support rescheduledBy information - Updated booking audit actions to include rescheduledBy details, allowing tracking of who rescheduled a booking. - Refactored related services to accommodate the new rescheduledBy parameter in booking events. - Adjusted type definitions and function signatures to reflect the changes in the booking audit context. * Avoid possible run time issue * Fix imoport path * fix failing test due to merge from main\ * Pass useruuid * Refactor getDisplayTitle method signatures to use _params for consistency across audit action services. Update IAuditActionService to include dbStore in GetDisplayTitleParams for improved data handling during title retrieval. * fix: extend bulk-fetching optimization to actor and impersonator enrichment - Collect actor userUuids (for USER type actors) in collectDataRequirements - Collect impersonator UUIDs from log.context.impersonatedBy - Update enrichActorInformation to use dbStore.getUserByUuid() for USER actors - Update enrichImpersonator to use dbStore.getUserByUuid() - Update tests to verify findByUuids is called with correct aggregated UUIDs Co-Authored-By: [email protected] <[email protected]> * feat: Add infrastructure for no-show audit integration - Add Prisma migrations for SYSTEM source and NO_SHOW_UPDATED audit action - Add NoShowUpdatedAuditActionService with array-based attendeesNoShow schema - Update BookingAuditActionServiceRegistry to include NO_SHOW_UPDATED - Update BookingAuditTaskConsumer and BookingAuditViewerService - Add AttendeeRepository methods for no-show queries - Update IAuditActionService interface with values array support - Update locales with no-show audit translation keys Co-Authored-By: [email protected] <[email protected]> * fix: Add NO_SHOW_UPDATED to BookingAuditAction and SYSTEM to ActionSource types Co-Authored-By: [email protected] <[email protected]> * fix: Remove HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED from BookingAuditAction type to match Prisma schema Co-Authored-By: [email protected] <[email protected]> * fix: Update BookingAuditActionSchema to use NO_SHOW_UPDATED instead of HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED Co-Authored-By: [email protected] <[email protected]> * refactor: Remove deprecated no-show audit services and unify to NoShowUpdatedAuditActionService - Delete HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService - Update BookingAuditProducerService.interface.ts to use queueNoShowUpdatedAudit - Update BookingAuditTaskerProducerService.ts to use queueNoShowUpdatedAudit - Update BookingEventHandlerService.ts to use onNoShowUpdated - Add integration tests for NoShowUpdatedAuditActionService Co-Authored-By: [email protected] <[email protected]> * fix: Add data migration step for deprecated no-show enum values Addresses Cubic AI review feedback (confidence 9/10): The migration now includes an UPDATE statement to convert existing records using the deprecated 'host_no_show_updated' or 'attendee_no_show_updated' enum values to the new unified 'no_show_updated' value before the type cast. This prevents migration failures if any existing data uses the old values. Co-Authored-By: unknown <> * fix: Use CASE expression in USING clause for enum migration Fixes PostgreSQL error 'unsafe use of new value of enum type' by avoiding the ADD VALUE statement and instead using a CASE expression in the ALTER TABLE USING clause to convert deprecated enum values (host_no_show_updated, attendee_no_show_updated) to the new unified value (no_show_updated) during the type conversion. Co-Authored-By: unknown <> * fix: Remove dbStore usage from audit action services after merge - Update CreatedAuditActionService to use userRepository.findByUuid directly - Remove unused dbStore parameter from RescheduledAuditActionService - Sync test file with prepare-for-no-show-audit branch Co-Authored-By: [email protected] <[email protected]> * fix: Restore dbStore bulk-fetching optimization pattern - Add dbStore parameter and getDataRequirements method to IAuditActionService interface - Update CreatedAuditActionService to use dbStore.getUserByUuid() for bulk-fetched data - Update RescheduledAuditActionService to accept dbStore parameter - Update BookingAuditViewerService to: - Collect data requirements from all audit logs - Bulk-fetch users with findByUuids() before enrichment - Pass dbStore to action services and enrichment methods - Update tests to verify findByUuids is called for bulk-fetching optimization Co-Authored-By: [email protected] <[email protected]> * fix: Remove duplicate fireBookingEvents method in RecurringBookingService Co-Authored-By: [email protected] <[email protected]> * revert: Remove formatting-only changes in audit action services Co-Authored-By: [email protected] <[email protected]> * feat: Make getDataRequirements required and fetch rescheduled logs early - Make getDataRequirements a required method in IAuditActionService interface - Add getDataRequirements to all action services that didn't have it - Fetch rescheduled logs early to include their data requirements in bulk fetch - Update ReassignmentAuditActionService to use dbStore pattern instead of repository - Update NoShowUpdatedAuditActionService to use dbStore pattern - Update tests to use new dbStore pattern - Handle invalid data gracefully in collectDataRequirements Co-Authored-By: [email protected] <[email protected]> * Remve unncessary variable * feat: Implement Actor Strategy pattern for bulk-fetching attendees and credentials - Add ActorStrategies.ts with strategy pattern for all 5 actor types - Extend EnrichmentDataStore with StoredAttendee and StoredCredential types - Add findByIds method to CredentialRepository for bulk-fetching - Update BookingAuditViewerService to use strategy pattern: - Replace switch statement in enrichActorInformation with strategy dispatch - Update collectDataRequirements to collect attendeeIds and credentialIds - Update buildEnrichmentDataStore to bulk-fetch in parallel - Update tests to use findByIds mock and reflect graceful handling of missing data Co-Authored-By: [email protected] <[email protected]> * fix: Restore error-throwing for actors with missing required IDs - USER actor now throws error when userUuid is missing - ATTENDEE actor now throws error when attendeeId is missing - Updated tests to expect hasError: true for fallback logs Co-Authored-By: [email protected] <[email protected]> * fix: Restore original test assertions, only update mocks for bulk methods Co-Authored-By: [email protected] <[email protected]> * test: Add contract verification tests for getDataRequirements Co-Authored-By: [email protected] <[email protected]> * refactor: Clean up imports and improve type definitions in BookingAuditTaskConsumer and BookingAuditViewerService - Consolidated import statements for better readability - Enhanced type definitions for clarity and consistency - Updated method signatures to use destructured parameters for improved maintainability Co-Authored-By: [email protected] <[email protected]> * test: Split contract verification tests into separate files per action service Co-Authored-By: [email protected] <[email protected]> * test: Rename contract test files to .test.ts and merge into existing test file Co-Authored-By: [email protected] <[email protected]> * feat: Add runtime validation to EnrichmentDataStore for undeclared data access Co-Authored-By: [email protected] <[email protected]> * refactor: Make declaredRequirements required and add ensureFetched helper Co-Authored-By: [email protected] <[email protected]> * refactor: Simplify EnrichmentDataStore with constructor + fetch() pattern - Remove unused userIds and bookingUids from DataRequirements - Remove unused getUserById and getBookingByUid methods - Refactor constructor to accept requirements and repositories - Add fetch() method that fetches data and populates maps - Use Map keys as source of truth for declared requirements - Update getters to throw when accessing undeclared data - Add comprehensive unit tests for EnrichmentDataStore - Update BookingAuditViewerService to use new API - Update contractVerification.ts to remove unused tracking Co-Authored-By: [email protected] <[email protected]> * refactor: Clean up imports and improve type definitions in BookingAuditTaskConsumer and BookingAuditViewerService - Consolidated import statements for better readability - Enhanced type definitions for clarity and consistency - Updated method signatures to use destructured parameters for improved maintainability Co-Authored-By: [email protected] <[email protected]> * fix: Include contextRequirements in mergeDataRequirements and add accessedData assertions - Fix bug where contextRequirements (impersonator UUIDs) were not merged - Add accessedData assertions to CreatedAuditActionService.test.ts - Add accessedData assertions to ReassignmentAuditActionService.test.ts Co-Authored-By: [email protected] <[email protected]> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
191 lines
6.2 KiB
TypeScript
191 lines
6.2 KiB
TypeScript
import type { IAttendeeRepository } from "@calcom/features/bookings/repositories/IAttendeeRepository";
|
|
import type { CredentialRepository } from "@calcom/features/credentials/repositories/CredentialRepository";
|
|
import type { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
|
|
|
/**
|
|
* Entity types stored in the enrichment data store
|
|
* These represent the minimal data needed for audit log enrichment
|
|
*/
|
|
|
|
export type StoredUser = {
|
|
id: number;
|
|
uuid: string;
|
|
name: string | null;
|
|
email: string;
|
|
avatarUrl: string | null;
|
|
};
|
|
|
|
export type StoredAttendee = {
|
|
id: number;
|
|
name: string;
|
|
email: string;
|
|
};
|
|
|
|
export type StoredCredential = {
|
|
id: number;
|
|
appId: string | null;
|
|
};
|
|
|
|
/**
|
|
* Data requirements that action services can declare
|
|
* Used to collect all identifiers needed before bulk fetching
|
|
*/
|
|
export type DataRequirements = {
|
|
userUuids?: string[];
|
|
attendeeIds?: number[];
|
|
credentialIds?: number[];
|
|
};
|
|
|
|
/**
|
|
* Mapper functions to ensure only defined properties are stored avoiding unnecessary memory usage
|
|
* These functions extract only the properties defined in StoredUser, StoredAttendee, and StoredCredential
|
|
* Accepts broader types that extend the stored types (allowing additional properties)
|
|
*/
|
|
function mapToStoredUser<T extends StoredUser>(user: T): StoredUser {
|
|
return {
|
|
id: user.id,
|
|
uuid: user.uuid,
|
|
name: user.name,
|
|
email: user.email,
|
|
avatarUrl: user.avatarUrl,
|
|
};
|
|
}
|
|
|
|
function mapToStoredAttendee<T extends StoredAttendee>(attendee: T): StoredAttendee {
|
|
return {
|
|
id: attendee.id,
|
|
name: attendee.name,
|
|
email: attendee.email,
|
|
};
|
|
}
|
|
|
|
function mapToStoredCredential<T extends StoredCredential>(credential: T): StoredCredential {
|
|
return {
|
|
id: credential.id,
|
|
appId: credential.appId,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Repository dependencies for fetching data
|
|
*/
|
|
export interface EnrichmentDataStoreRepositories {
|
|
userRepository: UserRepository;
|
|
attendeeRepository: IAttendeeRepository;
|
|
credentialRepository: CredentialRepository;
|
|
}
|
|
|
|
/**
|
|
* EnrichmentDataStore
|
|
*
|
|
* Holds pre-fetched data for audit log enrichment.
|
|
* Action services use this store instead of making individual DB queries,
|
|
* eliminating N+1 query problems.
|
|
*
|
|
* The store validates that accessed data was declared in requirements - throws an error
|
|
* if code tries to access data that wasn't declared (catches bugs at runtime).
|
|
*
|
|
* Usage:
|
|
* ```
|
|
* const store = new EnrichmentDataStore(requirements, repositories);
|
|
* await store.fetch();
|
|
* const user = store.getUserByUuid("uuid"); // throws if "uuid" wasn't declared
|
|
* ```
|
|
*/
|
|
export class EnrichmentDataStore {
|
|
private usersByUuid: Map<string, StoredUser | null> = new Map();
|
|
private attendeesById: Map<number, StoredAttendee | null> = new Map();
|
|
private credentialsById: Map<number, StoredCredential | null> = new Map();
|
|
|
|
constructor(
|
|
private requirements: DataRequirements,
|
|
private repositories: EnrichmentDataStoreRepositories
|
|
) {
|
|
// Pre-populate maps with null for all declared IDs
|
|
// This marks them as "declared" - the key exists, but data not yet fetched
|
|
for (const uuid of requirements.userUuids ?? []) {
|
|
this.usersByUuid.set(uuid, null);
|
|
}
|
|
for (const id of requirements.attendeeIds ?? []) {
|
|
this.attendeesById.set(id, null);
|
|
}
|
|
for (const id of requirements.credentialIds ?? []) {
|
|
this.credentialsById.set(id, null);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch all declared data from the database
|
|
* Must be called before using any getter methods
|
|
*/
|
|
async fetch(): Promise<void> {
|
|
const [users, attendees, credentials] = await Promise.all([
|
|
this.requirements.userUuids?.length
|
|
? this.repositories.userRepository.findByUuids({ uuids: this.requirements.userUuids })
|
|
: [],
|
|
this.requirements.attendeeIds?.length
|
|
? this.repositories.attendeeRepository.findByIds({ ids: this.requirements.attendeeIds })
|
|
: [],
|
|
this.requirements.credentialIds?.length
|
|
? this.repositories.credentialRepository.findByIds({ ids: this.requirements.credentialIds })
|
|
: [],
|
|
]);
|
|
|
|
// Overwrite nulls with actual data from DB, using mappers to ensure only defined properties are stored
|
|
for (const user of users) {
|
|
this.usersByUuid.set(user.uuid, mapToStoredUser(user));
|
|
}
|
|
for (const attendee of attendees) {
|
|
this.attendeesById.set(attendee.id, mapToStoredAttendee(attendee));
|
|
}
|
|
for (const credential of credentials) {
|
|
this.credentialsById.set(credential.id, mapToStoredCredential(credential));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get user by UUID
|
|
* Throws error if UUID was not declared in requirements (bug in getDataRequirements)
|
|
* Returns null if user was declared but doesn't exist in database
|
|
*/
|
|
getUserByUuid(uuid: string): StoredUser | null {
|
|
if (!this.usersByUuid.has(uuid)) {
|
|
throw new Error(
|
|
`EnrichmentDataStore: getUserByUuid("${uuid}") called but was not declared in getDataRequirements. ` +
|
|
`This is a bug - ensure the action service declares all required userUuids.`
|
|
);
|
|
}
|
|
return this.usersByUuid.get(uuid) ?? null;
|
|
}
|
|
|
|
/**
|
|
* Get attendee by ID
|
|
* Throws error if ID was not declared in requirements (bug in getDataRequirements)
|
|
* Returns null if attendee was declared but doesn't exist in database
|
|
*/
|
|
getAttendeeById(id: number): StoredAttendee | null {
|
|
if (!this.attendeesById.has(id)) {
|
|
throw new Error(
|
|
`EnrichmentDataStore: getAttendeeById(${id}) called but was not declared in getDataRequirements. ` +
|
|
`This is a bug - ensure the action service declares all required attendeeIds.`
|
|
);
|
|
}
|
|
return this.attendeesById.get(id) ?? null;
|
|
}
|
|
|
|
/**
|
|
* Get credential by ID
|
|
* Throws error if ID was not declared in requirements (bug in getDataRequirements)
|
|
* Returns null if credential was declared but doesn't exist in database
|
|
*/
|
|
getCredentialById(id: number): StoredCredential | null {
|
|
if (!this.credentialsById.has(id)) {
|
|
throw new Error(
|
|
`EnrichmentDataStore: getCredentialById(${id}) called but was not declared in getDataRequirements. ` +
|
|
`This is a bug - ensure the action service declares all required credentialIds.`
|
|
);
|
|
}
|
|
return this.credentialsById.get(id) ?? null;
|
|
}
|
|
}
|