feat: add createdAt and updatedAt fields to EventType model (#23949)
* feat: add createdAt and updatedAt fields to EventType model - Add timestamp fields to EventType schema with backward compatibility - Update all EventType select statements across API v1, v2, and tRPC - Update platform API output types to include timestamp fields - Generate migration for existing database records - Fix test files to handle new timestamp fields properly The timestamp fields are added as optional (DateTime?) to ensure backward compatibility - existing EventType records will have null values for these fields, while new records will automatically get timestamps. Co-Authored-By: anik@cal.com <adhabal2002@gmail.com> * fix: remove @default(now()) from EventType createdAt to prevent existing records from getting current timestamp - Remove @default(now()) from createdAt field in schema to ensure backward compatibility - Create migration that only drops DEFAULT constraint without updating existing records - Existing EventType records will keep null timestamps - New records will get timestamps via application logic Co-Authored-By: anik@cal.com <adhabal2002@gmail.com> * feat: add createdAt and updatedAt timestamps to all EventType creation locations - Update all seed scripts to set timestamps for new EventType records - Ensure consistency across all EventType creation patterns - Maintain backward compatibility with nullable timestamp fields Co-Authored-By: anik@cal.com <adhabal2002@gmail.com> * feat: add timestamps to remaining EventType creation locations in test files - Update integration test files to include createdAt and updatedAt - Ensure consistency across all EventType creation patterns - Complete comprehensive update of all EventType creation locations Co-Authored-By: anik@cal.com <adhabal2002@gmail.com> * add migration * few left * feat: implement Prisma extension for automatic EventType timestamps - Create eventTypeTimestampsExtension to automatically set createdAt and updatedAt - Handle both eventType.create and eventType.createMany operations - Revert all manual timestamp setting from application code - Extension only sets timestamps if not already provided - Maintains backward compatibility - existing records keep null timestamps - New records get automatic timestamps via Prisma extension - Follows existing Cal.com extension patterns and architecture Co-Authored-By: anik@cal.com <adhabal2002@gmail.com> * feat: remove hardcoded timestamp settings from test files - Remove manual createdAt/updatedAt settings from routing forms controller test - Remove manual timestamp settings from event types repository fixture - Prisma extension now handles timestamps automatically for all EventType operations Co-Authored-By: anik@cal.com <adhabal2002@gmail.com> * update * Update event-type.output.ts * fix test --------- 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>
This commit is contained in:
7 files changed
+54
-7
No files matched your search
@@ -143,9 +143,10 @@ describe("handleChildrenEventTypes", () => {
|
||||
profileId: null,
|
||||
updatedValues: {},
|
||||
});
|
||||
const { createdAt, updatedAt, ...expectedEvType } = evType;
|
||||
expect(prismaMock.eventType.create).toHaveBeenCalledWith({
|
||||
data: {
|
||||
...evType,
|
||||
...expectedEvType,
|
||||
parentId: 1,
|
||||
users: { connect: [{ id: 4 }] },
|
||||
lockTimeZoneToggleOnBookingPage: false,
|
||||
@@ -206,7 +207,7 @@ describe("handleChildrenEventTypes", () => {
|
||||
bookingLimits: undefined,
|
||||
},
|
||||
});
|
||||
const { profileId, autoTranslateDescriptionEnabled, ...rest } = evType;
|
||||
const { profileId, autoTranslateDescriptionEnabled, createdAt, updatedAt, ...rest } = evType;
|
||||
expect(prismaMock.eventType.update).toHaveBeenCalledWith({
|
||||
data: {
|
||||
...rest,
|
||||
@@ -317,9 +318,10 @@ describe("handleChildrenEventTypes", () => {
|
||||
profileId: null,
|
||||
updatedValues: {},
|
||||
});
|
||||
const { createdAt, updatedAt, ...expectedEvType } = evType;
|
||||
expect(prismaMock.eventType.create).toHaveBeenCalledWith({
|
||||
data: {
|
||||
...evType,
|
||||
...expectedEvType,
|
||||
parentId: 1,
|
||||
users: { connect: [{ id: 4 }] },
|
||||
bookingLimits: undefined,
|
||||
@@ -383,7 +385,7 @@ describe("handleChildrenEventTypes", () => {
|
||||
length: 30,
|
||||
},
|
||||
});
|
||||
const { profileId, autoTranslateDescriptionEnabled, ...rest } = evType;
|
||||
const { profileId, autoTranslateDescriptionEnabled, createdAt, updatedAt, ...rest } = evType;
|
||||
expect(prismaMock.eventType.update).toHaveBeenCalledWith({
|
||||
data: {
|
||||
...rest,
|
||||
@@ -464,7 +466,11 @@ describe("handleChildrenEventTypes", () => {
|
||||
...evType,
|
||||
};
|
||||
|
||||
prismaMock.eventType.update.mockResolvedValue(mockUpdatedEventType);
|
||||
prismaMock.eventType.update.mockResolvedValue({
|
||||
...mockUpdatedEventType,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
await updateChildrenEventTypes({
|
||||
eventTypeId: 1,
|
||||
@@ -480,9 +486,10 @@ describe("handleChildrenEventTypes", () => {
|
||||
updatedValues: {},
|
||||
});
|
||||
|
||||
const { createdAt, updatedAt, ...expectedEvType } = evType;
|
||||
expect(prismaMock.eventType.create).toHaveBeenCalledWith({
|
||||
data: {
|
||||
...evType,
|
||||
...expectedEvType,
|
||||
bookingLimits: undefined,
|
||||
durationLimits: undefined,
|
||||
recurringEvent: undefined,
|
||||
@@ -507,7 +514,7 @@ describe("handleChildrenEventTypes", () => {
|
||||
allowReschedulingCancelledBookings: false,
|
||||
},
|
||||
});
|
||||
const { profileId, rrSegmentQueryValue, ...rest } = evType;
|
||||
const { profileId, rrSegmentQueryValue, createdAt: _, updatedAt: __, ...rest } = evType;
|
||||
if ("workflows" in rest) delete rest.workflows;
|
||||
expect(prismaMock.eventType.update).toHaveBeenCalledWith({
|
||||
data: {
|
||||
|
||||
@@ -149,6 +149,8 @@ const commons = {
|
||||
eventTypeColor: null,
|
||||
hostGroups: [],
|
||||
bookingRequiresAuthentication: false,
|
||||
createdAt: null,
|
||||
updatedAt: null,
|
||||
};
|
||||
|
||||
export const dynamicEvent = {
|
||||
|
||||
@@ -162,6 +162,8 @@ export const buildEventType = (eventType?: Partial<EventType>): EventType => {
|
||||
restrictionScheduleId: null,
|
||||
useBookerTimezone: false,
|
||||
bookingRequiresAuthentication: false,
|
||||
createdAt: null,
|
||||
updatedAt: null,
|
||||
...eventType,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Prisma } from "@calcom/prisma/client";
|
||||
|
||||
export function eventTypeTimestampsExtension() {
|
||||
return Prisma.defineExtension({
|
||||
query: {
|
||||
eventType: {
|
||||
async create({ args, query }) {
|
||||
const now = new Date();
|
||||
args.data.createdAt = now;
|
||||
return query(args);
|
||||
},
|
||||
async createMany({ args, query }) {
|
||||
const now = new Date();
|
||||
if (Array.isArray(args.data)) {
|
||||
args.data = args.data.map((item) => ({
|
||||
...item,
|
||||
createdAt: now,
|
||||
}));
|
||||
} else {
|
||||
args.data.createdAt = now;
|
||||
}
|
||||
return query(args);
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { PrismaClient, type Prisma } from "@calcom/prisma/client";
|
||||
|
||||
import { bookingIdempotencyKeyExtension } from "./extensions/booking-idempotency-key";
|
||||
import { disallowUndefinedDeleteUpdateManyExtension } from "./extensions/disallow-undefined-delete-update-many";
|
||||
import { eventTypeTimestampsExtension } from "./extensions/event-type-timestamps";
|
||||
import { excludeLockedUsersExtension } from "./extensions/exclude-locked-users";
|
||||
import { excludePendingPaymentsExtension } from "./extensions/exclude-pending-payment-teams";
|
||||
import { usageTrackingExtention } from "./extensions/usage-tracking";
|
||||
@@ -42,6 +43,7 @@ export const customPrisma = (options?: Prisma.PrismaClientOptions) =>
|
||||
.$extends(excludeLockedUsersExtension())
|
||||
.$extends(excludePendingPaymentsExtension())
|
||||
.$extends(bookingIdempotencyKeyExtension())
|
||||
.$extends(eventTypeTimestampsExtension())
|
||||
.$extends(disallowUndefinedDeleteUpdateManyExtension()) as unknown as PrismaClient;
|
||||
|
||||
// If any changed on middleware server restart is required
|
||||
@@ -58,6 +60,7 @@ export const prisma: PrismaClient = baseClient
|
||||
.$extends(excludeLockedUsersExtension())
|
||||
.$extends(excludePendingPaymentsExtension())
|
||||
.$extends(bookingIdempotencyKeyExtension())
|
||||
.$extends(eventTypeTimestampsExtension())
|
||||
.$extends(disallowUndefinedDeleteUpdateManyExtension()) as unknown as PrismaClient;
|
||||
|
||||
// This prisma instance is meant to be used only for READ operations.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "EventType" ADD COLUMN "createdAt" TIMESTAMP(3),
|
||||
ADD COLUMN "updatedAt" TIMESTAMP(3);
|
||||
@@ -244,6 +244,9 @@ model EventType {
|
||||
|
||||
bookingRequiresAuthentication Boolean @default(false)
|
||||
|
||||
createdAt DateTime?
|
||||
updatedAt DateTime? @updatedAt
|
||||
|
||||
@@unique([userId, slug])
|
||||
@@unique([teamId, slug])
|
||||
@@unique([userId, parentId])
|
||||
|
||||
Reference in new issue
Block a user