From 4d74cc0a28d67f6ee332c91827d50ca90822701e Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Sun, 29 Mar 2026 11:43:35 +0200 Subject: [PATCH] chore: remove IS_APPLICATION_ENABLED feature flag (#19081) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Remove the `IS_APPLICATION_ENABLED` feature flag — application features are now always enabled - Remove `@RequireFeatureFlag` decorators and `FeatureFlagGuard` from 7 application resolvers (registration, development, manifest, install, upgrade, marketplace, oauth) - Remove frontend feature flag checks: settings navigation visibility, route protection wrapper, side panel widget type gating, and role permission flag filtering - Delete the integration test for disabled-flag behavior - Clean up seed data, test mocks, and generated schema files --- .../src/metadata/generated/schema.graphql | 1 - .../src/metadata/generated/schema.ts | 3 +- .../src/generated-metadata/graphql.ts | 1 - .../modules/app/components/SettingsRoutes.tsx | 2 +- .../hooks/useSettingsNavigationItems.tsx | 7 +--- .../useSettingsRolePermissionFlagConfig.ts | 11 ------ .../SidePanelPageLayoutWidgetTypeSelect.tsx | 10 +---- .../application-development.resolver.ts | 11 +----- .../application-install.resolver.ts | 9 +---- .../application-manifest.resolver.ts | 10 +---- .../marketplace.resolver.ts | 16 +------- .../application-oauth.resolver.ts | 9 +---- .../application-registration.resolver.ts | 37 +------------------ .../application-upgrade.resolver.ts | 14 +------ .../workspace-entity-manager.spec.ts | 2 - .../core/utils/seed-feature-flags.util.ts | 5 --- ...ure-flag-disabled.integration-spec.ts.snap | 11 ------ ...-feature-flag-disabled.integration-spec.ts | 33 ----------------- .../twenty-shared/src/types/FeatureFlagKey.ts | 1 - 19 files changed, 13 insertions(+), 180 deletions(-) delete mode 100644 packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/failing-install-application-feature-flag-disabled.integration-spec.ts.snap delete mode 100644 packages/twenty-server/test/integration/metadata/suites/application/failing-install-application-feature-flag-disabled.integration-spec.ts diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql index f8a2296730..f72273f77f 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql @@ -1725,7 +1725,6 @@ enum FeatureFlagKey { IS_UNIQUE_INDEXES_ENABLED IS_JSON_FILTER_ENABLED IS_AI_ENABLED - IS_APPLICATION_ENABLED IS_MARKETPLACE_ENABLED IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED IS_PUBLIC_DOMAIN_ENABLED diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts index ba7f3f6b51..fc613776ac 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts @@ -1427,7 +1427,7 @@ export interface PublicFeatureFlag { __typename: 'PublicFeatureFlag' } -export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_AI_ENABLED' | 'IS_APPLICATION_ENABLED' | 'IS_MARKETPLACE_ENABLED' | 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAILING_DOMAIN_ENABLED' | 'IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED' | 'IS_JUNCTION_RELATIONS_ENABLED' | 'IS_COMMAND_MENU_ITEM_ENABLED' | 'IS_NAVIGATION_MENU_ITEM_ENABLED' | 'IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED' | 'IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED' | 'IS_DRAFT_EMAIL_ENABLED' | 'IS_USAGE_ANALYTICS_ENABLED' | 'IS_RICH_TEXT_V1_MIGRATED' | 'IS_DIRECT_GRAPHQL_EXECUTION_ENABLED' | 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED' | 'IS_CONNECTED_ACCOUNT_MIGRATED' | 'IS_GRAPHQL_QUERY_TIMING_ENABLED' | 'IS_RECORD_TABLE_WIDGET_ENABLED' | 'IS_DATASOURCE_MIGRATED' +export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_AI_ENABLED' | 'IS_MARKETPLACE_ENABLED' | 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAILING_DOMAIN_ENABLED' | 'IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED' | 'IS_JUNCTION_RELATIONS_ENABLED' | 'IS_COMMAND_MENU_ITEM_ENABLED' | 'IS_NAVIGATION_MENU_ITEM_ENABLED' | 'IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED' | 'IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED' | 'IS_DRAFT_EMAIL_ENABLED' | 'IS_USAGE_ANALYTICS_ENABLED' | 'IS_RICH_TEXT_V1_MIGRATED' | 'IS_DIRECT_GRAPHQL_EXECUTION_ENABLED' | 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED' | 'IS_CONNECTED_ACCOUNT_MIGRATED' | 'IS_GRAPHQL_QUERY_TIMING_ENABLED' | 'IS_RECORD_TABLE_WIDGET_ENABLED' | 'IS_DATASOURCE_MIGRATED' export interface ClientConfig { appVersion?: Scalars['String'] @@ -9141,7 +9141,6 @@ export const enumFeatureFlagKey = { IS_UNIQUE_INDEXES_ENABLED: 'IS_UNIQUE_INDEXES_ENABLED' as const, IS_JSON_FILTER_ENABLED: 'IS_JSON_FILTER_ENABLED' as const, IS_AI_ENABLED: 'IS_AI_ENABLED' as const, - IS_APPLICATION_ENABLED: 'IS_APPLICATION_ENABLED' as const, IS_MARKETPLACE_ENABLED: 'IS_MARKETPLACE_ENABLED' as const, IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED: 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' as const, IS_PUBLIC_DOMAIN_ENABLED: 'IS_PUBLIC_DOMAIN_ENABLED' as const, diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 8fdd024070..c85d713329 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -1702,7 +1702,6 @@ export type FeatureFlag = { export enum FeatureFlagKey { IS_AI_ENABLED = 'IS_AI_ENABLED', - IS_APPLICATION_ENABLED = 'IS_APPLICATION_ENABLED', IS_COMMAND_MENU_ITEM_ENABLED = 'IS_COMMAND_MENU_ITEM_ENABLED', IS_CONNECTED_ACCOUNT_MIGRATED = 'IS_CONNECTED_ACCOUNT_MIGRATED', IS_DATASOURCE_MIGRATED = 'IS_DATASOURCE_MIGRATED', diff --git a/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx b/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx index a7f840fa0a..7d98740ccf 100644 --- a/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx +++ b/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx @@ -689,7 +689,7 @@ export const SettingsRoutes = ({ isAdminPageEnabled }: SettingsRoutesProps) => ( } > diff --git a/packages/twenty-front/src/modules/settings/hooks/useSettingsNavigationItems.tsx b/packages/twenty-front/src/modules/settings/hooks/useSettingsNavigationItems.tsx index 37305db70e..55c091b71a 100644 --- a/packages/twenty-front/src/modules/settings/hooks/useSettingsNavigationItems.tsx +++ b/packages/twenty-front/src/modules/settings/hooks/useSettingsNavigationItems.tsx @@ -75,9 +75,6 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => { (currentUser?.canImpersonate || currentUser?.canAccessFullAdminPanel) ?? false; const isAIEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED); - const isApplicationEnabled = useIsFeatureEnabled( - FeatureFlagKey.IS_APPLICATION_ENABLED, - ); const isSupportChatConfigured = supportChat?.supportDriver === 'FRONT' && isNonEmptyString(supportChat.supportFrontChatId); @@ -178,9 +175,7 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => { label: t`Apps`, path: SettingsPath.Applications, Icon: IconPlug, - isHidden: - !isApplicationEnabled || - !permissionMap[PermissionFlagType.WORKSPACE], + isHidden: !permissionMap[PermissionFlagType.APPLICATIONS], modifier: 'new', }, { diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/permission-flags/hooks/useSettingsRolePermissionFlagConfig.ts b/packages/twenty-front/src/modules/settings/roles/role-permissions/permission-flags/hooks/useSettingsRolePermissionFlagConfig.ts index 80c59860a2..e67cf8aa03 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/permission-flags/hooks/useSettingsRolePermissionFlagConfig.ts +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/permission-flags/hooks/useSettingsRolePermissionFlagConfig.ts @@ -34,9 +34,6 @@ export const useSettingsRolePermissionFlagConfig = ({ assignmentCapabilities, }: UseSettingsRolePermissionFlagConfigParams = {}): SettingsRolePermissionsSettingPermission[] => { const isAIEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED); - const isApplicationEnabled = useIsFeatureEnabled( - FeatureFlagKey.IS_APPLICATION_ENABLED, - ); const { canBeAssignedToAgents = false, @@ -180,13 +177,6 @@ export const useSettingsRolePermissionFlagConfig = ({ if (permission.key === PermissionFlagType.AI_SETTINGS && !isAIEnabled) { return false; } - if ( - permission.key === PermissionFlagType.APPLICATIONS && - !isApplicationEnabled - ) { - return false; - } - if (hasAssignmentCapabilities) { if (canBeAssignedOnlyToAgents && !permission.isRelevantForAgents) { return false; @@ -209,6 +199,5 @@ export const useSettingsRolePermissionFlagConfig = ({ canBeAssignedToUsers, canBeAssignedToApiKeys, isAIEnabled, - isApplicationEnabled, ]); }; diff --git a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/SidePanelPageLayoutWidgetTypeSelect.tsx b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/SidePanelPageLayoutWidgetTypeSelect.tsx index c2be42b842..37fc59bfb0 100644 --- a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/SidePanelPageLayoutWidgetTypeSelect.tsx +++ b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/SidePanelPageLayoutWidgetTypeSelect.tsx @@ -86,19 +86,13 @@ export const SidePanelPageLayoutWidgetTypeSelect = () => { const { removePageLayoutWidgetAndPreservePosition } = useRemovePageLayoutWidgetAndPreservePosition(pageLayoutId); - const isApplicationEnabled = useIsFeatureEnabled( - FeatureFlagKey.IS_APPLICATION_ENABLED, - ); - const isRecordTableWidgetEnabled = useIsFeatureEnabled( FeatureFlagKey.IS_RECORD_TABLE_WIDGET_ENABLED, ); const { data: frontComponentsData } = useQuery<{ frontComponents: FrontComponent[]; - }>(FIND_MANY_FRONT_COMPONENTS, { - skip: !isApplicationEnabled, - }); + }>(FIND_MANY_FRONT_COMPONENTS); const frontComponents = frontComponentsData?.frontComponents ?? []; @@ -288,7 +282,7 @@ export const SidePanelPageLayoutWidgetTypeSelect = () => { - {isApplicationEnabled && frontComponentsWithSelectItemId.length > 0 && ( + {frontComponentsWithSelectItemId.length > 0 && ( {frontComponentsWithSelectItemId.map( ({ frontComponent, selectItemId }) => ( diff --git a/packages/twenty-server/src/engine/core-modules/application/application-development/application-development.resolver.ts b/packages/twenty-server/src/engine/core-modules/application/application-development/application-development.resolver.ts index 753e742f5f..31bd5b133f 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-development/application-development.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-development/application-development.resolver.ts @@ -8,7 +8,7 @@ import { Args, Mutation } from '@nestjs/graphql'; import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs'; import { PermissionFlagType } from 'twenty-shared/constants'; -import { FeatureFlagKey, FileFolder } from 'twenty-shared/types'; +import { FileFolder } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import type { FileUpload } from 'graphql-upload/processRequest.mjs'; @@ -41,10 +41,6 @@ import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator'; import { AuthUserWorkspaceId } from 'src/engine/decorators/auth/auth-user-workspace-id.decorator'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { DevelopmentGuard } from 'src/engine/guards/development.guard'; -import { - FeatureFlagGuard, - RequireFeatureFlag, -} from 'src/engine/guards/feature-flag.guard'; import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { WorkspaceMigrationGraphqlApiExceptionInterceptor } from 'src/engine/workspace-manager/workspace-migration/interceptors/workspace-migration-graphql-api-exception.interceptor'; @@ -56,7 +52,6 @@ import { streamToBuffer } from 'src/utils/stream-to-buffer'; @UseFilters(ApplicationExceptionFilter) @UseGuards( WorkspaceAuthGuard, - FeatureFlagGuard, DevelopmentGuard, SettingsPermissionGuard(PermissionFlagType.APPLICATIONS), ) @@ -72,7 +67,6 @@ export class ApplicationDevelopmentResolver { ) {} @Mutation(() => DevelopmentApplicationDTO) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) async createDevelopmentApplication( @Args() { universalIdentifier, name }: CreateDevelopmentApplicationInput, @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, @@ -110,7 +104,6 @@ export class ApplicationDevelopmentResolver { } @Mutation(() => ApplicationTokenPairDTO) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) async generateApplicationToken( @Args() { applicationId }: GenerateApplicationTokenInput, @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, @@ -126,7 +119,6 @@ export class ApplicationDevelopmentResolver { } @Mutation(() => WorkspaceMigrationDTO) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) async syncApplication( @Args() { manifest }: ApplicationInput, @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, @@ -183,7 +175,6 @@ export class ApplicationDevelopmentResolver { @Mutation(() => FileDTO) @UseGuards(SettingsPermissionGuard(PermissionFlagType.UPLOAD_FILE)) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) async uploadApplicationFile( @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, @Args({ name: 'file', type: () => GraphQLUpload }) diff --git a/packages/twenty-server/src/engine/core-modules/application/application-install/application-install.resolver.ts b/packages/twenty-server/src/engine/core-modules/application/application-install/application-install.resolver.ts index f92a9ea546..49d2036a4f 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-install/application-install.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-install/application-install.resolver.ts @@ -2,8 +2,6 @@ import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Query } from '@nestjs/graphql'; import { PermissionFlagType } from 'twenty-shared/constants'; -import { FeatureFlagKey } from 'twenty-shared/types'; - import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; import { ApplicationExceptionFilter } from 'src/engine/core-modules/application/application-exception-filter'; @@ -14,17 +12,13 @@ import { AuthGraphqlApiExceptionFilter } from 'src/engine/core-modules/auth/filt import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; -import { - FeatureFlagGuard, - RequireFeatureFlag, -} from 'src/engine/guards/feature-flag.guard'; import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; @UsePipes(ResolverValidationPipe) @MetadataResolver() @UseFilters(ApplicationExceptionFilter, AuthGraphqlApiExceptionFilter) -@UseGuards(WorkspaceAuthGuard, FeatureFlagGuard) +@UseGuards(WorkspaceAuthGuard) export class ApplicationInstallResolver { constructor( private readonly applicationService: ApplicationService, @@ -59,7 +53,6 @@ export class ApplicationInstallResolver { @Mutation(() => Boolean) @UseGuards(SettingsPermissionGuard(PermissionFlagType.APPLICATIONS)) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) async installApplication( @Args('appRegistrationId') appRegistrationId: string, @Args('version', { type: () => String, nullable: true }) diff --git a/packages/twenty-server/src/engine/core-modules/application/application-manifest/application-manifest.resolver.ts b/packages/twenty-server/src/engine/core-modules/application/application-manifest/application-manifest.resolver.ts index 0f4512fe20..5575d88bf5 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-manifest/application-manifest.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-manifest/application-manifest.resolver.ts @@ -7,8 +7,6 @@ import { import { Args, Mutation } from '@nestjs/graphql'; import { PermissionFlagType } from 'twenty-shared/constants'; -import { FeatureFlagKey } from 'twenty-shared/types'; - import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; import { ApplicationExceptionFilter } from 'src/engine/core-modules/application/application-exception-filter'; import { ApplicationSyncService } from 'src/engine/core-modules/application/application-manifest/application-sync.service'; @@ -19,10 +17,6 @@ import { AuthGraphqlApiExceptionFilter } from 'src/engine/core-modules/auth/filt import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; -import { - FeatureFlagGuard, - RequireFeatureFlag, -} from 'src/engine/guards/feature-flag.guard'; import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { AllUniversalWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/workspace-migration-action-common'; @@ -33,7 +27,7 @@ import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/wo @MetadataResolver() @UseInterceptors(WorkspaceMigrationGraphqlApiExceptionInterceptor) @UseFilters(ApplicationExceptionFilter, AuthGraphqlApiExceptionFilter) -@UseGuards(WorkspaceAuthGuard, FeatureFlagGuard) +@UseGuards(WorkspaceAuthGuard) export class ApplicationManifestResolver { constructor( private readonly applicationService: ApplicationService, @@ -43,7 +37,6 @@ export class ApplicationManifestResolver { @Mutation(() => Boolean) @UseGuards(SettingsPermissionGuard(PermissionFlagType.APPLICATIONS)) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) async runWorkspaceMigration( @Args() { workspaceMigration: { actions } }: RunWorkspaceMigrationInput, @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, @@ -69,7 +62,6 @@ export class ApplicationManifestResolver { @Mutation(() => Boolean) @UseGuards(SettingsPermissionGuard(PermissionFlagType.APPLICATIONS)) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) async uninstallApplication( @Args() { universalIdentifier }: UninstallApplicationInput, @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, diff --git a/packages/twenty-server/src/engine/core-modules/application/application-marketplace/marketplace.resolver.ts b/packages/twenty-server/src/engine/core-modules/application/application-marketplace/marketplace.resolver.ts index 8433590c78..f04ea1caf6 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-marketplace/marketplace.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-marketplace/marketplace.resolver.ts @@ -2,8 +2,6 @@ import { UseFilters, UseGuards } from '@nestjs/common'; import { Args, Mutation, Query } from '@nestjs/graphql'; import { PermissionFlagType } from 'twenty-shared/constants'; -import { FeatureFlagKey } from 'twenty-shared/types'; - import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; import { ApplicationRegistrationExceptionFilter } from 'src/engine/core-modules/application/application-registration/application-registration-exception-filter'; import { ApplicationInstallService } from 'src/engine/core-modules/application/application-install/application-install.service'; @@ -11,10 +9,6 @@ import { MarketplaceAppDTO } from 'src/engine/core-modules/application/applicati import { MarketplaceQueryService } from 'src/engine/core-modules/application/application-marketplace/marketplace-query.service'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; -import { - FeatureFlagGuard, - RequireFeatureFlag, -} from 'src/engine/guards/feature-flag.guard'; import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard'; import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard'; import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; @@ -22,12 +16,7 @@ import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; @MetadataResolver() @UseFilters(ApplicationRegistrationExceptionFilter) -@UseGuards( - UserAuthGuard, - WorkspaceAuthGuard, - FeatureFlagGuard, - NoPermissionGuard, -) +@UseGuards(UserAuthGuard, WorkspaceAuthGuard, NoPermissionGuard) export class MarketplaceResolver { constructor( private readonly marketplaceQueryService: MarketplaceQueryService, @@ -35,13 +24,11 @@ export class MarketplaceResolver { ) {} @Query(() => [MarketplaceAppDTO]) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) async findManyMarketplaceApps(): Promise { return this.marketplaceQueryService.findManyMarketplaceApps(); } @Query(() => MarketplaceAppDTO) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) async findOneMarketplaceApp( @Args('universalIdentifier') universalIdentifier: string, ): Promise { @@ -52,7 +39,6 @@ export class MarketplaceResolver { @Mutation(() => Boolean) @UseGuards(SettingsPermissionGuard(PermissionFlagType.MARKETPLACE_APPS)) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) async installMarketplaceApp( @Args('universalIdentifier') universalIdentifier: string, @Args('version', { type: () => String, nullable: true }) diff --git a/packages/twenty-server/src/engine/core-modules/application/application-oauth/application-oauth.resolver.ts b/packages/twenty-server/src/engine/core-modules/application/application-oauth/application-oauth.resolver.ts index 64af1b2d3f..ba880adb17 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-oauth/application-oauth.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-oauth/application-oauth.resolver.ts @@ -1,8 +1,6 @@ import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation } from '@nestjs/graphql'; -import { FeatureFlagKey } from 'twenty-shared/types'; - import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; import { ApplicationException, @@ -14,17 +12,13 @@ import { ApplicationTokenService } from 'src/engine/core-modules/auth/token/serv import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; -import { - FeatureFlagGuard, - RequireFeatureFlag, -} from 'src/engine/guards/feature-flag.guard'; import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; @UsePipes(ResolverValidationPipe) @MetadataResolver() @UseFilters(AuthGraphqlApiExceptionFilter) -@UseGuards(WorkspaceAuthGuard, FeatureFlagGuard) +@UseGuards(WorkspaceAuthGuard) export class ApplicationOAuthResolver { constructor( private readonly applicationTokenService: ApplicationTokenService, @@ -32,7 +26,6 @@ export class ApplicationOAuthResolver { @Mutation(() => ApplicationTokenPairDTO) @UseGuards(NoPermissionGuard) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) async renewApplicationToken( @Args('applicationRefreshToken') applicationRefreshToken: string, @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, diff --git a/packages/twenty-server/src/engine/core-modules/application/application-registration/application-registration.resolver.ts b/packages/twenty-server/src/engine/core-modules/application/application-registration/application-registration.resolver.ts index e87e65cb09..47fa498fa0 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-registration/application-registration.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-registration/application-registration.resolver.ts @@ -3,7 +3,7 @@ import { Args, Mutation, Query } from '@nestjs/graphql'; import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs'; import { PermissionFlagType } from 'twenty-shared/constants'; -import { FeatureFlagKey, FileFolder } from 'twenty-shared/types'; +import { FileFolder } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import type { FileUpload } from 'graphql-upload/processRequest.mjs'; @@ -40,10 +40,6 @@ import { UserEntity } from 'src/engine/core-modules/user/user.entity'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; -import { - FeatureFlagGuard, - RequireFeatureFlag, -} from 'src/engine/guards/feature-flag.guard'; import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard'; import { PublicEndpointGuard } from 'src/engine/guards/public-endpoint.guard'; import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard'; @@ -73,8 +69,7 @@ export class ApplicationRegistrationResolver { return this.applicationRegistrationService.findPublicByClientId(clientId); } - @UseGuards(WorkspaceAuthGuard, FeatureFlagGuard, NoPermissionGuard) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) + @UseGuards(WorkspaceAuthGuard, NoPermissionGuard) @Query(() => ApplicationRegistrationEntity, { nullable: true }) async findApplicationRegistrationByUniversalIdentifier( @Args('universalIdentifier') universalIdentifier: string, @@ -86,10 +81,8 @@ export class ApplicationRegistrationResolver { @UseGuards( WorkspaceAuthGuard, - FeatureFlagGuard, SettingsPermissionGuard(PermissionFlagType.API_KEYS_AND_WEBHOOKS), ) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) @Query(() => [ApplicationRegistrationEntity]) async findManyApplicationRegistrations( @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, @@ -99,10 +92,8 @@ export class ApplicationRegistrationResolver { @UseGuards( WorkspaceAuthGuard, - FeatureFlagGuard, SettingsPermissionGuard(PermissionFlagType.API_KEYS_AND_WEBHOOKS), ) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) @Query(() => ApplicationRegistrationEntity) async findOneApplicationRegistration( @Args('id') id: string, @@ -113,10 +104,8 @@ export class ApplicationRegistrationResolver { @UseGuards( WorkspaceAuthGuard, - FeatureFlagGuard, SettingsPermissionGuard(PermissionFlagType.API_KEYS_AND_WEBHOOKS), ) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) @Query(() => ApplicationRegistrationStatsDTO) async findApplicationRegistrationStats( @Args('id') id: string, @@ -127,10 +116,8 @@ export class ApplicationRegistrationResolver { @UseGuards( WorkspaceAuthGuard, - FeatureFlagGuard, SettingsPermissionGuard(PermissionFlagType.API_KEYS_AND_WEBHOOKS), ) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) @Mutation(() => CreateApplicationRegistrationDTO) async createApplicationRegistration( @Args('input') input: CreateApplicationRegistrationInput, @@ -146,10 +133,8 @@ export class ApplicationRegistrationResolver { @UseGuards( WorkspaceAuthGuard, - FeatureFlagGuard, SettingsPermissionGuard(PermissionFlagType.API_KEYS_AND_WEBHOOKS), ) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) @Mutation(() => ApplicationRegistrationEntity) async updateApplicationRegistration( @Args('input') input: UpdateApplicationRegistrationInput, @@ -160,10 +145,8 @@ export class ApplicationRegistrationResolver { @UseGuards( WorkspaceAuthGuard, - FeatureFlagGuard, SettingsPermissionGuard(PermissionFlagType.API_KEYS_AND_WEBHOOKS), ) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) @Mutation(() => Boolean) async deleteApplicationRegistration( @Args('id') id: string, @@ -174,10 +157,8 @@ export class ApplicationRegistrationResolver { @UseGuards( WorkspaceAuthGuard, - FeatureFlagGuard, SettingsPermissionGuard(PermissionFlagType.API_KEYS_AND_WEBHOOKS), ) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) @Mutation(() => RotateClientSecretDTO) async rotateApplicationRegistrationClientSecret( @Args('id') id: string, @@ -194,10 +175,8 @@ export class ApplicationRegistrationResolver { @UseGuards( WorkspaceAuthGuard, - FeatureFlagGuard, SettingsPermissionGuard(PermissionFlagType.API_KEYS_AND_WEBHOOKS), ) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) @Query(() => [ApplicationRegistrationVariableEntity]) async findApplicationRegistrationVariables( @Args('applicationRegistrationId') applicationRegistrationId: string, @@ -211,10 +190,8 @@ export class ApplicationRegistrationResolver { @UseGuards( WorkspaceAuthGuard, - FeatureFlagGuard, SettingsPermissionGuard(PermissionFlagType.API_KEYS_AND_WEBHOOKS), ) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) @Mutation(() => ApplicationRegistrationVariableEntity) async createApplicationRegistrationVariable( @Args('input') input: CreateApplicationRegistrationVariableInput, @@ -228,10 +205,8 @@ export class ApplicationRegistrationResolver { @UseGuards( WorkspaceAuthGuard, - FeatureFlagGuard, SettingsPermissionGuard(PermissionFlagType.API_KEYS_AND_WEBHOOKS), ) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) @Mutation(() => ApplicationRegistrationVariableEntity) async updateApplicationRegistrationVariable( @Args('input') input: UpdateApplicationRegistrationVariableInput, @@ -245,10 +220,8 @@ export class ApplicationRegistrationResolver { @UseGuards( WorkspaceAuthGuard, - FeatureFlagGuard, SettingsPermissionGuard(PermissionFlagType.API_KEYS_AND_WEBHOOKS), ) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) @Mutation(() => Boolean) async deleteApplicationRegistrationVariable( @Args('id') id: string, @@ -262,10 +235,8 @@ export class ApplicationRegistrationResolver { @UseGuards( WorkspaceAuthGuard, - FeatureFlagGuard, SettingsPermissionGuard(PermissionFlagType.MARKETPLACE_APPS), ) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) @Mutation(() => ApplicationRegistrationEntity) async uploadAppTarball( @Args({ name: 'file', type: () => GraphQLUpload }) @@ -293,10 +264,8 @@ export class ApplicationRegistrationResolver { @UseGuards( WorkspaceAuthGuard, - FeatureFlagGuard, SettingsPermissionGuard(PermissionFlagType.API_KEYS_AND_WEBHOOKS), ) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) @Query(() => String, { nullable: true }) async applicationRegistrationTarballUrl( @Args('id') id: string, @@ -323,10 +292,8 @@ export class ApplicationRegistrationResolver { @UseGuards( WorkspaceAuthGuard, - FeatureFlagGuard, SettingsPermissionGuard(PermissionFlagType.APPLICATIONS), ) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) @Mutation(() => ApplicationRegistrationEntity) async transferApplicationRegistrationOwnership( @Args() diff --git a/packages/twenty-server/src/engine/core-modules/application/application-upgrade/application-upgrade.resolver.ts b/packages/twenty-server/src/engine/core-modules/application/application-upgrade/application-upgrade.resolver.ts index 15ca639bb5..91a79d7a34 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-upgrade/application-upgrade.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-upgrade/application-upgrade.resolver.ts @@ -2,17 +2,11 @@ import { UseFilters, UseGuards } from '@nestjs/common'; import { Args, Mutation } from '@nestjs/graphql'; import { PermissionFlagType } from 'twenty-shared/constants'; -import { FeatureFlagKey } from 'twenty-shared/types'; - import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; import { ApplicationExceptionFilter } from 'src/engine/core-modules/application/application-exception-filter'; import { ApplicationUpgradeService } from 'src/engine/core-modules/application/application-upgrade/application-upgrade.service'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; -import { - FeatureFlagGuard, - RequireFeatureFlag, -} from 'src/engine/guards/feature-flag.guard'; import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard'; import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard'; import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; @@ -20,12 +14,7 @@ import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; @MetadataResolver() @UseFilters(ApplicationExceptionFilter) -@UseGuards( - UserAuthGuard, - WorkspaceAuthGuard, - FeatureFlagGuard, - NoPermissionGuard, -) +@UseGuards(UserAuthGuard, WorkspaceAuthGuard, NoPermissionGuard) export class ApplicationUpgradeResolver { constructor( private readonly applicationUpgradeService: ApplicationUpgradeService, @@ -33,7 +22,6 @@ export class ApplicationUpgradeResolver { @Mutation(() => Boolean) @UseGuards(SettingsPermissionGuard(PermissionFlagType.MARKETPLACE_APPS)) - @RequireFeatureFlag(FeatureFlagKey.IS_APPLICATION_ENABLED) async upgradeApplication( @Args('appRegistrationId') appRegistrationId: string, @Args('targetVersion') targetVersion: string, diff --git a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts index 7007a53e03..a62b305604 100644 --- a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts +++ b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts @@ -233,7 +233,6 @@ describe('WorkspaceEntityManager', () => { IS_UNIQUE_INDEXES_ENABLED: false, IS_JSON_FILTER_ENABLED: false, IS_AI_ENABLED: false, - IS_APPLICATION_ENABLED: false, IS_MARKETPLACE_ENABLED: false, IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED: false, IS_PUBLIC_DOMAIN_ENABLED: false, @@ -273,7 +272,6 @@ describe('WorkspaceEntityManager', () => { IS_UNIQUE_INDEXES_ENABLED: false, IS_JSON_FILTER_ENABLED: false, IS_AI_ENABLED: false, - IS_APPLICATION_ENABLED: false, IS_PUBLIC_DOMAIN_ENABLED: false, IS_EMAILING_DOMAIN_ENABLED: false, IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED: false, diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts index 2d79e7c60c..0c46b56465 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts @@ -30,11 +30,6 @@ export const seedFeatureFlags = async ({ workspaceId: workspaceId, value: true, }, - { - key: FeatureFlagKey.IS_APPLICATION_ENABLED, - workspaceId: workspaceId, - value: true, - }, { key: FeatureFlagKey.IS_PUBLIC_DOMAIN_ENABLED, workspaceId: workspaceId, diff --git a/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/failing-install-application-feature-flag-disabled.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/failing-install-application-feature-flag-disabled.integration-spec.ts.snap deleted file mode 100644 index 4bb2897930..0000000000 --- a/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/failing-install-application-feature-flag-disabled.integration-spec.ts.snap +++ /dev/null @@ -1,11 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`Install application should fail when feature flag is disabled should fail with forbidden error when feature flag is disabled 1`] = ` -{ - "extensions": { - "code": "FORBIDDEN", - "userFriendlyMessage": "An error occurred.", - }, - "message": "Feature flag "IS_APPLICATION_ENABLED" is not enabled for this workspace", -} -`; diff --git a/packages/twenty-server/test/integration/metadata/suites/application/failing-install-application-feature-flag-disabled.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/application/failing-install-application-feature-flag-disabled.integration-spec.ts deleted file mode 100644 index c6f2381129..0000000000 --- a/packages/twenty-server/test/integration/metadata/suites/application/failing-install-application-feature-flag-disabled.integration-spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { FeatureFlagKey } from 'twenty-shared/types'; -import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util'; -import { installApplication } from 'test/integration/metadata/suites/application/utils/install-application.util'; -import { updateFeatureFlag } from 'test/integration/metadata/suites/utils/update-feature-flag.util'; - -describe('Install application should fail when feature flag is disabled', () => { - beforeAll(async () => { - await updateFeatureFlag({ - featureFlag: FeatureFlagKey.IS_APPLICATION_ENABLED, - value: false, - expectToFail: false, - }); - }); - - afterAll(async () => { - await updateFeatureFlag({ - featureFlag: FeatureFlagKey.IS_APPLICATION_ENABLED, - value: true, - expectToFail: false, - }); - }); - - it('should fail with forbidden error when feature flag is disabled', async () => { - const { errors } = await installApplication({ - expectToFail: true, - input: { - appRegistrationId: '20202020-0000-0000-0000-000000000000', - }, - }); - - expectOneNotInternalServerErrorSnapshot({ errors }); - }); -}); diff --git a/packages/twenty-shared/src/types/FeatureFlagKey.ts b/packages/twenty-shared/src/types/FeatureFlagKey.ts index 5e0261806a..6437e154ff 100644 --- a/packages/twenty-shared/src/types/FeatureFlagKey.ts +++ b/packages/twenty-shared/src/types/FeatureFlagKey.ts @@ -2,7 +2,6 @@ export enum FeatureFlagKey { IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED', IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED', IS_AI_ENABLED = 'IS_AI_ENABLED', - IS_APPLICATION_ENABLED = 'IS_APPLICATION_ENABLED', IS_MARKETPLACE_ENABLED = 'IS_MARKETPLACE_ENABLED', IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED = 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED', IS_PUBLIC_DOMAIN_ENABLED = 'IS_PUBLIC_DOMAIN_ENABLED',