refactor: use dependency injection for InsightsBookingService (#22790)
* refactor: use dependency injection for InsightsBookingService - Add DI tokens for InsightsBookingService and module - Rename InsightsBookingService to InsightsBookingBaseService - Create InsightsBookingService DI interface with create method - Add DI module and container for InsightsBookingService - Update tRPC router to use getInsightsBookingService DI container - Update test file to use InsightsBookingBaseService - Update documentation to reflect new DI pattern Follows the same dependency injection pattern established for InsightsRoutingService in PR #22677 Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * refactor: restore createInsightsBookingService helper using DI internally - Keep createInsightsBookingService helper function for cleaner API - Use getInsightsBookingService DI container internally - Maintain same function signature and behavior - All existing calls continue to work unchanged Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * refactor: rename service files with proper capitalization - Rename insightsBookingBase.ts to InsightsBookingBaseService.ts - Rename insightsBookingDI.ts to InsightsBookingDIService.ts - Update all import statements to use new file names - Maintain existing handler functionality using createInsightsBookingService(ctx, input) Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * revert some changes * rename * update doc --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
eunjae@cal.com <hey@eunjae.dev>
Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent
2965450929
commit
cd576f5378
@@ -103,7 +103,7 @@ export default function InsightsPage() {
|
||||
|
||||
## Step 4: Create tRPC Handler
|
||||
|
||||
Add the tRPC endpoint in the insights router using the `createInsightsBookingService()` helper:
|
||||
Add the tRPC endpoint in the insights router using the `getInsightsBookingService()` DI container function:
|
||||
|
||||
```typescript
|
||||
// packages/features/insights/server/trpc-router.ts
|
||||
@@ -118,6 +118,7 @@ export const insightsRouter = router({
|
||||
myNewChartData: userBelongsToTeamProcedure
|
||||
.input(bookingRepositoryBaseInputSchema)
|
||||
.query(async ({ ctx, input }) => {
|
||||
// `createInsightsBookingService` is defined at the root level in this file
|
||||
const insightsBookingService = createInsightsBookingService(ctx, input);
|
||||
|
||||
try {
|
||||
@@ -129,13 +130,13 @@ export const insightsRouter = router({
|
||||
});
|
||||
```
|
||||
|
||||
## Step 5: Add Service Method to InsightsBookingService
|
||||
## Step 5: Add Service Method to InsightsBookingBaseService
|
||||
|
||||
Add your new method to the `InsightsBookingService` class:
|
||||
Add your new method to the `InsightsBookingBaseService` class:
|
||||
|
||||
```typescript
|
||||
// packages/lib/server/service/insightsBooking.ts
|
||||
export class InsightsBookingService {
|
||||
// packages/lib/server/service/InsightsBookingBaseService.ts
|
||||
export class InsightsBookingBaseService {
|
||||
// ... existing methods
|
||||
|
||||
async getMyNewChartData() {
|
||||
@@ -168,7 +169,7 @@ export class InsightsBookingService {
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `createInsightsBookingService()`**: Always use the helper function for consistent service creation
|
||||
1. **Use `getInsightsBookingService()`**: Always use the DI container function for consistent service creation
|
||||
2. **Raw SQL for Performance**: Use `$queryRaw` for complex aggregations and better performance
|
||||
3. **Base Conditions**: Always use `await this.getBaseConditions()` for proper filtering and permissions
|
||||
4. **Error Handling**: Wrap service calls in try-catch blocks with `TRPCError`
|
||||
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
routingRepositoryBaseInputSchema,
|
||||
bookingRepositoryBaseInputSchema,
|
||||
} from "@calcom/features/insights/server/raw-data.schema";
|
||||
import { getInsightsBookingService } from "@calcom/lib/di/containers/insights-booking";
|
||||
import { getInsightsRoutingService } from "@calcom/lib/di/containers/insights-routing";
|
||||
import { InsightsBookingService } from "@calcom/lib/server/service/insightsBooking";
|
||||
import type { readonlyPrisma } from "@calcom/prisma";
|
||||
import { BookingStatus } from "@calcom/prisma/enums";
|
||||
import authedProcedure from "@calcom/trpc/server/procedures/authedProcedure";
|
||||
@@ -316,14 +316,13 @@ const BATCH_SIZE = 1000; // Adjust based on your needs
|
||||
* Helper function to create InsightsBookingService with standardized parameters
|
||||
*/
|
||||
function createInsightsBookingService(
|
||||
ctx: { insightsDb: typeof readonlyPrisma; user: { id: number; organizationId: number | null } },
|
||||
ctx: { user: { id: number; organizationId: number | null } },
|
||||
input: z.infer<typeof bookingRepositoryBaseInputSchema>,
|
||||
dateTarget: "createdAt" | "startTime" = "createdAt"
|
||||
) {
|
||||
const { scope, selectedTeamId, startDate, endDate, columnFilters } = input;
|
||||
|
||||
return new InsightsBookingService({
|
||||
prisma: ctx.insightsDb,
|
||||
return getInsightsBookingService({
|
||||
options: {
|
||||
scope,
|
||||
userId: ctx.user.id,
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { createContainer } from "@evyweb/ioctopus";
|
||||
|
||||
import { DI_TOKENS } from "@calcom/lib/di/tokens";
|
||||
import type {
|
||||
InsightsBookingServicePublicOptions,
|
||||
InsightsBookingServiceFilterOptions,
|
||||
InsightsBookingBaseService,
|
||||
} from "@calcom/lib/server/service/InsightsBookingBaseService";
|
||||
import type { InsightsBookingService } from "@calcom/lib/server/service/InsightsBookingDIService";
|
||||
import { prismaModule } from "@calcom/prisma/prisma.module";
|
||||
|
||||
import { insightsBookingModule } from "../modules/insights-booking";
|
||||
|
||||
export function getInsightsBookingService({
|
||||
options,
|
||||
filters,
|
||||
}: {
|
||||
options: InsightsBookingServicePublicOptions;
|
||||
filters?: InsightsBookingServiceFilterOptions;
|
||||
}): InsightsBookingBaseService {
|
||||
const container = createContainer();
|
||||
container.load(DI_TOKENS.READ_ONLY_PRISMA_CLIENT, prismaModule);
|
||||
container.load(DI_TOKENS.INSIGHTS_BOOKING_SERVICE_MODULE, insightsBookingModule);
|
||||
|
||||
const diService = container.get<InsightsBookingService>(DI_TOKENS.INSIGHTS_BOOKING_SERVICE);
|
||||
return diService.create({ options, filters });
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { createModule } from "@evyweb/ioctopus";
|
||||
|
||||
import type { IInsightsBookingService } from "@calcom/lib/server/service/InsightsBookingDIService";
|
||||
import { InsightsBookingService } from "@calcom/lib/server/service/InsightsBookingDIService";
|
||||
|
||||
import { DI_TOKENS } from "../tokens";
|
||||
|
||||
export const insightsBookingModule = createModule();
|
||||
insightsBookingModule.bind(DI_TOKENS.INSIGHTS_BOOKING_SERVICE).toClass(InsightsBookingService, {
|
||||
prisma: DI_TOKENS.READ_ONLY_PRISMA_CLIENT,
|
||||
} satisfies Record<keyof IInsightsBookingService, symbol>);
|
||||
@@ -22,6 +22,8 @@ export const DI_TOKENS = {
|
||||
AVAILABLE_SLOTS_SERVICE_MODULE: Symbol("AvailableSlotsModule"),
|
||||
INSIGHTS_ROUTING_SERVICE: Symbol("InsightsRoutingService"),
|
||||
INSIGHTS_ROUTING_SERVICE_MODULE: Symbol("InsightsRoutingServiceModule"),
|
||||
INSIGHTS_BOOKING_SERVICE: Symbol("InsightsBookingService"),
|
||||
INSIGHTS_BOOKING_SERVICE_MODULE: Symbol("InsightsBookingServiceModule"),
|
||||
FEATURES_REPOSITORY: Symbol("FeaturesRepository"),
|
||||
FEATURES_REPOSITORY_MODULE: Symbol("FeaturesRepositoryModule"),
|
||||
CACHE_SERVICE: Symbol("CacheService"),
|
||||
|
||||
+1
-1
@@ -135,7 +135,7 @@ const NOTHING_CONDITION = Prisma.sql`1=0`;
|
||||
|
||||
const bookingDataKeys = new Set(Object.keys(bookingDataSchema.shape));
|
||||
|
||||
export class InsightsBookingService {
|
||||
export class InsightsBookingBaseService {
|
||||
private prisma: typeof readonlyPrisma;
|
||||
private options: InsightsBookingServiceOptions | null;
|
||||
private filters: InsightsBookingServiceFilterOptions | null;
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { readonlyPrisma } from "@calcom/prisma";
|
||||
|
||||
import {
|
||||
InsightsBookingBaseService,
|
||||
type InsightsBookingServicePublicOptions,
|
||||
type InsightsBookingServiceFilterOptions,
|
||||
} from "./InsightsBookingBaseService";
|
||||
|
||||
export interface IInsightsBookingService {
|
||||
prisma: typeof readonlyPrisma;
|
||||
}
|
||||
|
||||
export class InsightsBookingService {
|
||||
constructor(private readonly dependencies: IInsightsBookingService) {}
|
||||
|
||||
create({
|
||||
options,
|
||||
filters,
|
||||
}: {
|
||||
options: InsightsBookingServicePublicOptions;
|
||||
filters?: InsightsBookingServiceFilterOptions;
|
||||
}): InsightsBookingBaseService {
|
||||
return new InsightsBookingBaseService({
|
||||
prisma: this.dependencies.prisma,
|
||||
options,
|
||||
filters,
|
||||
});
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -6,7 +6,7 @@ import { ColumnFilterType } from "@calcom/features/data-table/lib/types";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { BookingStatus, MembershipRole } from "@calcom/prisma/enums";
|
||||
|
||||
import { InsightsBookingService } from "../../service/insightsBooking";
|
||||
import { InsightsBookingBaseService as InsightsBookingService } from "../InsightsBookingBaseService";
|
||||
|
||||
const NOTHING_CONDITION = Prisma.sql`1=0`;
|
||||
|
||||
Reference in New Issue
Block a user