Files
Eunjae LeeandClaude Opus 4.5 512002aaef refactor: replace FeaturesRepository with DI-based feature repositories (#27200)
* refactor: replace FeaturesRepository with DI-based feature repositories

Migrate from the legacy FeaturesRepository pattern to separate DI-based repositories:
- Add checkIfFeatureIsEnabledGlobally to IFeatureRepository
- Add getTeamsWithFeatureEnabled to ITeamFeatureRepository
- Update CalendarSubscriptionService to use featureRepository, teamFeatureRepository, and userFeatureRepository
- Update SelectedCalendarRepository.findNextSubscriptionBatch to filter by teamIds instead of featureIds
- Update OnboardingPathService to use DI via getFeatureRepository()
- Remove prisma parameter from OnboardingPathService callsites

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: update tests to use new DI-based feature repositories

Update CalendarSubscriptionService and SelectedCalendarRepository tests
to use the new separate repository interfaces:
- featureRepository for global feature checks
- teamFeatureRepository for team-level feature checks
- userFeatureRepository for user-level feature checks

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: update API routes to use DI-based feature repositories

Update cron and webhook routes to use the new separate repository
interfaces instead of the combined FeaturesRepository.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: remove prisma arg from getGettingStartedPathWithParams call

The OnboardingPathService method no longer requires a prisma argument
as it now uses DI containers internally.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: update route tests to expect new DI-based feature repositories

Update service instantiation tests to expect featureRepository,
teamFeatureRepository, and userFeatureRepository instead of the
old featuresRepository.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: optimize global feature check and add guard in checkForNewSubscriptions

- Use targeted select for only `enabled` field in checkIfFeatureIsEnabledGlobally
- Add global feature flag guard in checkForNewSubscriptions to avoid unnecessary
  DB queries and API calls when the cache feature is globally disabled

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* remove redundant comment

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 09:34:03 +00:00

80 lines
2.1 KiB
TypeScript

import type { FeatureId } from "@calcom/features/flags/config";
import type { FeatureDto } from "@calcom/lib/dto/FeatureDto";
import type { PrismaClient } from "@calcom/prisma/client";
export interface IFeatureRepository {
findAll(): Promise<FeatureDto[]>;
findBySlug(slug: string): Promise<FeatureDto | null>;
update(input: { featureId: FeatureId; enabled: boolean; updatedBy?: number }): Promise<FeatureDto>;
checkIfFeatureIsEnabledGlobally(slug: string): Promise<boolean>;
}
export class PrismaFeatureRepository implements IFeatureRepository {
private prisma: PrismaClient;
constructor(prisma: PrismaClient) {
this.prisma = prisma;
}
async findAll(): Promise<FeatureDto[]> {
return this.prisma.feature.findMany({
orderBy: { slug: "asc" },
select: {
slug: true,
enabled: true,
description: true,
type: true,
stale: true,
lastUsedAt: true,
createdAt: true,
updatedAt: true,
updatedBy: true,
},
});
}
async findBySlug(slug: string): Promise<FeatureDto | null> {
return this.prisma.feature.findUnique({
where: { slug },
select: {
slug: true,
enabled: true,
description: true,
type: true,
stale: true,
lastUsedAt: true,
createdAt: true,
updatedAt: true,
updatedBy: true,
},
});
}
async update(input: { featureId: FeatureId; enabled: boolean; updatedBy?: number }): Promise<FeatureDto> {
const { featureId, enabled, updatedBy } = input;
return this.prisma.feature.update({
where: { slug: featureId },
data: { enabled, updatedBy, updatedAt: new Date() },
select: {
slug: true,
enabled: true,
description: true,
type: true,
stale: true,
lastUsedAt: true,
createdAt: true,
updatedAt: true,
updatedBy: true,
},
});
}
async checkIfFeatureIsEnabledGlobally(slug: string): Promise<boolean> {
const feature = await this.prisma.feature.findUnique({
where: { slug },
select: { enabled: true },
});
return Boolean(feature?.enabled);
}
}