From 1296327c03fb58e8d3192e826855702334d56969 Mon Sep 17 00:00:00 2001 From: "gitar-bot[bot]" <159877585+gitar-bot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 11:51:55 +0200 Subject: [PATCH] [Gitar] Cleaning up stale flag: archiveProjects with value true (#8201) --- .../src/component/project/Project/Project.tsx | 4 +-- .../Settings/EditProject/EditProject.tsx | 17 +-------- .../project/ProjectList/LegacyProjectList.tsx | 25 +++++-------- .../project/ProjectList/ProjectList.tsx | 6 +--- frontend/src/interfaces/uiConfig.ts | 1 - .../feature-toggle/feature-toggle-service.ts | 22 +++++------- .../tests/feature-toggle-service.e2e.test.ts | 16 ++------- .../features/project/project-read-model.ts | 30 ++++++---------- .../project/project-service.e2e.test.ts | 2 +- src/lib/features/project/project-service.ts | 18 ++++------ .../project/project-store.e2e.test.ts | 2 +- src/lib/features/project/project-store.ts | 35 ++++++------------- src/lib/types/experimental.ts | 5 --- 13 files changed, 51 insertions(+), 132 deletions(-) diff --git a/frontend/src/component/project/Project/Project.tsx b/frontend/src/component/project/Project/Project.tsx index e7b8084b1e..2db8712c5f 100644 --- a/frontend/src/component/project/Project/Project.tsx +++ b/frontend/src/component/project/Project/Project.tsx @@ -44,7 +44,6 @@ import { ProjectApplications } from '../ProjectApplications/ProjectApplications' import { ProjectInsights } from './ProjectInsights/ProjectInsights'; import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview'; import { ProjectArchived } from './ArchiveProject/ProjectArchived'; -import { useUiFlag } from 'hooks/useUiFlag'; const StyledBadge = styled(Badge)(({ theme }) => ({ position: 'absolute', @@ -77,7 +76,6 @@ export const Project = () => { const basePath = `/projects/${projectId}`; const projectName = project?.name || projectId; const { favorite, unfavorite } = useFavoriteProjectsApi(); - const archiveProjectsEnabled = useUiFlag('archiveProjects'); const [showDelDialog, setShowDelDialog] = useState(false); @@ -192,7 +190,7 @@ export const Project = () => { ); - if (archiveProjectsEnabled && Boolean(project.archivedAt)) { + if (project.archivedAt) { return ; } diff --git a/frontend/src/component/project/Project/ProjectSettings/Settings/EditProject/EditProject.tsx b/frontend/src/component/project/Project/ProjectSettings/Settings/EditProject/EditProject.tsx index 01a277b16d..56d13e2b36 100644 --- a/frontend/src/component/project/Project/ProjectSettings/Settings/EditProject/EditProject.tsx +++ b/frontend/src/component/project/Project/ProjectSettings/Settings/EditProject/EditProject.tsx @@ -10,12 +10,10 @@ import { Alert, styled } from '@mui/material'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { UpdateEnterpriseSettings } from './UpdateEnterpriseSettings'; import { UpdateProject } from './UpdateProject'; -import { DeleteProjectForm } from './DeleteProjectForm'; import useProjectOverview, { featuresCount, } from 'hooks/api/getters/useProjectOverview/useProjectOverview'; import { ArchiveProjectForm } from './ArchiveProjectForm'; -import { useUiFlag } from 'hooks/useUiFlag'; const StyledFormContainer = styled('div')(({ theme }) => ({ display: 'flex', @@ -28,7 +26,6 @@ const EditProject = () => { const { hasAccess } = useContext(AccessContext); const id = useRequiredPathParam('projectId'); const { project } = useProjectOverview(id); - const archiveProjectsEnabled = useUiFlag('archiveProjects'); if (!project.name) { return null; @@ -52,19 +49,7 @@ const EditProject = () => { condition={isEnterprise()} show={} /> - - } - elseShow={ - - } - /> + ); diff --git a/frontend/src/component/project/ProjectList/LegacyProjectList.tsx b/frontend/src/component/project/ProjectList/LegacyProjectList.tsx index deb87431fc..68f34b4857 100644 --- a/frontend/src/component/project/ProjectList/LegacyProjectList.tsx +++ b/frontend/src/component/project/ProjectList/LegacyProjectList.tsx @@ -25,7 +25,6 @@ import { useProfile } from 'hooks/api/getters/useProfile/useProfile'; import { groupProjects } from './group-projects'; import { ProjectGroup } from './ProjectGroup'; import { CreateProjectDialog } from '../Project/CreateProject/NewCreateProjectForm/CreateProjectDialog'; -import { useUiFlag } from 'hooks/useUiFlag'; const StyledApiError = styled(ApiError)(({ theme }) => ({ maxWidth: '500px', @@ -126,7 +125,6 @@ export const ProjectList = () => { const [searchValue, setSearchValue] = useState( searchParams.get('search') || '', ); - const archiveProjectsEnabled = useUiFlag('archiveProjects'); const myProjects = new Set(useProfile().profile?.projects || []); @@ -200,20 +198,15 @@ export const ProjectList = () => { } /> - - - Archived projects - - - - } - /> + <> + + Archived projects + + + diff --git a/frontend/src/component/project/ProjectList/ProjectList.tsx b/frontend/src/component/project/ProjectList/ProjectList.tsx index 4c3acbb40f..801f59039f 100644 --- a/frontend/src/component/project/ProjectList/ProjectList.tsx +++ b/frontend/src/component/project/ProjectList/ProjectList.tsx @@ -36,7 +36,6 @@ const NewProjectList = () => { const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); const [state, setState] = useProjectsListState(); - const archiveProjectsEnabled = useUiFlag('archiveProjects'); const myProjects = new Set(useProfile().profile?.projects || []); @@ -78,10 +77,7 @@ const NewProjectList = () => { } /> - } - /> + diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index 7fcb3fb327..784192cf63 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -86,7 +86,6 @@ export type UiFlags = { enableLegacyVariants?: boolean; navigationSidebar?: boolean; flagCreator?: boolean; - archiveProjects?: boolean; projectListImprovements?: boolean; onboardingUI?: boolean; eventTimeline?: boolean; diff --git a/src/lib/features/feature-toggle/feature-toggle-service.ts b/src/lib/features/feature-toggle/feature-toggle-service.ts index 99c06a39ac..d34a4b3f6f 100644 --- a/src/lib/features/feature-toggle/feature-toggle-service.ts +++ b/src/lib/features/feature-toggle/feature-toggle-service.ts @@ -1230,14 +1230,12 @@ class FeatureToggleService { } private async validateActiveProject(projectId: string) { - if (this.flagResolver.isEnabled('archiveProjects')) { - const hasActiveProject = - await this.projectStore.hasActiveProject(projectId); - if (!hasActiveProject) { - throw new NotFoundError( - `Active project with id ${projectId} does not exist`, - ); - } + const hasActiveProject = + await this.projectStore.hasActiveProject(projectId); + if (!hasActiveProject) { + throw new NotFoundError( + `Active project with id ${projectId} does not exist`, + ); } } @@ -1253,12 +1251,8 @@ class FeatureToggleService { await this.validateName(value.name); await this.validateFeatureFlagNameAgainstPattern(value.name, projectId); - let projectExists: boolean; - if (this.flagResolver.isEnabled('archiveProjects')) { - projectExists = await this.projectStore.hasActiveProject(projectId); - } else { - projectExists = await this.projectStore.hasProject(projectId); - } + const projectExists = + await this.projectStore.hasActiveProject(projectId); if (await this.projectStore.isFeatureLimitReached(projectId)) { throw new InvalidOperationError( diff --git a/src/lib/features/feature-toggle/tests/feature-toggle-service.e2e.test.ts b/src/lib/features/feature-toggle/tests/feature-toggle-service.e2e.test.ts index 5fd0b835b2..fb93c96dab 100644 --- a/src/lib/features/feature-toggle/tests/feature-toggle-service.e2e.test.ts +++ b/src/lib/features/feature-toggle/tests/feature-toggle-service.e2e.test.ts @@ -38,7 +38,6 @@ let segmentService: ISegmentService; let eventService: EventService; let environmentService: EnvironmentService; let unleashConfig: IUnleashConfig; -const TEST_USER_ID = -9999; const mockConstraints = (): IConstraint[] => { return Array.from({ length: 5 }).map(() => ({ values: ['x', 'y', 'z'], @@ -51,7 +50,7 @@ const irrelevantDate = new Date(); beforeAll(async () => { const config = createTestConfig({ - experimental: { flags: { archiveProjects: true } }, + experimental: { flags: {} }, }); db = await dbInit( 'feature_toggle_service_v2_service_serial', @@ -77,7 +76,6 @@ beforeEach(async () => { }); test('Should create feature flag strategy configuration', async () => { const projectId = 'default'; - const username = 'feature-flag'; const config: Omit = { name: 'default', constraints: [], @@ -104,7 +102,6 @@ test('Should create feature flag strategy configuration', async () => { test('Should be able to update existing strategy configuration', async () => { const projectId = 'default'; - const username = 'existing-strategy'; const featureName = 'update-existing-strategy'; const config: Omit = { name: 'default', @@ -139,8 +136,6 @@ test('Should be able to update existing strategy configuration', async () => { test('Should be able to get strategy by id', async () => { const featureName = 'get-strategy-by-id'; const projectId = 'default'; - - const userName = 'strategy'; const config: Omit = { name: 'default', constraints: [], @@ -168,8 +163,6 @@ test('Should be able to get strategy by id', async () => { test('should ignore name in the body when updating feature flag', async () => { const featureName = 'body-name-update'; const projectId = 'default'; - - const userName = 'strategy'; const secondFeatureName = 'body-name-update2'; await service.createFeatureToggle( @@ -213,8 +206,6 @@ test('should ignore name in the body when updating feature flag', async () => { test('should not get empty rows as features', async () => { const projectId = 'default'; - const userName = 'strategy'; - await service.createFeatureToggle( projectId, { @@ -505,7 +496,6 @@ test('If change requests are enabled, cannot change variants without going via C }); test('If CRs are protected for any environment in the project stops bulk update of variants', async () => { - const user = { email: 'test@example.com', username: 'test-user' } as User; const project = await stores.projectStore.create({ id: 'crOnVariantsProject', name: 'crOnVariantsProject', @@ -599,7 +589,6 @@ test('getPlaygroundFeatures should return ids and titles (if they exist) on clie const projectId = 'default'; const title = 'custom strategy title'; - const userName = 'strategy'; const config: Omit = { name: 'default', constraints: [], @@ -819,7 +808,6 @@ test('Should enable disabled strategies on feature environment enabled', async ( const flagName = 'enableThisFlag'; const project = 'default'; const environment = 'default'; - const shouldActivateDisabledStrategies = true; await service.createFeatureToggle( project, { @@ -856,7 +844,7 @@ test('Should enable disabled strategies on feature environment enabled', async ( true, TEST_AUDIT_USER, { email: 'test@example.com' } as User, - shouldActivateDisabledStrategies, + true, ); const strategy = await service.getStrategy(createdConfig.id); diff --git a/src/lib/features/project/project-read-model.ts b/src/lib/features/project/project-read-model.ts index c793543d81..b8b12d60cf 100644 --- a/src/lib/features/project/project-read-model.ts +++ b/src/lib/features/project/project-read-model.ts @@ -104,12 +104,10 @@ export class ProjectReadModel implements IProjectReadModel { }) .orderBy('projects.name', 'asc'); - if (this.flagResolver.isEnabled('archiveProjects')) { - if (query?.archived === true) { - projects = projects.whereNot(`${TABLE}.archived_at`, null); - } else { - projects = projects.where(`${TABLE}.archived_at`, null); - } + if (query?.archived === true) { + projects = projects.whereNot(`${TABLE}.archived_at`, null); + } else { + projects = projects.where(`${TABLE}.archived_at`, null); } if (query?.id) { @@ -124,12 +122,9 @@ export class ProjectReadModel implements IProjectReadModel { 'MAX(events.created_at) AS last_updated', ), 'project_settings.project_mode', + 'projects.archived_at', ] as (string | Raw)[]; - if (this.flagResolver.isEnabled('archiveProjects')) { - selectColumns.push(`${TABLE}.archived_at`); - } - let groupByColumns = ['projects.id', 'project_settings.project_mode']; if (userId) { @@ -179,12 +174,10 @@ export class ProjectReadModel implements IProjectReadModel { .leftJoin('project_stats', 'project_stats.project', 'projects.id') .orderBy('projects.name', 'asc'); - if (this.flagResolver.isEnabled('archiveProjects')) { - if (query?.archived === true) { - projects = projects.whereNot(`${TABLE}.archived_at`, null); - } else { - projects = projects.where(`${TABLE}.archived_at`, null); - } + if (query?.archived === true) { + projects = projects.whereNot(`${TABLE}.archived_at`, null); + } else { + projects = projects.where(`${TABLE}.archived_at`, null); } if (query?.id) { @@ -199,12 +192,9 @@ export class ProjectReadModel implements IProjectReadModel { 'count(features.name) FILTER (WHERE features.archived_at is null and features.potentially_stale IS TRUE) AS potentially_stale_feature_count', ), 'project_stats.avg_time_to_prod_current_window', + 'projects.archived_at', ] as (string | Raw)[]; - if (this.flagResolver.isEnabled('archiveProjects')) { - selectColumns.push(`${TABLE}.archived_at`); - } - const groupByColumns = [ 'projects.id', 'project_stats.avg_time_to_prod_current_window', diff --git a/src/lib/features/project/project-service.e2e.test.ts b/src/lib/features/project/project-service.e2e.test.ts index eeebf1f558..bdd3cda35f 100644 --- a/src/lib/features/project/project-service.e2e.test.ts +++ b/src/lib/features/project/project-service.e2e.test.ts @@ -83,7 +83,7 @@ beforeAll(async () => { const config = createTestConfig({ getLogger, experimental: { - flags: { archiveProjects: true, useProjectReadModel: true }, + flags: { useProjectReadModel: true }, }, }); eventService = createEventsService(db.rawDatabase, config); diff --git a/src/lib/features/project/project-service.ts b/src/lib/features/project/project-service.ts index 4128074635..c476c28647 100644 --- a/src/lib/features/project/project-service.ts +++ b/src/lib/features/project/project-service.ts @@ -500,14 +500,12 @@ export default class ProjectService { } private async validateActiveProject(projectId: string) { - if (this.flagResolver.isEnabled('archiveProjects')) { - const hasActiveProject = - await this.projectStore.hasActiveProject(projectId); - if (!hasActiveProject) { - throw new NotFoundError( - `Active project with id ${projectId} does not exist`, - ); - } + const hasActiveProject = + await this.projectStore.hasActiveProject(projectId); + if (!hasActiveProject) { + throw new NotFoundError( + `Active project with id ${projectId} does not exist`, + ); } } @@ -1552,9 +1550,7 @@ export default class ProjectService { health: project.health || 0, favorite: favorite, updatedAt: project.updatedAt, - ...(this.flagResolver.isEnabled('archiveProjects') - ? { archivedAt: project.archivedAt } - : {}), + archivedAt: project.archivedAt, createdAt: project.createdAt, onboardingStatus, environments, diff --git a/src/lib/features/project/project-store.e2e.test.ts b/src/lib/features/project/project-store.e2e.test.ts index 4f5c9cbdae..a338c10f82 100644 --- a/src/lib/features/project/project-store.e2e.test.ts +++ b/src/lib/features/project/project-store.e2e.test.ts @@ -12,7 +12,7 @@ let environmentStore: IEnvironmentStore; beforeAll(async () => { db = await dbInit('project_store_serial', getLogger, { - experimental: { flags: { archiveProjects: true } }, + experimental: { flags: {} }, }); stores = db.stores; projectStore = stores.projectStore; diff --git a/src/lib/features/project/project-store.ts b/src/lib/features/project/project-store.ts index 1a1bf0e174..d9f7162c65 100644 --- a/src/lib/features/project/project-store.ts +++ b/src/lib/features/project/project-store.ts @@ -132,12 +132,10 @@ class ProjectStore implements IProjectStore { .leftJoin('project_stats', 'project_stats.project', 'projects.id') .orderBy('projects.name', 'asc'); - if (this.flagResolver.isEnabled('archiveProjects')) { - if (query?.archived === true) { - projects = projects.whereNot(`${TABLE}.archived_at`, null); - } else { - projects = projects.where(`${TABLE}.archived_at`, null); - } + if (query?.archived === true) { + projects = projects.whereNot(`${TABLE}.archived_at`, null); + } else { + projects = projects.where(`${TABLE}.archived_at`, null); } if (query?.id) { @@ -154,12 +152,9 @@ class ProjectStore implements IProjectStore { 'project_settings.default_stickiness', 'project_settings.project_mode', 'project_stats.avg_time_to_prod_current_window', + 'projects.archived_at', ] as (string | Raw)[]; - if (this.flagResolver.isEnabled('archiveProjects')) { - selectColumns.push(`${TABLE}.archived_at`); - } - let groupByColumns = [ 'projects.id', 'project_settings.default_stickiness', @@ -237,9 +232,7 @@ class ProjectStore implements IProjectStore { .where(query) .orderBy('name', 'asc'); - if (this.flagResolver.isEnabled('archiveProjects')) { - projects = projects.where(`${TABLE}.archived_at`, null); - } + projects = projects.where(`${TABLE}.archived_at`, null); const rows = await projects; @@ -247,10 +240,7 @@ class ProjectStore implements IProjectStore { } async get(id: string): Promise { - let extraColumns: string[] = []; - if (this.flagResolver.isEnabled('archiveProjects')) { - extraColumns = ['archived_at']; - } + const extraColumns: string[] = ['archived_at']; return this.db .first([...COLUMNS, ...SETTINGS_COLUMNS, ...extraColumns]) @@ -634,8 +624,7 @@ class ProjectStore implements IProjectStore { async getApplicationsByProject( params: IProjectApplicationsSearchParams, ): Promise { - const { project, limit, sortOrder, sortBy, searchParams, offset } = - params; + const { project, limit, sortOrder, searchParams, offset } = params; const validatedSortOrder = sortOrder === 'asc' || sortOrder === 'desc' ? sortOrder : 'asc'; const query = this.db @@ -741,9 +730,7 @@ class ProjectStore implements IProjectStore { async count(): Promise { let count = this.db.from(TABLE).count('*'); - if (this.flagResolver.isEnabled('archiveProjects')) { - count = count.where(`${TABLE}.archived_at`, null); - } + count = count.where(`${TABLE}.archived_at`, null); return count.then((res) => Number(res[0].count)); } @@ -766,9 +753,7 @@ class ProjectStore implements IProjectStore { this.db.raw(`COALESCE(${SETTINGS_TABLE}.project_mode, 'open')`), ); - if (this.flagResolver.isEnabled('archiveProjects')) { - query = query.where(`${TABLE}.archived_at`, null); - } + query = query.where(`${TABLE}.archived_at`, null); const result: ProjectModeCount[] = await query; diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index 58512f2261..fb75b941a6 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -56,7 +56,6 @@ export type IFlagKey = | 'extendedMetrics' | 'removeUnsafeInlineStyleSrc' | 'originMiddleware' - | 'archiveProjects' | 'projectListImprovements' | 'useProjectReadModel' | 'addonUsageMetrics' @@ -281,10 +280,6 @@ const flags: IFlags = { process.env.UNLEASH_EXPERIMENTAL_ORIGIN_MIDDLEWARE, false, ), - archiveProjects: parseEnvVarBoolean( - process.env.UNLEASH_EXPERIMENTAL_ARCHIVE_PROJECTS, - false, - ), projectListImprovements: parseEnvVarBoolean( process.env.UNLEASH_EXPERIMENTAL_PROJECT_LIST_IMPROVEMENTS, false,