feat: create BookingHistoryViewerService to combine audit logs with routing form submissions (#26045)
* feat: create BookingHistoryViewerService to combine audit logs with routing form submissions Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * refactor(booking): rename audit log query and enhance type safety - Updated the booking logs view to use the new getBookingHistory query instead of getAuditLogs. - Introduced DisplayBookingAuditLog type for improved clarity in BookingAuditViewerService. - Refactored BookingHistoryViewerService to utilize the new DisplayBookingAuditLog type and added sorting functionality for history logs. - Adjusted related types and methods to ensure consistency across services. * refactor(routing-forms): streamline imports and enhance type definitions - Consolidated type exports and imports from the features library for better organization. - Removed redundant type definitions and functions in zod.ts, findFieldValueByIdentifier.ts, getFieldIdentifier.ts, and parseRoutingFormResponse.ts. - Introduced new utility functions for handling field responses and parsing routing form responses. - Improved type safety and clarity across routing form response handling. * fix: remove double prefix from uniqueId in form submission entry Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * 1c97f9cc5d50416788c01876fe539bcc9750e9b2 (#26453) --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
a7fd7890a7
commit
9d39590510
24 files changed
+341
-111
No files matched your search
@@ -1,20 +1 @@
|
||||
import getFieldIdentifier from "./getFieldIdentifier";
|
||||
import type { RoutingFormResponseData } from "./responseData/types";
|
||||
|
||||
type FindFieldValueByIdentifierResult =
|
||||
| { success: true; data: string | string[] | number | null }
|
||||
| { success: false; error: string };
|
||||
|
||||
export function findFieldValueByIdentifier(
|
||||
data: RoutingFormResponseData,
|
||||
identifier: string
|
||||
): FindFieldValueByIdentifierResult {
|
||||
const field = data.fields.find((field) => getFieldIdentifier(field) === identifier);
|
||||
if (!field) {
|
||||
return { success: false, error: `Field with identifier ${identifier} not found` };
|
||||
}
|
||||
|
||||
const fieldValue = data.response[field.id]?.value;
|
||||
|
||||
return { success: true, data: fieldValue ?? null };
|
||||
}
|
||||
export { findFieldValueByIdentifier } from "@calcom/features/routing-forms/lib/findFieldValueByIdentifier";
|
||||
@@ -1,5 +1 @@
|
||||
import type { Field } from "../types/types";
|
||||
|
||||
const getFieldIdentifier = (field: Field) => field.identifier || field.label;
|
||||
|
||||
export default getFieldIdentifier;
|
||||
export { default } from "@calcom/features/routing-forms/lib/getFieldIdentifier";
|
||||
@@ -1,10 +1 @@
|
||||
import { zodNonRouterField } from "@calcom/app-store/routing-forms/zod";
|
||||
import { routingFormResponseInDbSchema } from "@calcom/app-store/routing-forms/zod";
|
||||
|
||||
import type { RoutingFormResponseData } from "./types";
|
||||
|
||||
export function parseRoutingFormResponse(rawResponse: unknown, formFields: unknown): RoutingFormResponseData {
|
||||
const response = routingFormResponseInDbSchema.parse(rawResponse);
|
||||
const fields = zodNonRouterField.array().parse(formFields);
|
||||
return { response, fields };
|
||||
}
|
||||
export { parseRoutingFormResponse } from "@calcom/features/routing-forms/lib/parseRoutingFormResponse";
|
||||
@@ -4,50 +4,15 @@ import { raqbQueryValueSchema } from "@calcom/lib/raqb/zod";
|
||||
|
||||
import { routingFormAppDataSchemas } from "./appDataSchemas";
|
||||
|
||||
export type FieldOption = {
|
||||
label: string;
|
||||
id: string | null;
|
||||
};
|
||||
export {
|
||||
zodNonRouterField,
|
||||
routingFormResponseInDbSchema,
|
||||
type FieldOption,
|
||||
type TNonRouterField,
|
||||
} from "@calcom/features/routing-forms/lib/zod";
|
||||
|
||||
export type TNonRouterField = {
|
||||
id: string;
|
||||
label: string;
|
||||
identifier?: string;
|
||||
placeholder?: string;
|
||||
type: string;
|
||||
/** @deprecated in favour of `options` */
|
||||
selectText?: string;
|
||||
required?: boolean;
|
||||
deleted?: boolean;
|
||||
options?: FieldOption[];
|
||||
};
|
||||
|
||||
// Note: zodNonRouterField is NOT annotated with z.ZodType because it uses .extend() below
|
||||
// which requires the full ZodObject type to be preserved
|
||||
export const zodNonRouterField = z.object({
|
||||
id: z.string(),
|
||||
label: z.string(),
|
||||
identifier: z.string().optional(),
|
||||
placeholder: z.string().optional(),
|
||||
type: z.string(),
|
||||
/**
|
||||
* @deprecated in favour of `options`
|
||||
*/
|
||||
selectText: z.string().optional(),
|
||||
required: z.boolean().optional(),
|
||||
deleted: z.boolean().optional(),
|
||||
options: z
|
||||
.array(
|
||||
z.object({
|
||||
label: z.string(),
|
||||
// To keep backwards compatibility with the options generated from legacy selectText, we allow saving null as id
|
||||
// It helps in differentiating whether the routing logic should consider the option.label as value or option.id as value.
|
||||
// This is important for legacy routes which has option.label saved in conditions and it must keep matching with the value of the option
|
||||
id: z.string().or(z.null()),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
});
|
||||
import type { TNonRouterField } from "@calcom/features/routing-forms/lib/zod";
|
||||
import { zodNonRouterField } from "@calcom/features/routing-forms/lib/zod";
|
||||
|
||||
export type TRouterField = TNonRouterField & {
|
||||
routerId: string;
|
||||
@@ -158,12 +123,3 @@ export const zodRoutesView = z.union([z.array(zodRouteView), z.null()]).optional
|
||||
export const appDataSchema = z.any();
|
||||
|
||||
export const appKeysSchema = z.object({});
|
||||
|
||||
// This is different from FormResponse in types.d.ts in that it has label optional. We don't seem to be using label at this point, so we might want to use this only while saving the response when Routing Form is submitted
|
||||
// Record key is formFieldId
|
||||
export const routingFormResponseInDbSchema = z.record(
|
||||
z.object({
|
||||
label: z.string().optional(),
|
||||
value: z.union([z.string(), z.number(), z.array(z.string())]),
|
||||
})
|
||||
);
|
||||
@@ -360,7 +360,7 @@ export function BookingHistory({ bookingUid }: BookingHistoryProps) {
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [actorFilter, setActorFilter] = useState<string | null>(null);
|
||||
const { t } = useLocale();
|
||||
const { data, isLoading, error } = trpc.viewer.bookings.getAuditLogs.useQuery({
|
||||
const { data, isLoading, error } = trpc.viewer.bookings.getBookingHistory.useQuery({
|
||||
bookingUid,
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { createContainer } from "@calcom/features/di/di";
|
||||
|
||||
import {
|
||||
type BookingHistoryViewerService,
|
||||
moduleLoader as bookingHistoryViewerServiceModule,
|
||||
} from "./BookingHistoryViewerService.module";
|
||||
|
||||
const container = createContainer();
|
||||
|
||||
export function getBookingHistoryViewerService() {
|
||||
bookingHistoryViewerServiceModule.loadModule(container);
|
||||
|
||||
return container.get<BookingHistoryViewerService>(bookingHistoryViewerServiceModule.token);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { BookingHistoryViewerService } from "@calcom/features/booking-audit/lib/service/BookingHistoryViewerService";
|
||||
import { BOOKING_AUDIT_DI_TOKENS } from "@calcom/features/booking-audit/di/tokens";
|
||||
import { moduleLoader as bookingAuditViewerServiceModuleLoader } from "@calcom/features/booking-audit/di/BookingAuditViewerService.module";
|
||||
import { moduleLoader as routingFormResponseRepositoryModuleLoader } from "@calcom/features/routing-forms/di/RoutingFormResponseRepository.module";
|
||||
|
||||
import { createModule, bindModuleToClassOnToken } from "../../di/di";
|
||||
|
||||
export const bookingHistoryViewerServiceModule = createModule();
|
||||
const token = BOOKING_AUDIT_DI_TOKENS.BOOKING_HISTORY_VIEWER_SERVICE;
|
||||
const moduleToken = BOOKING_AUDIT_DI_TOKENS.BOOKING_HISTORY_VIEWER_SERVICE_MODULE;
|
||||
|
||||
export { BookingHistoryViewerService };
|
||||
|
||||
const loadModule = bindModuleToClassOnToken({
|
||||
module: bookingHistoryViewerServiceModule,
|
||||
moduleToken,
|
||||
token,
|
||||
classs: BookingHistoryViewerService,
|
||||
depsMap: {
|
||||
bookingAuditViewerService: bookingAuditViewerServiceModuleLoader,
|
||||
routingFormResponseRepository: routingFormResponseRepositoryModuleLoader,
|
||||
},
|
||||
});
|
||||
|
||||
export const moduleLoader = {
|
||||
token,
|
||||
loadModule,
|
||||
};
|
||||
@@ -9,4 +9,6 @@ export const BOOKING_AUDIT_DI_TOKENS = {
|
||||
BOOKING_AUDIT_REPOSITORY_MODULE: Symbol("BookingAuditRepositoryModule"),
|
||||
AUDIT_ACTOR_REPOSITORY: Symbol("AuditActorRepository"),
|
||||
AUDIT_ACTOR_REPOSITORY_MODULE: Symbol("AuditActorRepositoryModule"),
|
||||
BOOKING_HISTORY_VIEWER_SERVICE: Symbol("BookingHistoryViewerService"),
|
||||
BOOKING_HISTORY_VIEWER_SERVICE_MODULE: Symbol("BookingHistoryViewerServiceModule"),
|
||||
};
|
||||
@@ -48,6 +48,8 @@ type EnrichedAuditLog = {
|
||||
};
|
||||
};
|
||||
|
||||
export type DisplayBookingAuditLog = EnrichedAuditLog;
|
||||
|
||||
/**
|
||||
* BookingAuditViewerService - Service for viewing and formatting booking audit logs
|
||||
*/
|
||||
@@ -93,7 +95,7 @@ export class BookingAuditViewerService {
|
||||
userEmail: string;
|
||||
userTimeZone: string;
|
||||
organizationId: number | null;
|
||||
}): Promise<{ bookingUid: string; auditLogs: EnrichedAuditLog[] }> {
|
||||
}): Promise<{ bookingUid: string; auditLogs: DisplayBookingAuditLog[] }> {
|
||||
const { bookingUid, userId, userTimeZone, organizationId } = params;
|
||||
await this.accessService.assertPermissions({
|
||||
bookingUid,
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import type { RoutingFormResponseRepositoryInterface } from "@calcom/lib/server/repository/RoutingFormResponseRepository.interface";
|
||||
|
||||
import type { BookingAuditViewerService, DisplayBookingAuditLog } from "./BookingAuditViewerService";
|
||||
import { getFieldResponseByIdentifier } from "@calcom/features/routing-forms/lib/getFieldResponseByIdentifier";
|
||||
|
||||
type GetHistoryForBookingParams = {
|
||||
bookingUid: string;
|
||||
userId: number;
|
||||
userEmail: string;
|
||||
userTimeZone: string;
|
||||
organizationId: number | null;
|
||||
};
|
||||
|
||||
type BookingHistoryLog = DisplayBookingAuditLog;
|
||||
|
||||
interface BookingHistoryViewerServiceDeps {
|
||||
bookingAuditViewerService: BookingAuditViewerService;
|
||||
routingFormResponseRepository: RoutingFormResponseRepositoryInterface;
|
||||
}
|
||||
|
||||
export class BookingHistoryViewerService {
|
||||
private readonly bookingAuditViewerService: BookingAuditViewerService;
|
||||
private readonly routingFormResponseRepository: RoutingFormResponseRepositoryInterface;
|
||||
|
||||
constructor(private readonly deps: BookingHistoryViewerServiceDeps) {
|
||||
this.bookingAuditViewerService = deps.bookingAuditViewerService;
|
||||
this.routingFormResponseRepository = deps.routingFormResponseRepository;
|
||||
}
|
||||
|
||||
private sortLogsReverseChronologically(historyLogs: BookingHistoryLog[]): BookingHistoryLog[] {
|
||||
return historyLogs.sort((a, b) => {
|
||||
const timestampA = new Date(a.timestamp).getTime();
|
||||
const timestampB = new Date(b.timestamp).getTime();
|
||||
return timestampB - timestampA;
|
||||
});
|
||||
}
|
||||
|
||||
private async getFormAuditLogsForBooking(bookingUid: string): Promise<BookingHistoryLog[]> {
|
||||
// TODO: Form doesn't have its Audit Logs yet, so we replicate them using the Form Response directly for now.
|
||||
const formResponse = await this.routingFormResponseRepository.findByBookingUidIncludeForm(bookingUid);
|
||||
if (!formResponse) {
|
||||
return [];
|
||||
}
|
||||
return [this.createFormSubmissionEntry({ formResponse, bookingUid })];
|
||||
}
|
||||
|
||||
async getHistoryForBooking(
|
||||
params: GetHistoryForBookingParams
|
||||
): Promise<{ bookingUid: string; auditLogs: BookingHistoryLog[] }> {
|
||||
const { bookingUid } = params;
|
||||
|
||||
const { auditLogs: bookingAuditLogs } = await this.bookingAuditViewerService.getAuditLogsForBooking(params);
|
||||
|
||||
const historyEntries: BookingHistoryLog[] = [...bookingAuditLogs, ...await this.getFormAuditLogsForBooking(bookingUid)];
|
||||
|
||||
const sortedLogs = this.sortLogsReverseChronologically(historyEntries);
|
||||
|
||||
return {
|
||||
bookingUid,
|
||||
auditLogs: sortedLogs,
|
||||
};
|
||||
}
|
||||
|
||||
private createFormSubmissionEntry({
|
||||
formResponse,
|
||||
bookingUid,
|
||||
}: {
|
||||
formResponse: NonNullable<
|
||||
Awaited<ReturnType<RoutingFormResponseRepositoryInterface["findByBookingUidIncludeForm"]>>
|
||||
>;
|
||||
bookingUid: string;
|
||||
}): BookingHistoryLog {
|
||||
const timestamp = formResponse.createdAt.toISOString();
|
||||
|
||||
const emailFieldResult = getFieldResponseByIdentifier({ responsePayload: formResponse.response, formFields: formResponse.form.fields, identifier: "email" });
|
||||
const emailFieldValueFromResponse = emailFieldResult.success ? emailFieldResult.data : null;
|
||||
// A valid string can be the email otherwise we assume it is not an email
|
||||
const submitterEmail = typeof emailFieldValueFromResponse === "string" ? emailFieldValueFromResponse : null;
|
||||
const uniqueId = `form-submission-${formResponse.id}`;
|
||||
return {
|
||||
id: uniqueId,
|
||||
bookingUid,
|
||||
type: "RECORD_CREATED",
|
||||
action: "CREATED",
|
||||
timestamp,
|
||||
createdAt: timestamp,
|
||||
source: "WEBAPP",
|
||||
operationId: uniqueId,
|
||||
displayJson: null,
|
||||
actionDisplayTitle: { key: "form_submitted" },
|
||||
displayFields: null,
|
||||
actor: {
|
||||
id: `form-submission-actor-${formResponse.id}`,
|
||||
type: "GUEST",
|
||||
userUuid: null,
|
||||
attendeeId: null,
|
||||
name: null,
|
||||
createdAt: formResponse.createdAt,
|
||||
displayName: submitterEmail ? `${submitterEmail}` : "Guest",
|
||||
displayEmail: submitterEmail || null,
|
||||
displayAvatar: null,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@
|
||||
"date-fns-tz": "3.2.0",
|
||||
"framer-motion": "10.12.8",
|
||||
"p-limit": "6.2.0",
|
||||
"react-awesome-query-builder": "5.1.2",
|
||||
"react-select": "5.8.0",
|
||||
"react-sticky-box": "2.0.4",
|
||||
"recharts": "3.0.2",
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { PrismaRoutingFormResponseRepository } from "@calcom/lib/server/repository/PrismaRoutingFormResponseRepository";
|
||||
import { ROUTING_FORM_DI_TOKENS } from "@calcom/features/routing-forms/di/tokens";
|
||||
import { bindModuleToClassOnToken } from "@calcom/features/di/di";
|
||||
import { moduleLoader as prismaModuleLoader } from "@calcom/features/di/modules/Prisma";
|
||||
import { createModule } from "../../di/di";
|
||||
|
||||
export const routingFormResponseRepositoryModule = createModule();
|
||||
const token = ROUTING_FORM_DI_TOKENS.ROUTING_FORM_RESPONSE_REPOSITORY;
|
||||
const moduleToken = ROUTING_FORM_DI_TOKENS.ROUTING_FORM_RESPONSE_REPOSITORY_MODULE;
|
||||
const loadModule = bindModuleToClassOnToken({
|
||||
module: routingFormResponseRepositoryModule,
|
||||
moduleToken,
|
||||
token,
|
||||
classs: PrismaRoutingFormResponseRepository,
|
||||
dep: prismaModuleLoader,
|
||||
});
|
||||
|
||||
export const moduleLoader = {
|
||||
token,
|
||||
loadModule,
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
export const ROUTING_FORM_DI_TOKENS = {
|
||||
ROUTING_FORM_RESPONSE_REPOSITORY: Symbol("RoutingFormResponseRepository"),
|
||||
ROUTING_FORM_RESPONSE_REPOSITORY_MODULE: Symbol("RoutingFormResponseRepositoryModule"),
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import getFieldIdentifier from "./getFieldIdentifier";
|
||||
import type { RoutingFormResponseData } from "./types";
|
||||
|
||||
type FindFieldValueByIdentifierResult =
|
||||
| { success: true; data: string | string[] | number | null }
|
||||
| { success: false; error: string };
|
||||
|
||||
export function findFieldValueByIdentifier(
|
||||
data: RoutingFormResponseData,
|
||||
identifier: string
|
||||
): FindFieldValueByIdentifierResult {
|
||||
const field = data.fields.find((field) => getFieldIdentifier(field) === identifier);
|
||||
if (!field) {
|
||||
return { success: false, error: `Field with identifier ${identifier} not found` };
|
||||
}
|
||||
|
||||
const fieldValue = data.response[field.id]?.value;
|
||||
|
||||
return { success: true, data: fieldValue ?? null };
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { Field } from "./types";
|
||||
|
||||
const getFieldIdentifier = (field: Field) => field.identifier || field.label;
|
||||
|
||||
export default getFieldIdentifier;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { parseRoutingFormResponse } from "./parseRoutingFormResponse";
|
||||
import { findFieldValueByIdentifier } from "./findFieldValueByIdentifier";
|
||||
|
||||
export const getFieldResponseByIdentifier = ({
|
||||
responsePayload,
|
||||
formFields,
|
||||
identifier,
|
||||
}: {
|
||||
responsePayload: unknown;
|
||||
formFields: unknown;
|
||||
identifier: string;
|
||||
}) => {
|
||||
const parsedResponse = parseRoutingFormResponse(responsePayload, formFields);
|
||||
const emailFieldResult = findFieldValueByIdentifier(parsedResponse, identifier);
|
||||
return emailFieldResult;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import { zodNonRouterField, routingFormResponseInDbSchema } from "./zod";
|
||||
import type { RoutingFormResponseData } from "./types";
|
||||
|
||||
export function parseRoutingFormResponse(rawResponse: unknown, formFields: unknown): RoutingFormResponseData {
|
||||
const response = routingFormResponseInDbSchema.parse(rawResponse);
|
||||
const fields = zodNonRouterField.array().parse(formFields);
|
||||
return { response, fields };
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import type z from "zod";
|
||||
|
||||
import type { zodNonRouterField, routingFormResponseInDbSchema } from "./zod";
|
||||
|
||||
export type FormResponse = Record<
|
||||
// Field ID
|
||||
string,
|
||||
{
|
||||
value: number | string | string[];
|
||||
label: string;
|
||||
identifier?: string;
|
||||
}
|
||||
>;
|
||||
|
||||
export type Field = z.infer<typeof zodNonRouterField>;
|
||||
export type Fields = Field[];
|
||||
|
||||
export type RoutingFormResponseData = {
|
||||
fields: z.infer<typeof zodNonRouterField>[];
|
||||
response: z.infer<typeof routingFormResponseInDbSchema>;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export type FieldOption = {
|
||||
label: string;
|
||||
id: string | null;
|
||||
};
|
||||
|
||||
export type TNonRouterField = {
|
||||
id: string;
|
||||
label: string;
|
||||
identifier?: string;
|
||||
placeholder?: string;
|
||||
type: string;
|
||||
/** @deprecated in favour of `options` */
|
||||
selectText?: string;
|
||||
required?: boolean;
|
||||
deleted?: boolean;
|
||||
options?: FieldOption[];
|
||||
};
|
||||
|
||||
// Note: zodNonRouterField is NOT annotated with z.ZodType because it uses .extend() below
|
||||
// which requires the full ZodObject type to be preserved
|
||||
export const zodNonRouterField = z.object({
|
||||
id: z.string(),
|
||||
label: z.string(),
|
||||
identifier: z.string().optional(),
|
||||
placeholder: z.string().optional(),
|
||||
type: z.string(),
|
||||
/**
|
||||
* @deprecated in favour of `options`
|
||||
*/
|
||||
selectText: z.string().optional(),
|
||||
required: z.boolean().optional(),
|
||||
deleted: z.boolean().optional(),
|
||||
options: z
|
||||
.array(
|
||||
z.object({
|
||||
label: z.string(),
|
||||
// To keep backwards compatibility with the options generated from legacy selectText, we allow saving null as id
|
||||
// It helps in differentiating whether the routing logic should consider the option.label as value or option.id as value.
|
||||
// This is important for legacy routes which has option.label saved in conditions and it must keep matching with the value of the option
|
||||
id: z.string().or(z.null()),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
});
|
||||
|
||||
// This is different from FormResponse in types.d.ts in that it has label optional. We don't seem to be using label at this point, so we might want to use this only while saving the response when Routing Form is submitted
|
||||
// Record key is formFieldId
|
||||
export const routingFormResponseInDbSchema = z.record(
|
||||
z.object({
|
||||
label: z.string().optional(),
|
||||
value: z.union([z.string(), z.number(), z.array(z.string())]),
|
||||
})
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ZConfirmInputSchema } from "./confirm.schema";
|
||||
import { ZEditLocationInputSchema } from "./editLocation.schema";
|
||||
import { ZFindInputSchema } from "./find.schema";
|
||||
import { ZGetInputSchema } from "./get.schema";
|
||||
import { ZGetAuditLogsInputSchema } from "./getAuditLogs.schema";
|
||||
import { ZGetBookingHistoryInputSchema } from "./getBookingHistory.schema";
|
||||
import { ZGetBookingAttendeesInputSchema } from "./getBookingAttendees.schema";
|
||||
import { ZGetBookingDetailsInputSchema } from "./getBookingDetails.schema";
|
||||
import { ZInstantBookingInputSchema } from "./getInstantBookingLocation.schema";
|
||||
@@ -108,10 +108,10 @@ export const bookingsRouter = router({
|
||||
input,
|
||||
});
|
||||
}),
|
||||
getAuditLogs: authedProcedure.input(ZGetAuditLogsInputSchema).query(async ({ input, ctx }) => {
|
||||
const { getAuditLogsHandler } = await import("./getAuditLogs.handler");
|
||||
getBookingHistory: authedProcedure.input(ZGetBookingHistoryInputSchema).query(async ({ input, ctx }) => {
|
||||
const { getBookingHistoryHandler } = await import("./getBookingHistory.handler");
|
||||
|
||||
return getAuditLogsHandler({
|
||||
return getBookingHistoryHandler({
|
||||
ctx,
|
||||
input,
|
||||
});
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export type TGetAuditLogsInputSchema = {
|
||||
bookingUid: string;
|
||||
};
|
||||
|
||||
export const ZGetAuditLogsInputSchema: z.ZodType<TGetAuditLogsInputSchema> = z.object({
|
||||
bookingUid: z.string(),
|
||||
});
|
||||
|
||||
+7
-8
@@ -3,19 +3,19 @@ import type { TFunction } from "i18next";
|
||||
import type { PrismaClient } from "@calcom/prisma/client";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
||||
import { getBookingAuditViewerService } from "@calcom/features/booking-audit/di/BookingAuditViewerService.container";
|
||||
import { getBookingHistoryViewerService } from "@calcom/features/booking-audit/di/BookingHistoryViewerService.container";
|
||||
import { BookingAuditErrorCode, BookingAuditPermissionError } from "@calcom/features/booking-audit/lib/service/BookingAuditAccessService";
|
||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||
|
||||
import type { TrpcSessionUser } from "../../../types";
|
||||
import type { TGetAuditLogsInputSchema } from "./getAuditLogs.schema";
|
||||
import type { TGetBookingHistoryInputSchema } from "./getBookingHistory.schema";
|
||||
|
||||
type GetAuditLogsOptions = {
|
||||
type GetBookingHistoryOptions = {
|
||||
ctx: {
|
||||
user: NonNullable<TrpcSessionUser>;
|
||||
prisma: PrismaClient;
|
||||
};
|
||||
input: TGetAuditLogsInputSchema;
|
||||
input: TGetBookingHistoryInputSchema;
|
||||
};
|
||||
|
||||
const getErrorMessage = (code: BookingAuditErrorCode, t: TFunction): string => {
|
||||
@@ -35,15 +35,15 @@ const getErrorMessage = (code: BookingAuditErrorCode, t: TFunction): string => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getAuditLogsHandler = async ({ ctx, input }: GetAuditLogsOptions) => {
|
||||
export const getBookingHistoryHandler = async ({ ctx, input }: GetBookingHistoryOptions) => {
|
||||
const { user } = ctx;
|
||||
const { bookingUid } = input;
|
||||
|
||||
const t = await getTranslation(user.locale ?? "en", "common");
|
||||
const bookingAuditViewerService = getBookingAuditViewerService();
|
||||
const bookingHistoryViewerService = getBookingHistoryViewerService();
|
||||
|
||||
try {
|
||||
const result = await bookingAuditViewerService.getAuditLogsForBooking({
|
||||
const result = await bookingHistoryViewerService.getHistoryForBooking({
|
||||
bookingUid,
|
||||
userId: user.id,
|
||||
userEmail: user.email,
|
||||
@@ -62,4 +62,3 @@ export const getAuditLogsHandler = async ({ ctx, input }: GetAuditLogsOptions) =
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export type TGetBookingHistoryInputSchema = {
|
||||
bookingUid: string;
|
||||
};
|
||||
|
||||
export const ZGetBookingHistoryInputSchema: z.ZodType<TGetBookingHistoryInputSchema> = z.object({
|
||||
bookingUid: z.string(),
|
||||
});
|
||||
Reference in new issue
Block a user