refactor(admin-panel): standardize FeatureFlagKey usage (#9548)

Replaced string-based feature flag keys with the typed FeatureFlagKey
enum across the admin panel module and related front-end hooks. This
ensures stronger type safety, reduces potential errors, and improves
consistency in handling feature flags.
This commit is contained in:
Antoine Moreaux
2025-01-10 18:38:50 +01:00
committed by GitHub
parent ed51bff2f4
commit b40f58a512
5 changed files with 20 additions and 13 deletions

View File

@ -4,6 +4,7 @@ import { isDefined } from 'twenty-ui';
import {
useUpdateWorkspaceFeatureFlagMutation,
useUserLookupAdminPanelMutation,
FeatureFlagKey,
} from '~/generated/graphql';
export const useFeatureFlagsManagement = () => {
@ -42,7 +43,7 @@ export const useFeatureFlagsManagement = () => {
const handleFeatureFlagUpdate = async (
workspaceId: string,
featureFlag: string,
featureFlag: FeatureFlagKey,
value: boolean,
) => {
setError(null);

View File

@ -1,4 +1,6 @@
import { FeatureFlagKey } from '~/generated/graphql';
export type FeatureFlag = {
key: string;
key: FeatureFlagKey;
value: boolean;
};

View File

@ -8,7 +8,6 @@ import { UpdateWorkspaceFeatureFlagInput } from 'src/engine/core-modules/admin-p
import { UserLookup } from 'src/engine/core-modules/admin-panel/dtos/user-lookup.entity';
import { UserLookupInput } from 'src/engine/core-modules/admin-panel/dtos/user-lookup.input';
import { AuthGraphqlApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { ImpersonateGuard } from 'src/engine/guards/impersonate-guard';
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
@ -41,7 +40,7 @@ export class AdminPanelResolver {
): Promise<boolean> {
await this.adminService.updateWorkspaceFeatureFlags(
updateFlagInput.workspaceId,
FeatureFlagKey[updateFlagInput.featureFlag],
updateFlagInput.featureFlag,
updateFlagInput.value,
);

View File

@ -118,7 +118,7 @@ export class AdminPanelService {
async updateWorkspaceFeatureFlags(
workspaceId: string,
featureFlag: string,
featureFlag: FeatureFlagKey,
value: boolean,
) {
featureFlagValidator.assertIsFeatureFlagKey(
@ -140,14 +140,14 @@ export class AdminPanelService {
);
const existingFlag = workspace.featureFlags?.find(
(flag) => flag.key === featureFlag,
(flag) => flag.key === FeatureFlagKey[featureFlag],
);
if (existingFlag) {
await this.featureFlagRepository.update(existingFlag.id, { value });
} else {
await this.featureFlagRepository.save({
key: featureFlag,
key: FeatureFlagKey[featureFlag],
value,
workspaceId: workspace.id,
});

View File

@ -12,6 +12,7 @@ import {
AuthException,
AuthExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
const UserFindOneMock = jest.fn();
const WorkspaceFindOneMock = jest.fn();
@ -74,9 +75,13 @@ describe('AdminPanelService', () => {
it('should update an existing feature flag if it exists', async () => {
const workspaceId = 'workspace-id';
const featureFlag = 'IsFlagEnabled';
const featureFlag = 'IsFlagEnabled' as FeatureFlagKey;
const value = true;
const existingFlag = { id: 'flag-id', key: featureFlag, value: false };
const existingFlag = {
id: 'flag-id',
key: 'IS_FLAG_ENABLED',
value: false,
};
WorkspaceFindOneMock.mockReturnValueOnce({
id: workspaceId,
@ -93,7 +98,7 @@ describe('AdminPanelService', () => {
it('should create a new feature flag if it does not exist', async () => {
const workspaceId = 'workspace-id';
const featureFlag = 'IsFlagEnabled';
const featureFlag = 'IsFlagEnabled' as FeatureFlagKey;
const value = true;
WorkspaceFindOneMock.mockReturnValueOnce({
@ -104,7 +109,7 @@ describe('AdminPanelService', () => {
await service.updateWorkspaceFeatureFlags(workspaceId, featureFlag, value);
expect(FeatureFlagSaveMock).toHaveBeenCalledWith({
key: featureFlag,
key: 'IS_FLAG_ENABLED',
value,
workspaceId,
});
@ -113,7 +118,7 @@ describe('AdminPanelService', () => {
it('should throw an exception if the workspace is not found', async () => {
const workspaceId = 'non-existent-workspace';
const featureFlag = 'IsFlagEnabled';
const featureFlag = 'IsFlagEnabled' as FeatureFlagKey;
const value = true;
WorkspaceFindOneMock.mockReturnValueOnce(null);
@ -127,7 +132,7 @@ describe('AdminPanelService', () => {
it('should throw an exception if the flag is not found', async () => {
const workspaceId = 'non-existent-workspace';
const featureFlag = 'IsUnknownFlagEnabled';
const featureFlag = 'IsUnknownFlagEnabled' as FeatureFlagKey;
const value = true;
WorkspaceFindOneMock.mockReturnValueOnce(null);