From edeaecad0517fbf96621dc4f725a037279ab877b Mon Sep 17 00:00:00 2001 From: nitin <142569587+ehconitin@users.noreply.github.com> Date: Tue, 4 Feb 2025 02:55:46 +0530 Subject: [PATCH] hotfix for lab (#9981) Issue: When attempting to toggle a public feature flag that didn't exist in the workspace's feature flags array, the LabService threw a FeatureFlagException with code FEATURE_FLAG_NOT_FOUND. This prevented users from enabling public feature flags for the first time in their workspace. Fix: Modified the LabService to handle non-existent public feature flags by creating them instead of throwing an error. When the flag doesn't exist, the service now saves a new feature flag record with the provided key and value, associates it with the workspace, and returns the newly created flag. --- .../twenty-front/src/generated/graphql.tsx | 10 ++++-- .../mutations/updateLabPublicFeatureFlag.ts | 6 +++- .../lab/hooks/useLabPublicFeatureFlags.ts | 36 ++++++++++++------- .../engine/core-modules/lab/lab.resolver.ts | 9 +++-- .../core-modules/lab/services/lab.service.ts | 23 ++++++++---- 5 files changed, 55 insertions(+), 29 deletions(-) diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 60947d6e7d44..9416282d18e8 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -800,7 +800,7 @@ export type Mutation = { skipSyncEmailOnboardingStep: OnboardingStepSuccess; track: Analytics; updateBillingSubscription: BillingUpdateOutput; - updateLabPublicFeatureFlag: Scalars['Boolean']; + updateLabPublicFeatureFlag: FeatureFlag; updateOneField: Field; updateOneObject: Object; updateOneServerlessFunction: ServerlessFunction; @@ -2225,7 +2225,7 @@ export type UpdateLabPublicFeatureFlagMutationVariables = Exact<{ }>; -export type UpdateLabPublicFeatureFlagMutation = { __typename?: 'Mutation', updateLabPublicFeatureFlag: boolean }; +export type UpdateLabPublicFeatureFlagMutation = { __typename?: 'Mutation', updateLabPublicFeatureFlag: { __typename?: 'FeatureFlag', id: any, key: FeatureFlagKey, value: boolean } }; export type CreateOidcIdentityProviderMutationVariables = Exact<{ input: SetupOidcSsoInput; @@ -3884,7 +3884,11 @@ export type GetEnvironmentVariablesGroupedLazyQueryHookResult = ReturnType; export const UpdateLabPublicFeatureFlagDocument = gql` mutation UpdateLabPublicFeatureFlag($input: UpdateLabPublicFeatureFlagInput!) { - updateLabPublicFeatureFlag(input: $input) + updateLabPublicFeatureFlag(input: $input) { + id + key + value + } } `; export type UpdateLabPublicFeatureFlagMutationFn = Apollo.MutationFunction; diff --git a/packages/twenty-front/src/modules/settings/lab/graphql/mutations/updateLabPublicFeatureFlag.ts b/packages/twenty-front/src/modules/settings/lab/graphql/mutations/updateLabPublicFeatureFlag.ts index 622d6a0e019a..0d8bb91f20ee 100644 --- a/packages/twenty-front/src/modules/settings/lab/graphql/mutations/updateLabPublicFeatureFlag.ts +++ b/packages/twenty-front/src/modules/settings/lab/graphql/mutations/updateLabPublicFeatureFlag.ts @@ -4,6 +4,10 @@ export const UPDATE_LAB_PUBLIC_FEATURE_FLAG = gql` mutation UpdateLabPublicFeatureFlag( $input: UpdateLabPublicFeatureFlagInput! ) { - updateLabPublicFeatureFlag(input: $input) + updateLabPublicFeatureFlag(input: $input) { + id + key + value + } } `; diff --git a/packages/twenty-front/src/modules/settings/lab/hooks/useLabPublicFeatureFlags.ts b/packages/twenty-front/src/modules/settings/lab/hooks/useLabPublicFeatureFlags.ts index 5e3cb52dc9c4..204fd97c00db 100644 --- a/packages/twenty-front/src/modules/settings/lab/hooks/useLabPublicFeatureFlags.ts +++ b/packages/twenty-front/src/modules/settings/lab/hooks/useLabPublicFeatureFlags.ts @@ -15,7 +15,29 @@ export const useLabPublicFeatureFlags = () => { ); const labPublicFeatureFlags = useRecoilValue(labPublicFeatureFlagsState); - const [updateLabPublicFeatureFlag] = useUpdateLabPublicFeatureFlagMutation(); + const [updateLabPublicFeatureFlag] = useUpdateLabPublicFeatureFlagMutation({ + onCompleted: (data) => { + if (isDefined(currentWorkspace)) { + const updatedFlag = data.updateLabPublicFeatureFlag; + + setCurrentWorkspace({ + ...currentWorkspace, + featureFlags: [ + ...(currentWorkspace.featureFlags?.filter( + (flag) => flag.key !== updatedFlag.key, + ) ?? []), + { + ...updatedFlag, + workspaceId: currentWorkspace.id, + }, + ], + }); + } + }, + onError: (error) => { + setError(error.message); + }, + }); const handleLabPublicFeatureFlagUpdate = async ( publicFeatureFlag: FeatureFlagKey, @@ -35,20 +57,8 @@ export const useLabPublicFeatureFlags = () => { value, }, }, - onError: (error) => { - setError(error.message); - }, }); - if (isDefined(response.data)) { - setCurrentWorkspace({ - ...currentWorkspace, - featureFlags: currentWorkspace.featureFlags?.map((flag) => - flag.key === publicFeatureFlag ? { ...flag, value } : flag, - ), - }); - } - return !!response.data; }; diff --git a/packages/twenty-server/src/engine/core-modules/lab/lab.resolver.ts b/packages/twenty-server/src/engine/core-modules/lab/lab.resolver.ts index 8e20917c84fa..1b435b89f25d 100644 --- a/packages/twenty-server/src/engine/core-modules/lab/lab.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/lab/lab.resolver.ts @@ -2,6 +2,7 @@ import { UseFilters, UseGuards } from '@nestjs/common'; import { Args, Mutation, Resolver } from '@nestjs/graphql'; import { AuthGraphqlApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter'; +import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; import { UpdateLabPublicFeatureFlagInput } from 'src/engine/core-modules/lab/dtos/update-lab-public-feature-flag.input'; import { LabService } from 'src/engine/core-modules/lab/services/lab.service'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -14,13 +15,11 @@ export class LabResolver { constructor(private labService: LabService) {} @UseGuards(WorkspaceAuthGuard) - @Mutation(() => Boolean) + @Mutation(() => FeatureFlag) async updateLabPublicFeatureFlag( @Args('input') input: UpdateLabPublicFeatureFlagInput, @AuthWorkspace() workspace: Workspace, - ): Promise { - await this.labService.updateLabPublicFeatureFlag(workspace.id, input); - - return true; + ): Promise { + return this.labService.updateLabPublicFeatureFlag(workspace.id, input); } } diff --git a/packages/twenty-server/src/engine/core-modules/lab/services/lab.service.ts b/packages/twenty-server/src/engine/core-modules/lab/services/lab.service.ts index 622eafa86c3e..c66c46790ed0 100644 --- a/packages/twenty-server/src/engine/core-modules/lab/services/lab.service.ts +++ b/packages/twenty-server/src/engine/core-modules/lab/services/lab.service.ts @@ -31,7 +31,7 @@ export class LabService { async updateLabPublicFeatureFlag( workspaceId: string, payload: UpdateLabPublicFeatureFlagInput, - ): Promise { + ): Promise { featureFlagValidator.assertIsFeatureFlagKey( payload.publicFeatureFlag, new FeatureFlagException( @@ -62,15 +62,24 @@ export class LabService { (flag) => flag.key === FeatureFlagKey[payload.publicFeatureFlag], ); - if (!existingFlag) { - throw new FeatureFlagException( - 'Public feature flag not found', - FeatureFlagExceptionCode.FEATURE_FLAG_NOT_FOUND, - ); + if (existingFlag) { + await this.featureFlagRepository.update(existingFlag.id, { + value: payload.value, + }); + + return { ...existingFlag, value: payload.value }; } - await this.featureFlagRepository.update(existingFlag.id, { + const newFlag = await this.featureFlagRepository.save({ + key: FeatureFlagKey[payload.publicFeatureFlag], value: payload.value, + workspaceId: workspace.id, }); + + workspace.featureFlags = [...(workspace.featureFlags || []), newFlag]; + + await this.workspaceRepository.save(workspace); + + return newFlag; } }