* Integrate creation/rescheduling booking audit * fix: add missing hostUserUuid to booking audit test data Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * 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: hariom@cal.com <hariombalhara@gmail.com> * 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: hariom@cal.com <hariombalhara@gmail.com> * 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: hariom@cal.com <hariombalhara@gmail.com> * fix: Add NO_SHOW_UPDATED to BookingAuditAction and SYSTEM to ActionSource types Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fix: Remove HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED from BookingAuditAction type to match Prisma schema Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fix: Update BookingAuditActionSchema to use NO_SHOW_UPDATED instead of HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * 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: hariom@cal.com <hariombalhara@gmail.com> * 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: hariom@cal.com <hariombalhara@gmail.com> * 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: hariom@cal.com <hariombalhara@gmail.com> * fix: Remove duplicate fireBookingEvents method in RecurringBookingService Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * revert: Remove formatting-only changes in audit action services Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * 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: hariom@cal.com <hariombalhara@gmail.com> * 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: hariom@cal.com <hariombalhara@gmail.com> * 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: hariom@cal.com <hariombalhara@gmail.com> * fix: Restore original test assertions, only update mocks for bulk methods Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * test: Add contract verification tests for getDataRequirements Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * 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: hariom@cal.com <hariombalhara@gmail.com> * test: Split contract verification tests into separate files per action service Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * test: Rename contract test files to .test.ts and merge into existing test file Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * feat: Add runtime validation to EnrichmentDataStore for undeclared data access Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * refactor: Make declaredRequirements required and add ensureFetched helper Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * 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: hariom@cal.com <hariombalhara@gmail.com> * 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: hariom@cal.com <hariombalhara@gmail.com> * 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: hariom@cal.com <hariombalhara@gmail.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
165 lines
5.5 KiB
TypeScript
165 lines
5.5 KiB
TypeScript
import { z } from "zod";
|
|
import { StringChangeSchema } from "../common/changeSchemas";
|
|
import type { DataRequirements, EnrichmentDataStore } from "../service/EnrichmentDataStore";
|
|
import { AuditActionServiceHelper } from "./AuditActionServiceHelper";
|
|
import type {
|
|
BaseStoredAuditData,
|
|
GetDisplayFieldsParams,
|
|
GetDisplayJsonParams,
|
|
GetDisplayTitleParams,
|
|
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({
|
|
organizerUuid: StringChangeSchema.optional(),
|
|
hostAttendeeUpdated: z
|
|
.object({
|
|
id: z.number().optional(),
|
|
withUserUuid: StringChangeSchema.optional(),
|
|
})
|
|
.optional(),
|
|
reassignmentReason: z.string().nullable(),
|
|
reassignmentType: z.enum(["manual", "roundRobin"]),
|
|
});
|
|
|
|
export class ReassignmentAuditActionService implements IAuditActionService {
|
|
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() {
|
|
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 };
|
|
}
|
|
|
|
getDataRequirements(storedData: BaseStoredAuditData): DataRequirements {
|
|
const { fields } = this.parseStored(storedData);
|
|
const userUuids: string[] = [];
|
|
const { newHostUuid, previousHostUuid } = this.getHostUuids(fields);
|
|
if (newHostUuid) userUuids.push(newHostUuid);
|
|
if (previousHostUuid) userUuids.push(previousHostUuid);
|
|
return { userUuids };
|
|
}
|
|
|
|
private getHostUuids(fields: ReassignmentAuditData) {
|
|
const hasAttendeeUpdated = fields.hostAttendeeUpdated != null;
|
|
const newHostUuid = hasAttendeeUpdated
|
|
? fields.hostAttendeeUpdated?.withUserUuid?.new
|
|
: fields.organizerUuid?.new;
|
|
const previousHostUuid = hasAttendeeUpdated
|
|
? fields.hostAttendeeUpdated?.withUserUuid?.old
|
|
: fields.organizerUuid?.old;
|
|
return { newHostUuid, previousHostUuid };
|
|
}
|
|
|
|
async getDisplayTitle({ storedData, dbStore }: GetDisplayTitleParams): Promise<TranslationWithParams> {
|
|
const { fields } = this.parseStored(storedData);
|
|
const { newHostUuid } = this.getHostUuids(fields);
|
|
const newUser = newHostUuid ? dbStore.getUserByUuid(newHostUuid) : null;
|
|
const reassignedToName = newUser?.name || "Unknown";
|
|
return {
|
|
key: "booking_audit_action.booking_reassigned_to_host",
|
|
params: { host: reassignedToName },
|
|
};
|
|
}
|
|
|
|
getDisplayJson({ storedData }: GetDisplayJsonParams): ReassignmentAuditDisplayData {
|
|
const { fields } = this.parseStored(storedData);
|
|
return {
|
|
...(fields.organizerUuid
|
|
? {
|
|
previousOrganizerUuid: fields.organizerUuid.old,
|
|
newOrganizerUuid: fields.organizerUuid.new,
|
|
}
|
|
: {}),
|
|
|
|
...(fields.hostAttendeeUpdated
|
|
? {
|
|
hostAttendeeIdUpdated: fields.hostAttendeeUpdated.id,
|
|
hostAttendeeUserUuidNew: fields.hostAttendeeUpdated.withUserUuid?.new,
|
|
hostAttendeeUserUuidOld: fields.hostAttendeeUpdated.withUserUuid?.old,
|
|
}
|
|
: {}),
|
|
reassignmentReason: fields.reassignmentReason ?? null,
|
|
};
|
|
}
|
|
|
|
async getDisplayFields({ storedData, dbStore }: GetDisplayFieldsParams): Promise<
|
|
Array<{
|
|
labelKey: string;
|
|
valueKey: string;
|
|
}>
|
|
> {
|
|
const { fields } = this.parseStored(storedData);
|
|
const map = {
|
|
manual: "manual",
|
|
roundRobin: "round_robin",
|
|
};
|
|
const typeTranslationKey = `booking_audit_action.assignment_type_${map[fields.reassignmentType]}`;
|
|
const { previousHostUuid } = this.getHostUuids(fields);
|
|
const previousUser = previousHostUuid ? dbStore.getUserByUuid(previousHostUuid) : null;
|
|
return [
|
|
{
|
|
labelKey: "booking_audit_action.assignment_type",
|
|
valueKey: typeTranslationKey,
|
|
},
|
|
{
|
|
labelKey: "booking_audit_action.previous_assignee",
|
|
valueKey: previousUser?.name ?? "Unknown",
|
|
},
|
|
];
|
|
}
|
|
}
|
|
|
|
export type ReassignmentAuditData = z.infer<typeof fieldsSchemaV1>;
|
|
|
|
export type ReassignmentAuditDisplayData = {
|
|
previousOrganizerUuid?: string | null;
|
|
newOrganizerUuid?: string | null;
|
|
hostAttendeeIdUpdated?: number | null;
|
|
hostAttendeeUserUuidNew?: string | null;
|
|
hostAttendeeUserUuidOld?: string | null;
|
|
reassignmentReason: string | null;
|
|
};
|