From f90dba25ec3126d4a6ba859dec2d6487727f05c6 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Thu, 14 Nov 2024 10:31:29 +0100 Subject: [PATCH 1/9] 1-3110: update schema and placeholders --- .../getters/useProjectStatus/useProjectStatus.ts | 3 +++ .../src/openapi/models/projectStatusSchema.ts | 5 +++++ .../openapi/spec/project-status-schema.test.ts | 3 +++ src/lib/openapi/spec/project-status-schema.ts | 16 ++++++++++++++++ 4 files changed, 27 insertions(+) diff --git a/frontend/src/hooks/api/getters/useProjectStatus/useProjectStatus.ts b/frontend/src/hooks/api/getters/useProjectStatus/useProjectStatus.ts index 8a29fbe01932..9668611b80b7 100644 --- a/frontend/src/hooks/api/getters/useProjectStatus/useProjectStatus.ts +++ b/frontend/src/hooks/api/getters/useProjectStatus/useProjectStatus.ts @@ -35,6 +35,9 @@ const placeholderData: ProjectStatusSchema = { last30Days: 0, }, }, + staleFlags: { + total: 0, + }, }; export const useProjectStatus = (projectId: string) => { diff --git a/frontend/src/openapi/models/projectStatusSchema.ts b/frontend/src/openapi/models/projectStatusSchema.ts index 87dda22ef496..f33b5f9dd258 100644 --- a/frontend/src/openapi/models/projectStatusSchema.ts +++ b/frontend/src/openapi/models/projectStatusSchema.ts @@ -22,4 +22,9 @@ export interface ProjectStatusSchema { lifecycleSummary: ProjectStatusSchemaLifecycleSummary; /** Key resources within the project */ resources: ProjectStatusSchemaResources; + /** Information on stale and potentially stale flags in this project. */ + staleFlags: { + /** The total number of flags in this project that are stale or potentially stale. */ + total: number; + }; } diff --git a/src/lib/openapi/spec/project-status-schema.test.ts b/src/lib/openapi/spec/project-status-schema.test.ts index bad54698db0f..b5db36a885da 100644 --- a/src/lib/openapi/spec/project-status-schema.test.ts +++ b/src/lib/openapi/spec/project-status-schema.test.ts @@ -36,6 +36,9 @@ test('projectStatusSchema', () => { members: 1, segments: 0, }, + staleFlags: { + total: 0, + }, }; expect( diff --git a/src/lib/openapi/spec/project-status-schema.ts b/src/lib/openapi/spec/project-status-schema.ts index 5b59f6cdb9c2..21b3ae026731 100644 --- a/src/lib/openapi/spec/project-status-schema.ts +++ b/src/lib/openapi/spec/project-status-schema.ts @@ -33,6 +33,7 @@ export const projectStatusSchema = { 'resources', 'averageHealth', 'lifecycleSummary', + 'staleFlags', ], description: 'Schema representing the overall status of a project, including an array of activity records. Each record in the activity array contains a date and a count, providing a snapshot of the project’s activity level over time.', @@ -85,6 +86,21 @@ export const projectStatusSchema = { }, }, }, + staleFlags: { + type: 'object', + additionalProperties: false, + description: + 'Information on stale and potentially stale flags in this project.', + required: ['total'], + properties: { + total: { + type: 'integer', + minimum: 0, + description: + 'The total number of flags in this project that are stale or potentially stale.', + }, + }, + }, lifecycleSummary: { type: 'object', additionalProperties: false, From e34c9df59ff67fc8a726ddc69c00aa0887c60e98 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Thu, 14 Nov 2024 10:43:04 +0100 Subject: [PATCH 2/9] 1-3110: add new query to feature toggle store --- .../fakes/fake-feature-toggle-store.ts | 4 ++++ .../feature-toggle/feature-toggle-store.ts | 12 ++++++++++++ .../types/feature-toggle-store-type.ts | 2 ++ .../project-status/project-status-service.ts | 15 ++++++++++++++- 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/lib/features/feature-toggle/fakes/fake-feature-toggle-store.ts b/src/lib/features/feature-toggle/fakes/fake-feature-toggle-store.ts index 59c3b37e16ef..9e8a2df83b7d 100644 --- a/src/lib/features/feature-toggle/fakes/fake-feature-toggle-store.ts +++ b/src/lib/features/feature-toggle/fakes/fake-feature-toggle-store.ts @@ -352,4 +352,8 @@ export default class FakeFeatureToggleStore implements IFeatureToggleStore { setCreatedByUserId(batchSize: number): Promise { throw new Error('Method not implemented.'); } + + async getStaleFlagCountForProject(): Promise { + return 0; + } } diff --git a/src/lib/features/feature-toggle/feature-toggle-store.ts b/src/lib/features/feature-toggle/feature-toggle-store.ts index fe412fb9986a..43d11b2c41e1 100644 --- a/src/lib/features/feature-toggle/feature-toggle-store.ts +++ b/src/lib/features/feature-toggle/feature-toggle-store.ts @@ -758,6 +758,18 @@ export default class FeatureToggleStore implements IFeatureToggleStore { await Promise.all(updatePromises); return toUpdate.length; } + + async getStaleFlagCountForProject(projectId: string): Promise { + const row = await this.db(TABLE) + .count() + .where({ project: projectId }) + .where((builder) => + builder + .orWhere({ stale: true }) + .orWhere({ potentially_stale: true }), + ); + return Number(row[0].count); + } } module.exports = FeatureToggleStore; diff --git a/src/lib/features/feature-toggle/types/feature-toggle-store-type.ts b/src/lib/features/feature-toggle/types/feature-toggle-store-type.ts index e083afb81db5..982c6fd9eb3a 100644 --- a/src/lib/features/feature-toggle/types/feature-toggle-store-type.ts +++ b/src/lib/features/feature-toggle/types/feature-toggle-store-type.ts @@ -105,4 +105,6 @@ export interface IFeatureToggleStore extends Store { ): Promise; setCreatedByUserId(batchSize: number): Promise; + + getStaleFlagCountForProject(projectId: string): Promise; } diff --git a/src/lib/features/project-status/project-status-service.ts b/src/lib/features/project-status/project-status-service.ts index 2900915654e4..40969f3c5e37 100644 --- a/src/lib/features/project-status/project-status-service.ts +++ b/src/lib/features/project-status/project-status-service.ts @@ -2,6 +2,7 @@ import type { ProjectStatusSchema } from '../../openapi'; import type { IApiTokenStore, IEventStore, + IFeatureToggleStore, IProjectStore, ISegmentStore, IUnleashStores, @@ -16,6 +17,7 @@ export class ProjectStatusService { private segmentStore: ISegmentStore; private personalDashboardReadModel: IPersonalDashboardReadModel; private projectLifecycleSummaryReadModel: IProjectLifecycleSummaryReadModel; + private featureToggleStore: IFeatureToggleStore; constructor( { @@ -23,9 +25,14 @@ export class ProjectStatusService { projectStore, apiTokenStore, segmentStore, + featureToggleStore, }: Pick< IUnleashStores, - 'eventStore' | 'projectStore' | 'apiTokenStore' | 'segmentStore' + | 'eventStore' + | 'projectStore' + | 'apiTokenStore' + | 'segmentStore' + | 'featureToggleStore' >, personalDashboardReadModel: IPersonalDashboardReadModel, projectLifecycleReadModel: IProjectLifecycleSummaryReadModel, @@ -36,6 +43,7 @@ export class ProjectStatusService { this.segmentStore = segmentStore; this.personalDashboardReadModel = personalDashboardReadModel; this.projectLifecycleSummaryReadModel = projectLifecycleReadModel; + this.featureToggleStore = featureToggleStore; } async getProjectStatus(projectId: string): Promise { @@ -47,6 +55,7 @@ export class ProjectStatusService { activityCountByDate, healthScores, lifecycleSummary, + staleFlagCount, ] = await Promise.all([ this.projectStore.getConnectedEnvironmentCountForProject(projectId), this.projectStore.getMembersCountByProject(projectId), @@ -57,6 +66,7 @@ export class ProjectStatusService { this.projectLifecycleSummaryReadModel.getProjectLifecycleSummary( projectId, ), + this.featureToggleStore.getStaleFlagCountForProject(projectId), ]); const averageHealth = healthScores.length @@ -74,6 +84,9 @@ export class ProjectStatusService { activityCountByDate, averageHealth: Math.round(averageHealth), lifecycleSummary, + staleFlags: { + total: staleFlagCount, + }, }; } } From a6f4b9a280385040b370926a821f5ab9da26b09b Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Thu, 14 Nov 2024 10:49:52 +0100 Subject: [PATCH 3/9] 1-3110: don't touch feature toggle stoe --- .../fakes/fake-feature-toggle-store.ts | 4 --- .../feature-toggle/feature-toggle-store.ts | 12 --------- .../types/feature-toggle-store-type.ts | 2 -- .../project-status/getStaleFlagsForProject.ts | 25 +++++++++++++++++++ .../project-status/project-status-service.ts | 16 +++++------- 5 files changed, 31 insertions(+), 28 deletions(-) create mode 100644 src/lib/features/project-status/getStaleFlagsForProject.ts diff --git a/src/lib/features/feature-toggle/fakes/fake-feature-toggle-store.ts b/src/lib/features/feature-toggle/fakes/fake-feature-toggle-store.ts index 9e8a2df83b7d..59c3b37e16ef 100644 --- a/src/lib/features/feature-toggle/fakes/fake-feature-toggle-store.ts +++ b/src/lib/features/feature-toggle/fakes/fake-feature-toggle-store.ts @@ -352,8 +352,4 @@ export default class FakeFeatureToggleStore implements IFeatureToggleStore { setCreatedByUserId(batchSize: number): Promise { throw new Error('Method not implemented.'); } - - async getStaleFlagCountForProject(): Promise { - return 0; - } } diff --git a/src/lib/features/feature-toggle/feature-toggle-store.ts b/src/lib/features/feature-toggle/feature-toggle-store.ts index 43d11b2c41e1..fe412fb9986a 100644 --- a/src/lib/features/feature-toggle/feature-toggle-store.ts +++ b/src/lib/features/feature-toggle/feature-toggle-store.ts @@ -758,18 +758,6 @@ export default class FeatureToggleStore implements IFeatureToggleStore { await Promise.all(updatePromises); return toUpdate.length; } - - async getStaleFlagCountForProject(projectId: string): Promise { - const row = await this.db(TABLE) - .count() - .where({ project: projectId }) - .where((builder) => - builder - .orWhere({ stale: true }) - .orWhere({ potentially_stale: true }), - ); - return Number(row[0].count); - } } module.exports = FeatureToggleStore; diff --git a/src/lib/features/feature-toggle/types/feature-toggle-store-type.ts b/src/lib/features/feature-toggle/types/feature-toggle-store-type.ts index 982c6fd9eb3a..e083afb81db5 100644 --- a/src/lib/features/feature-toggle/types/feature-toggle-store-type.ts +++ b/src/lib/features/feature-toggle/types/feature-toggle-store-type.ts @@ -105,6 +105,4 @@ export interface IFeatureToggleStore extends Store { ): Promise; setCreatedByUserId(batchSize: number): Promise; - - getStaleFlagCountForProject(projectId: string): Promise; } diff --git a/src/lib/features/project-status/getStaleFlagsForProject.ts b/src/lib/features/project-status/getStaleFlagsForProject.ts new file mode 100644 index 000000000000..ded0331a730a --- /dev/null +++ b/src/lib/features/project-status/getStaleFlagsForProject.ts @@ -0,0 +1,25 @@ +import type { Db } from '../../server-impl'; + +export type GetStaleFlagsForProject = (projectId: string) => Promise; + +export const createGetStaleFlagsForProject = + (db: Db): GetStaleFlagsForProject => + async (projectId) => { + const result = await db('feature_toggles') + .count() + .where({ project: projectId }) + .where((builder) => + builder + .orWhere({ stale: true }) + .orWhere({ potentially_stale: true }), + ); + + return Number(result[0].count); + }; + +export const createFakeGetStaleFlagsForProject = + ( + staleFlags: Awaited> = 0, + ): GetStaleFlagsForProject => + () => + Promise.resolve(staleFlags); diff --git a/src/lib/features/project-status/project-status-service.ts b/src/lib/features/project-status/project-status-service.ts index 40969f3c5e37..ca17ffb0ac3c 100644 --- a/src/lib/features/project-status/project-status-service.ts +++ b/src/lib/features/project-status/project-status-service.ts @@ -2,12 +2,12 @@ import type { ProjectStatusSchema } from '../../openapi'; import type { IApiTokenStore, IEventStore, - IFeatureToggleStore, IProjectStore, ISegmentStore, IUnleashStores, } from '../../types'; import type { IPersonalDashboardReadModel } from '../personal-dashboard/personal-dashboard-read-model-type'; +import type { GetStaleFlagsForProject } from './getStaleFlagsForProject'; import type { IProjectLifecycleSummaryReadModel } from './project-lifecycle-read-model/project-lifecycle-read-model-type'; export class ProjectStatusService { @@ -17,7 +17,7 @@ export class ProjectStatusService { private segmentStore: ISegmentStore; private personalDashboardReadModel: IPersonalDashboardReadModel; private projectLifecycleSummaryReadModel: IProjectLifecycleSummaryReadModel; - private featureToggleStore: IFeatureToggleStore; + private getStaleFlagsForProject: GetStaleFlagsForProject; constructor( { @@ -25,17 +25,13 @@ export class ProjectStatusService { projectStore, apiTokenStore, segmentStore, - featureToggleStore, }: Pick< IUnleashStores, - | 'eventStore' - | 'projectStore' - | 'apiTokenStore' - | 'segmentStore' - | 'featureToggleStore' + 'eventStore' | 'projectStore' | 'apiTokenStore' | 'segmentStore' >, personalDashboardReadModel: IPersonalDashboardReadModel, projectLifecycleReadModel: IProjectLifecycleSummaryReadModel, + getStaleFlagsForProject: GetStaleFlagsForProject, ) { this.eventStore = eventStore; this.projectStore = projectStore; @@ -43,7 +39,7 @@ export class ProjectStatusService { this.segmentStore = segmentStore; this.personalDashboardReadModel = personalDashboardReadModel; this.projectLifecycleSummaryReadModel = projectLifecycleReadModel; - this.featureToggleStore = featureToggleStore; + this.getStaleFlagsForProject = getStaleFlagsForProject; } async getProjectStatus(projectId: string): Promise { @@ -66,7 +62,7 @@ export class ProjectStatusService { this.projectLifecycleSummaryReadModel.getProjectLifecycleSummary( projectId, ), - this.featureToggleStore.getStaleFlagCountForProject(projectId), + this.getStaleFlagsForProject(projectId), ]); const averageHealth = healthScores.length From d15a2700ebc74e72a6d2a49f6ea3b2e2d373c267 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Thu, 14 Nov 2024 10:51:15 +0100 Subject: [PATCH 4/9] 1-3110: update project status service creation --- .../features/project-status/createProjectStatusService.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib/features/project-status/createProjectStatusService.ts b/src/lib/features/project-status/createProjectStatusService.ts index 9b85c3196577..91f4a5b04dfa 100644 --- a/src/lib/features/project-status/createProjectStatusService.ts +++ b/src/lib/features/project-status/createProjectStatusService.ts @@ -14,6 +14,10 @@ import { createFakeProjectLifecycleSummaryReadModel, createProjectLifecycleSummaryReadModel, } from './project-lifecycle-read-model/createProjectLifecycleSummaryReadModel'; +import { + createFakeGetStaleFlagsForProject, + createGetStaleFlagsForProject, +} from './getStaleFlagsForProject'; export const createProjectStatusService = ( db: Db, @@ -40,6 +44,7 @@ export const createProjectStatusService = ( ); const projectLifecycleSummaryReadModel = createProjectLifecycleSummaryReadModel(db, config); + const getStaleFlagsForProject = createGetStaleFlagsForProject(db); return new ProjectStatusService( { @@ -50,6 +55,7 @@ export const createProjectStatusService = ( }, new PersonalDashboardReadModel(db), projectLifecycleSummaryReadModel, + getStaleFlagsForProject, ); }; @@ -58,6 +64,7 @@ export const createFakeProjectStatusService = () => { const projectStore = new FakeProjectStore(); const apiTokenStore = new FakeApiTokenStore(); const segmentStore = new FakeSegmentStore(); + const getStaleFlagsForProject = createFakeGetStaleFlagsForProject(); const projectStatusService = new ProjectStatusService( { eventStore, @@ -67,6 +74,7 @@ export const createFakeProjectStatusService = () => { }, new FakePersonalDashboardReadModel(), createFakeProjectLifecycleSummaryReadModel(), + getStaleFlagsForProject, ); return { From fc657f3676ea9fad4bcf69ef8a3bc2f544bc0690 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Thu, 14 Nov 2024 11:13:01 +0100 Subject: [PATCH 5/9] 1-3110: test query --- .../project-status/getStaleFlagsForProject.ts | 4 +- .../projects-status.e2e.test.ts | 43 +++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/lib/features/project-status/getStaleFlagsForProject.ts b/src/lib/features/project-status/getStaleFlagsForProject.ts index ded0331a730a..5f3728a6af99 100644 --- a/src/lib/features/project-status/getStaleFlagsForProject.ts +++ b/src/lib/features/project-status/getStaleFlagsForProject.ts @@ -5,9 +5,9 @@ export type GetStaleFlagsForProject = (projectId: string) => Promise; export const createGetStaleFlagsForProject = (db: Db): GetStaleFlagsForProject => async (projectId) => { - const result = await db('feature_toggles') + const result = await db('features') .count() - .where({ project: projectId }) + .where({ project: projectId, archived: false }) .where((builder) => builder .orWhere({ stale: true }) diff --git a/src/lib/features/project-status/projects-status.e2e.test.ts b/src/lib/features/project-status/projects-status.e2e.test.ts index 85ea0faffa8e..d2fde4527c7d 100644 --- a/src/lib/features/project-status/projects-status.e2e.test.ts +++ b/src/lib/features/project-status/projects-status.e2e.test.ts @@ -6,6 +6,7 @@ import { import getLogger from '../../../test/fixtures/no-logger'; import { FEATURE_CREATED, + type IUser, RoleName, type IAuditUser, type IUnleashConfig, @@ -300,3 +301,45 @@ test('project status contains lifecycle data', async () => { }, }); }); + +test('project status includes stale flags', async () => { + const otherProject = await app.services.projectService.createProject( + { + name: 'otherProject', + id: randomId(), + }, + {} as IUser, + {} as IAuditUser, + ); + const createFlag = app.createFeature; + + const updateFlag = async (name: string, update: any) => + await db.rawDatabase('features').update(update).where({ name }); + + await createFlag('stale-flag'); + await createFlag('potentially-stale-flag'); + await createFlag('potentially-stale-and-stale-flag'); + await createFlag('non-stale-flag'); + await createFlag('archived-stale-flag'); + await createFlag('stale-other-project', otherProject.id); + + await updateFlag('stale-flag', { stale: true }); + await updateFlag('potentially-stale-flag', { + potentially_stale: true, + }); + await updateFlag('potentially-stale-and-stale-flag', { + potentially_stale: true, + stale: true, + }); + await updateFlag('archived-stale-flag', { archived: true, stale: true }); + await updateFlag('stale-other-project', { stale: true }); + + const { body } = await app.request + .get('/api/admin/projects/default/status') + .expect('Content-Type', /json/) + .expect(200); + + expect(body.staleFlags).toMatchObject({ + total: 3, + }); +}); From 2cbc683fe9c0ef0d38905ad7fdca2778920df0f6 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Thu, 14 Nov 2024 11:17:44 +0100 Subject: [PATCH 6/9] 1-3110: update test --- .../projects-status.e2e.test.ts | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/lib/features/project-status/projects-status.e2e.test.ts b/src/lib/features/project-status/projects-status.e2e.test.ts index d2fde4527c7d..e3b1f2697bde 100644 --- a/src/lib/features/project-status/projects-status.e2e.test.ts +++ b/src/lib/features/project-status/projects-status.e2e.test.ts @@ -311,28 +311,35 @@ test('project status includes stale flags', async () => { {} as IUser, {} as IAuditUser, ); - const createFlag = app.createFeature; - - const updateFlag = async (name: string, update: any) => - await db.rawDatabase('features').update(update).where({ name }); - - await createFlag('stale-flag'); - await createFlag('potentially-stale-flag'); - await createFlag('potentially-stale-and-stale-flag'); - await createFlag('non-stale-flag'); - await createFlag('archived-stale-flag'); - await createFlag('stale-other-project', otherProject.id); + const createFlagInState = async ( + name: string, + state?: Object, + projectId?: string, + ) => { + await app.createFeature(name, projectId); + if (state) { + await db.rawDatabase('features').update(state).where({ name }); + } + }; - await updateFlag('stale-flag', { stale: true }); - await updateFlag('potentially-stale-flag', { + await createFlagInState('stale-flag', { stale: true }); + await createFlagInState('potentially-stale-flag', { potentially_stale: true, }); - await updateFlag('potentially-stale-and-stale-flag', { + await createFlagInState('potentially-stale-and-stale-flag', { potentially_stale: true, stale: true, }); - await updateFlag('archived-stale-flag', { archived: true, stale: true }); - await updateFlag('stale-other-project', { stale: true }); + await createFlagInState('non-stale-flag'); + await createFlagInState('archived-stale-flag', { + archived: true, + stale: true, + }); + await createFlagInState( + 'stale-other-project', + { stale: true }, + otherProject.id, + ); const { body } = await app.request .get('/api/admin/projects/default/status') From acc716940018797a5a95c2d05e7dd0d5ba3de897 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Thu, 14 Nov 2024 12:45:46 +0100 Subject: [PATCH 7/9] Switch to read model instead of curried function 1-3110: use read model instead 1-3110: begin rename 1-3110: put things in own folder 1-3110: fix file rename 1-3110: finish switch 1-3110: fix missing imports --- .../createProjectStatusService.ts | 13 ++++------ .../project-status/getStaleFlagsForProject.ts | 25 ------------------- .../fake-project-stale-flags-read-model.ts | 9 +++++++ .../project-stale-flags-read-model-type.ts | 3 +++ .../project-stale-flags-read-model.ts | 19 ++++++++++++++ .../project-status/project-status-service.ts | 12 +++++---- 6 files changed, 43 insertions(+), 38 deletions(-) delete mode 100644 src/lib/features/project-status/getStaleFlagsForProject.ts create mode 100644 src/lib/features/project-status/project-stale-flags-read-model/fake-project-stale-flags-read-model.ts create mode 100644 src/lib/features/project-status/project-stale-flags-read-model/project-stale-flags-read-model-type.ts create mode 100644 src/lib/features/project-status/project-stale-flags-read-model/project-stale-flags-read-model.ts diff --git a/src/lib/features/project-status/createProjectStatusService.ts b/src/lib/features/project-status/createProjectStatusService.ts index 91f4a5b04dfa..0c5c02463c8b 100644 --- a/src/lib/features/project-status/createProjectStatusService.ts +++ b/src/lib/features/project-status/createProjectStatusService.ts @@ -14,10 +14,8 @@ import { createFakeProjectLifecycleSummaryReadModel, createProjectLifecycleSummaryReadModel, } from './project-lifecycle-read-model/createProjectLifecycleSummaryReadModel'; -import { - createFakeGetStaleFlagsForProject, - createGetStaleFlagsForProject, -} from './getStaleFlagsForProject'; +import { ProjectStaleFlagsReadModel } from './project-stale-flags-read-model/project-stale-flags-read-model'; +import { FakeProjectStaleFlagsReadModel } from './project-stale-flags-read-model/fake-project-stale-flags-read-model'; export const createProjectStatusService = ( db: Db, @@ -44,7 +42,7 @@ export const createProjectStatusService = ( ); const projectLifecycleSummaryReadModel = createProjectLifecycleSummaryReadModel(db, config); - const getStaleFlagsForProject = createGetStaleFlagsForProject(db); + const projectStaleFlagsReadModel = new ProjectStaleFlagsReadModel(db); return new ProjectStatusService( { @@ -55,7 +53,7 @@ export const createProjectStatusService = ( }, new PersonalDashboardReadModel(db), projectLifecycleSummaryReadModel, - getStaleFlagsForProject, + projectStaleFlagsReadModel, ); }; @@ -64,7 +62,6 @@ export const createFakeProjectStatusService = () => { const projectStore = new FakeProjectStore(); const apiTokenStore = new FakeApiTokenStore(); const segmentStore = new FakeSegmentStore(); - const getStaleFlagsForProject = createFakeGetStaleFlagsForProject(); const projectStatusService = new ProjectStatusService( { eventStore, @@ -74,7 +71,7 @@ export const createFakeProjectStatusService = () => { }, new FakePersonalDashboardReadModel(), createFakeProjectLifecycleSummaryReadModel(), - getStaleFlagsForProject, + new FakeProjectStaleFlagsReadModel(), ); return { diff --git a/src/lib/features/project-status/getStaleFlagsForProject.ts b/src/lib/features/project-status/getStaleFlagsForProject.ts deleted file mode 100644 index 5f3728a6af99..000000000000 --- a/src/lib/features/project-status/getStaleFlagsForProject.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { Db } from '../../server-impl'; - -export type GetStaleFlagsForProject = (projectId: string) => Promise; - -export const createGetStaleFlagsForProject = - (db: Db): GetStaleFlagsForProject => - async (projectId) => { - const result = await db('features') - .count() - .where({ project: projectId, archived: false }) - .where((builder) => - builder - .orWhere({ stale: true }) - .orWhere({ potentially_stale: true }), - ); - - return Number(result[0].count); - }; - -export const createFakeGetStaleFlagsForProject = - ( - staleFlags: Awaited> = 0, - ): GetStaleFlagsForProject => - () => - Promise.resolve(staleFlags); diff --git a/src/lib/features/project-status/project-stale-flags-read-model/fake-project-stale-flags-read-model.ts b/src/lib/features/project-status/project-stale-flags-read-model/fake-project-stale-flags-read-model.ts new file mode 100644 index 000000000000..74bce1c16667 --- /dev/null +++ b/src/lib/features/project-status/project-stale-flags-read-model/fake-project-stale-flags-read-model.ts @@ -0,0 +1,9 @@ +import type { IProjectStaleFlagsReadModel } from './project-stale-flags-read-model-type'; + +export class FakeProjectStaleFlagsReadModel + implements IProjectStaleFlagsReadModel +{ + async getStaleFlagCountForProject(): Promise { + return 0; + } +} diff --git a/src/lib/features/project-status/project-stale-flags-read-model/project-stale-flags-read-model-type.ts b/src/lib/features/project-status/project-stale-flags-read-model/project-stale-flags-read-model-type.ts new file mode 100644 index 000000000000..e8e68af9a259 --- /dev/null +++ b/src/lib/features/project-status/project-stale-flags-read-model/project-stale-flags-read-model-type.ts @@ -0,0 +1,3 @@ +export interface IProjectStaleFlagsReadModel { + getStaleFlagCountForProject: (projectId: string) => Promise; +} diff --git a/src/lib/features/project-status/project-stale-flags-read-model/project-stale-flags-read-model.ts b/src/lib/features/project-status/project-stale-flags-read-model/project-stale-flags-read-model.ts new file mode 100644 index 000000000000..bc0a4c56123a --- /dev/null +++ b/src/lib/features/project-status/project-stale-flags-read-model/project-stale-flags-read-model.ts @@ -0,0 +1,19 @@ +import type { Db } from '../../../server-impl'; +import type { IProjectStaleFlagsReadModel } from './project-stale-flags-read-model-type'; + +export class ProjectStaleFlagsReadModel implements IProjectStaleFlagsReadModel { + constructor(private db: Db) {} + + async getStaleFlagCountForProject(projectId: string): Promise { + const result = await this.db('features') + .count() + .where({ project: projectId, archived: false }) + .where((builder) => + builder + .orWhere({ stale: true }) + .orWhere({ potentially_stale: true }), + ); + + return Number(result[0].count); + } +} diff --git a/src/lib/features/project-status/project-status-service.ts b/src/lib/features/project-status/project-status-service.ts index ca17ffb0ac3c..3a3bb0fc12c7 100644 --- a/src/lib/features/project-status/project-status-service.ts +++ b/src/lib/features/project-status/project-status-service.ts @@ -7,7 +7,7 @@ import type { IUnleashStores, } from '../../types'; import type { IPersonalDashboardReadModel } from '../personal-dashboard/personal-dashboard-read-model-type'; -import type { GetStaleFlagsForProject } from './getStaleFlagsForProject'; +import type { IProjectStaleFlagsReadModel } from './IProjectStaleFlagsReadModel'; import type { IProjectLifecycleSummaryReadModel } from './project-lifecycle-read-model/project-lifecycle-read-model-type'; export class ProjectStatusService { @@ -17,7 +17,7 @@ export class ProjectStatusService { private segmentStore: ISegmentStore; private personalDashboardReadModel: IPersonalDashboardReadModel; private projectLifecycleSummaryReadModel: IProjectLifecycleSummaryReadModel; - private getStaleFlagsForProject: GetStaleFlagsForProject; + private projectStaleFlagsReadModel: IProjectStaleFlagsReadModel; constructor( { @@ -31,7 +31,7 @@ export class ProjectStatusService { >, personalDashboardReadModel: IPersonalDashboardReadModel, projectLifecycleReadModel: IProjectLifecycleSummaryReadModel, - getStaleFlagsForProject: GetStaleFlagsForProject, + projectStaleFlagsReadModel: IProjectStaleFlagsReadModel, ) { this.eventStore = eventStore; this.projectStore = projectStore; @@ -39,7 +39,7 @@ export class ProjectStatusService { this.segmentStore = segmentStore; this.personalDashboardReadModel = personalDashboardReadModel; this.projectLifecycleSummaryReadModel = projectLifecycleReadModel; - this.getStaleFlagsForProject = getStaleFlagsForProject; + this.projectStaleFlagsReadModel = projectStaleFlagsReadModel; } async getProjectStatus(projectId: string): Promise { @@ -62,7 +62,9 @@ export class ProjectStatusService { this.projectLifecycleSummaryReadModel.getProjectLifecycleSummary( projectId, ), - this.getStaleFlagsForProject(projectId), + this.projectStaleFlagsReadModel.getStaleFlagCountForProject( + projectId, + ), ]); const averageHealth = healthScores.length From 1ecae906a8295fdcf56de59703b531ca0b1087d3 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Thu, 14 Nov 2024 15:02:02 +0100 Subject: [PATCH 8/9] 1-3110: make linter happy --- website/prepare-generated-docs.mjs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/website/prepare-generated-docs.mjs b/website/prepare-generated-docs.mjs index 20a46ffc31ac..c7e461b895a4 100644 --- a/website/prepare-generated-docs.mjs +++ b/website/prepare-generated-docs.mjs @@ -29,24 +29,20 @@ if (!response.ok) { const data = await response.json(); -data.servers = [{ - url: '', -}]; +data.servers = [ + { + url: '', + }, +]; -const outputDir = './docs/generated/' +const outputDir = './docs/generated/'; // Write the JSON to file -const outputPath = path.join(outputDir, 'openapi.json') +const outputPath = path.join(outputDir, 'openapi.json'); // Ensure directory exists await fs.mkdir(outputDir, { recursive: true }); -await fs.writeFile( - outputPath, - JSON.stringify(data, null, 2), - 'utf8' -); +await fs.writeFile(outputPath, JSON.stringify(data, null, 2), 'utf8'); console.log(`OpenAPI spec saved to ${outputPath}`); - - From 8235d7d12c8990e310116c717d63d4026aa9780e Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Thu, 14 Nov 2024 15:06:00 +0100 Subject: [PATCH 9/9] 1-3110: fix import --- src/lib/features/project-status/project-status-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/features/project-status/project-status-service.ts b/src/lib/features/project-status/project-status-service.ts index 3a3bb0fc12c7..aae6c02fc36c 100644 --- a/src/lib/features/project-status/project-status-service.ts +++ b/src/lib/features/project-status/project-status-service.ts @@ -7,8 +7,8 @@ import type { IUnleashStores, } from '../../types'; import type { IPersonalDashboardReadModel } from '../personal-dashboard/personal-dashboard-read-model-type'; -import type { IProjectStaleFlagsReadModel } from './IProjectStaleFlagsReadModel'; import type { IProjectLifecycleSummaryReadModel } from './project-lifecycle-read-model/project-lifecycle-read-model-type'; +import type { IProjectStaleFlagsReadModel } from './project-stale-flags-read-model/project-stale-flags-read-model-type'; export class ProjectStatusService { private eventStore: IEventStore;