Remove IS_APPLICATION_INSTALLATION_FROM_TARBALL_ENABLED feature flag (#18520)
## Summary - Removes the `IS_APPLICATION_INSTALLATION_FROM_TARBALL_ENABLED` feature flag, consolidating tarball-based app installation under the existing `IS_APPLICATION_ENABLED` flag - Removes the runtime feature flag check in `runWorkspaceMigration` resolver (the `@RequireFeatureFlag(IS_APPLICATION_ENABLED)` decorator already gates this endpoint) - Cleans up related integration test setup/teardown and mock feature flag maps ## Test plan - [ ] Verify tarball-based app installation still works when `IS_APPLICATION_ENABLED` is true - [ ] Verify app installation is blocked when `IS_APPLICATION_ENABLED` is false - [ ] Run `failing-install-application.integration-spec.ts` to confirm it passes without the removed flag Made with [Cursor](https://cursor.com)
This commit is contained in:
@@ -1643,7 +1643,6 @@ export type FeatureFlag = {
|
||||
export enum FeatureFlagKey {
|
||||
IS_AI_ENABLED = 'IS_AI_ENABLED',
|
||||
IS_APPLICATION_ENABLED = 'IS_APPLICATION_ENABLED',
|
||||
IS_APPLICATION_INSTALLATION_FROM_TARBALL_ENABLED = 'IS_APPLICATION_INSTALLATION_FROM_TARBALL_ENABLED',
|
||||
IS_ATTACHMENT_MIGRATED = 'IS_ATTACHMENT_MIGRATED',
|
||||
IS_COMMAND_MENU_ITEM_ENABLED = 'IS_COMMAND_MENU_ITEM_ENABLED',
|
||||
IS_DASHBOARD_V2_ENABLED = 'IS_DASHBOARD_V2_ENABLED',
|
||||
|
||||
@@ -1580,7 +1580,6 @@ enum FeatureFlagKey {
|
||||
IS_JSON_FILTER_ENABLED
|
||||
IS_AI_ENABLED
|
||||
IS_APPLICATION_ENABLED
|
||||
IS_APPLICATION_INSTALLATION_FROM_TARBALL_ENABLED
|
||||
IS_MARKETPLACE_ENABLED
|
||||
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED
|
||||
IS_PUBLIC_DOMAIN_ENABLED
|
||||
|
||||
@@ -1295,7 +1295,7 @@ export interface FeatureFlag {
|
||||
__typename: 'FeatureFlag'
|
||||
}
|
||||
|
||||
export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_AI_ENABLED' | 'IS_APPLICATION_ENABLED' | 'IS_APPLICATION_INSTALLATION_FROM_TARBALL_ENABLED' | 'IS_MARKETPLACE_ENABLED' | 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAILING_DOMAIN_ENABLED' | 'IS_DASHBOARD_V2_ENABLED' | 'IS_ATTACHMENT_MIGRATED' | 'IS_NOTE_TARGET_MIGRATED' | 'IS_TASK_TARGET_MIGRATED' | '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'
|
||||
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_DASHBOARD_V2_ENABLED' | 'IS_ATTACHMENT_MIGRATED' | 'IS_NOTE_TARGET_MIGRATED' | 'IS_TASK_TARGET_MIGRATED' | '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'
|
||||
|
||||
export interface SSOIdentityProvider {
|
||||
id: Scalars['UUID']
|
||||
@@ -8504,7 +8504,6 @@ export const enumFeatureFlagKey = {
|
||||
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_APPLICATION_INSTALLATION_FROM_TARBALL_ENABLED: 'IS_APPLICATION_INSTALLATION_FROM_TARBALL_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,
|
||||
|
||||
-22
@@ -11,10 +11,6 @@ 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 {
|
||||
ApplicationException,
|
||||
ApplicationExceptionCode,
|
||||
} from 'src/engine/core-modules/application/application.exception';
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
import { ApplicationSyncService } from 'src/engine/core-modules/application/application-manifest/application-sync.service';
|
||||
import { RunWorkspaceMigrationInput } from 'src/engine/core-modules/application/application-manifest/dtos/run-workspace-migration.input';
|
||||
@@ -29,7 +25,6 @@ import {
|
||||
} 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 { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
import { WorkspaceMigrationGraphqlApiExceptionInterceptor } from 'src/engine/workspace-manager/workspace-migration/interceptors/workspace-migration-graphql-api-exception.interceptor';
|
||||
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/services/workspace-migration-runner.service';
|
||||
|
||||
@@ -43,7 +38,6 @@ export class ApplicationManifestResolver {
|
||||
private readonly applicationService: ApplicationService,
|
||||
private readonly applicationSyncService: ApplicationSyncService,
|
||||
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
|
||||
private readonly workspaceCacheService: WorkspaceCacheService,
|
||||
) {}
|
||||
|
||||
@Mutation(() => Boolean)
|
||||
@@ -53,22 +47,6 @@ export class ApplicationManifestResolver {
|
||||
@Args() { workspaceMigration: { actions } }: RunWorkspaceMigrationInput,
|
||||
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
||||
) {
|
||||
const { featureFlagsMap } = await this.workspaceCacheService.getOrRecompute(
|
||||
workspaceId,
|
||||
['featureFlagsMap'],
|
||||
);
|
||||
|
||||
if (
|
||||
featureFlagsMap[
|
||||
FeatureFlagKey.IS_APPLICATION_INSTALLATION_FROM_TARBALL_ENABLED
|
||||
] !== true
|
||||
) {
|
||||
throw new ApplicationException(
|
||||
'Application installation from tarball is not enabled',
|
||||
ApplicationExceptionCode.FORBIDDEN,
|
||||
);
|
||||
}
|
||||
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{
|
||||
|
||||
-1
@@ -241,7 +241,6 @@ describe('WorkspaceEntityManager', () => {
|
||||
IS_COMMAND_MENU_ITEM_ENABLED: false,
|
||||
IS_NAVIGATION_MENU_ITEM_ENABLED: false,
|
||||
IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED: false,
|
||||
IS_APPLICATION_INSTALLATION_FROM_TARBALL_ENABLED: false,
|
||||
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED: false,
|
||||
IS_MARKETPLACE_ENABLED: false,
|
||||
IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED: false,
|
||||
|
||||
-20
@@ -4,23 +4,12 @@ import { installApplication } from 'test/integration/metadata/suites/application
|
||||
import { setupApplicationForSync } from 'test/integration/metadata/suites/application/utils/setup-application-for-sync.util';
|
||||
import { syncApplication } from 'test/integration/metadata/suites/application/utils/sync-application.util';
|
||||
import { uninstallApplication } from 'test/integration/metadata/suites/application/utils/uninstall-application.util';
|
||||
import { updateFeatureFlag } from 'test/integration/metadata/suites/utils/update-feature-flag.util';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { FeatureFlagKey } from 'twenty-shared/types';
|
||||
|
||||
const INVALID_UUID_APP_ID = uuidv4();
|
||||
const INVALID_UUID_ROLE_ID = uuidv4();
|
||||
|
||||
describe('Install application should fail when entity does not exist', () => {
|
||||
beforeAll(async () => {
|
||||
await updateFeatureFlag({
|
||||
featureFlag:
|
||||
FeatureFlagKey.IS_APPLICATION_INSTALLATION_FROM_TARBALL_ENABLED,
|
||||
value: true,
|
||||
expectToFail: false,
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await setupApplicationForSync({
|
||||
applicationUniversalIdentifier: INVALID_UUID_APP_ID,
|
||||
@@ -30,15 +19,6 @@ describe('Install application should fail when entity does not exist', () => {
|
||||
});
|
||||
}, 60000);
|
||||
|
||||
afterAll(async () => {
|
||||
await updateFeatureFlag({
|
||||
featureFlag:
|
||||
FeatureFlagKey.IS_APPLICATION_INSTALLATION_FROM_TARBALL_ENABLED,
|
||||
value: false,
|
||||
expectToFail: false,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
try {
|
||||
await uninstallApplication({
|
||||
|
||||
@@ -3,7 +3,6 @@ export enum FeatureFlagKey {
|
||||
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',
|
||||
IS_AI_ENABLED = 'IS_AI_ENABLED',
|
||||
IS_APPLICATION_ENABLED = 'IS_APPLICATION_ENABLED',
|
||||
IS_APPLICATION_INSTALLATION_FROM_TARBALL_ENABLED = 'IS_APPLICATION_INSTALLATION_FROM_TARBALL_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',
|
||||
|
||||
Reference in New Issue
Block a user