From d4f52cdb540d8c1fe59dc442ef523ffe2eb0e464 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Mon, 25 Mar 2024 14:44:32 +0100 Subject: [PATCH] refactor: remove change requests from project insights api (#6685) --- .../ChangeRequests/ChangeRequests.test.tsx | 34 +++++----- .../ChangeRequests/ChangeRequests.tsx | 10 ++- .../ProjectInsights/ProjectInsights.tsx | 4 +- .../Project/ProjectOverviewChangeRequests.tsx | 2 +- .../useProjectInsights/useProjectInsights.ts | 8 --- frontend/src/openapi/models/index.ts | 1 - .../openapi/models/projectInsightsSchema.ts | 3 - .../projectInsightsSchemaChangeRequests.ts | 23 ------- .../createProjectInsightsService.ts | 39 ++++------- .../fake-project-insights-read-model.ts | 25 ------- .../project-insights-read-model-type.ts | 12 ---- .../project-insights-read-model.test.ts | 66 ------------------- .../project-insights-read-model.ts | 61 ----------------- .../project-insights-service.test.ts | 17 ----- .../project-insights-service.ts | 65 +++++++----------- .../projects-insights.e2e.test.ts | 8 --- .../openapi/spec/project-insights-schema.ts | 47 ------------- 17 files changed, 62 insertions(+), 363 deletions(-) delete mode 100644 frontend/src/openapi/models/projectInsightsSchemaChangeRequests.ts delete mode 100644 src/lib/features/project-insights/fake-project-insights-read-model.ts delete mode 100644 src/lib/features/project-insights/project-insights-read-model-type.ts delete mode 100644 src/lib/features/project-insights/project-insights-read-model.test.ts delete mode 100644 src/lib/features/project-insights/project-insights-read-model.ts diff --git a/frontend/src/component/project/Project/ProjectInsights/ChangeRequests/ChangeRequests.test.tsx b/frontend/src/component/project/Project/ProjectInsights/ChangeRequests/ChangeRequests.test.tsx index f3c28f1747..c7abfec409 100644 --- a/frontend/src/component/project/Project/ProjectInsights/ChangeRequests/ChangeRequests.test.tsx +++ b/frontend/src/component/project/Project/ProjectInsights/ChangeRequests/ChangeRequests.test.tsx @@ -12,6 +12,18 @@ const setupEnterpriseApi = () => { current: { enterprise: 'present' }, }, }); + testServerRoute( + server, + '/api/admin/projects/default/change-requests/count', + { + total: 14, + approved: 2, + applied: 0, + rejected: 0, + reviewRequired: 10, + scheduled: 2, + }, + ); }; const setupOssApi = () => { @@ -22,23 +34,11 @@ const setupOssApi = () => { }); }; -const changeRequests = { - applied: 0, - total: 0, - approved: 0, - scheduled: 0, - reviewRequired: 0, - rejected: 0, -}; - test('Show enterprise hints', async () => { setupOssApi(); render( - } - /> + } /> , { route: '/projects/default', @@ -52,10 +52,7 @@ test('Show change requests info', async () => { setupEnterpriseApi(); render( - } - /> + } /> , { route: '/projects/default', @@ -63,4 +60,7 @@ test('Show change requests info', async () => { ); await screen.findByText('To be applied'); + await screen.findByText('10'); + await screen.findByText('4'); + await screen.findByText('14'); }); diff --git a/frontend/src/component/project/Project/ProjectInsights/ChangeRequests/ChangeRequests.tsx b/frontend/src/component/project/Project/ProjectInsights/ChangeRequests/ChangeRequests.tsx index 4b321a2463..ff25afb72e 100644 --- a/frontend/src/component/project/Project/ProjectInsights/ChangeRequests/ChangeRequests.tsx +++ b/frontend/src/component/project/Project/ProjectInsights/ChangeRequests/ChangeRequests.tsx @@ -4,8 +4,7 @@ import { Link } from 'react-router-dom'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import { PremiumFeature } from 'component/common/PremiumFeature/PremiumFeature'; -import type { ProjectInsightsSchemaChangeRequests } from '../../../../../openapi'; -import type { FC } from 'react'; +import { useChangeRequestsCount } from 'hooks/api/getters/useChangeRequestsCount/useChangeRequestsCount'; const Container = styled(Box)(({ theme }) => ({ display: 'flex', @@ -83,14 +82,13 @@ const BigNumber = styled(Typography)(({ theme }) => ({ color: theme.palette.text.primary, })); -export const ChangeRequests: FC<{ - changeRequests: ProjectInsightsSchemaChangeRequests; -}> = ({ changeRequests }) => { +export const ChangeRequests = () => { const projectId = useRequiredPathParam('projectId'); const { isOss, isPro } = useUiConfig(); + const { data } = useChangeRequestsCount(projectId); const { total, applied, rejected, reviewRequired, scheduled, approved } = - changeRequests; + data; const toBeApplied = scheduled + approved; if (isOss() || isPro()) { diff --git a/frontend/src/component/project/Project/ProjectInsights/ProjectInsights.tsx b/frontend/src/component/project/Project/ProjectInsights/ProjectInsights.tsx index 78034b9112..de89803c4c 100644 --- a/frontend/src/component/project/Project/ProjectInsights/ProjectInsights.tsx +++ b/frontend/src/component/project/Project/ProjectInsights/ProjectInsights.tsx @@ -61,9 +61,7 @@ export const ProjectInsights = () => { - {data.changeRequests && ( - - )} + ); diff --git a/frontend/src/component/project/Project/ProjectOverviewChangeRequests.tsx b/frontend/src/component/project/Project/ProjectOverviewChangeRequests.tsx index 4c97bd3c4d..702c737adb 100644 --- a/frontend/src/component/project/Project/ProjectOverviewChangeRequests.tsx +++ b/frontend/src/component/project/Project/ProjectOverviewChangeRequests.tsx @@ -48,7 +48,7 @@ export const ProjectOverviewChangeRequests: FC<{ project: string }> = ({ useChangeRequestsEnabled(project); const { data } = useChangeRequestsCount(project); - if (!isChangeRequestConfiguredInAnyEnv) { + if (!isChangeRequestConfiguredInAnyEnv()) { return null; } diff --git a/frontend/src/hooks/api/getters/useProjectInsights/useProjectInsights.ts b/frontend/src/hooks/api/getters/useProjectInsights/useProjectInsights.ts index 89d6274287..fdb43ac4a5 100644 --- a/frontend/src/hooks/api/getters/useProjectInsights/useProjectInsights.ts +++ b/frontend/src/hooks/api/getters/useProjectInsights/useProjectInsights.ts @@ -49,14 +49,6 @@ const placeholderData: ProjectInsightsSchema = { currentMembers: 0, change: 0, }, - changeRequests: { - total: 0, - applied: 0, - approved: 0, - rejected: 0, - reviewRequired: 0, - scheduled: 0, - }, }; export const useProjectInsights = (projectId: string) => { diff --git a/frontend/src/openapi/models/index.ts b/frontend/src/openapi/models/index.ts index 0e91fcaff1..3ddae3fa96 100644 --- a/frontend/src/openapi/models/index.ts +++ b/frontend/src/openapi/models/index.ts @@ -879,7 +879,6 @@ export * from './projectCreatedSchemaMode'; export * from './projectDoraMetricsSchema'; export * from './projectEnvironmentSchema'; export * from './projectInsightsSchema'; -export * from './projectInsightsSchemaChangeRequests'; export * from './projectInsightsSchemaHealth'; export * from './projectInsightsSchemaMembers'; export * from './projectOverviewSchema'; diff --git a/frontend/src/openapi/models/projectInsightsSchema.ts b/frontend/src/openapi/models/projectInsightsSchema.ts index d85187bff6..95246e6b23 100644 --- a/frontend/src/openapi/models/projectInsightsSchema.ts +++ b/frontend/src/openapi/models/projectInsightsSchema.ts @@ -3,7 +3,6 @@ * Do not edit manually. * See `gen:api` script in package.json */ -import type { ProjectInsightsSchemaChangeRequests } from './projectInsightsSchemaChangeRequests'; import type { FeatureTypeCountSchema } from './featureTypeCountSchema'; import type { ProjectInsightsSchemaHealth } from './projectInsightsSchemaHealth'; import type { ProjectDoraMetricsSchema } from './projectDoraMetricsSchema'; @@ -14,8 +13,6 @@ import type { ProjectStatsSchema } from './projectStatsSchema'; * A high-level overview of a project insights. It contains information such as project statistics, overall health, types of flags, members overview, change requests overview. */ export interface ProjectInsightsSchema { - /** Count of change requests in different stages of the [process](https://docs.getunleash.io/reference/change-requests#change-request-flow). Only for enterprise users. */ - changeRequests?: ProjectInsightsSchemaChangeRequests; /** The number of features of each type */ featureTypeCounts: FeatureTypeCountSchema[]; /** Health summary of the project */ diff --git a/frontend/src/openapi/models/projectInsightsSchemaChangeRequests.ts b/frontend/src/openapi/models/projectInsightsSchemaChangeRequests.ts deleted file mode 100644 index 5ce8e17e6b..0000000000 --- a/frontend/src/openapi/models/projectInsightsSchemaChangeRequests.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Generated by Orval - * Do not edit manually. - * See `gen:api` script in package.json - */ - -/** - * Count of change requests in different stages of the [process](https://docs.getunleash.io/reference/change-requests#change-request-flow). Only for enterprise users. - */ -export type ProjectInsightsSchemaChangeRequests = { - /** The number of applied change requests */ - applied: number; - /** The number of approved change requests */ - approved: number; - /** The number of rejected change requests */ - rejected: number; - /** The number of change requests awaiting the review */ - reviewRequired: number; - /** The number of scheduled change requests */ - scheduled: number; - /** The number of total change requests in this project */ - total: number; -}; diff --git a/src/lib/features/project-insights/createProjectInsightsService.ts b/src/lib/features/project-insights/createProjectInsightsService.ts index 8793d4ec68..68b46ac394 100644 --- a/src/lib/features/project-insights/createProjectInsightsService.ts +++ b/src/lib/features/project-insights/createProjectInsightsService.ts @@ -8,8 +8,6 @@ import FeatureTypeStore from '../../db/feature-type-store'; import FakeFeatureTypeStore from '../../../test/fixtures/fake-feature-type-store'; import { ProjectInsightsService } from './project-insights-service'; import ProjectStore from '../project/project-store'; -import { ProjectInsightsReadModel } from './project-insights-read-model'; -import { FakeProjectInsightsReadModel } from './fake-project-insights-read-model'; import FeatureStrategiesStore from '../feature-toggle/feature-toggle-strategies-store'; import FakeFeatureStrategiesStore from '../feature-toggle/fakes/fake-feature-strategies-store'; @@ -39,18 +37,14 @@ export const createProjectInsightsService = ( getLogger, flagResolver, ); - const projectInsightsReadModel = new ProjectInsightsReadModel(db); - return new ProjectInsightsService( - { - projectStore, - featureToggleStore, - featureTypeStore, - projectStatsStore, - featureStrategiesStore, - }, - projectInsightsReadModel, - ); + return new ProjectInsightsService({ + projectStore, + featureToggleStore, + featureTypeStore, + projectStatsStore, + featureStrategiesStore, + }); }; export const createFakeProjectInsightsService = () => { @@ -59,23 +53,18 @@ export const createFakeProjectInsightsService = () => { const featureTypeStore = new FakeFeatureTypeStore(); const projectStatsStore = new FakeProjectStatsStore(); const featureStrategiesStore = new FakeFeatureStrategiesStore(); - const projectInsightsReadModel = new FakeProjectInsightsReadModel(); - const projectInsightsService = new ProjectInsightsService( - { - projectStore, - featureToggleStore, - featureTypeStore, - projectStatsStore, - featureStrategiesStore, - }, - projectInsightsReadModel, - ); + const projectInsightsService = new ProjectInsightsService({ + projectStore, + featureToggleStore, + featureTypeStore, + projectStatsStore, + featureStrategiesStore, + }); return { projectInsightsService, projectStatsStore, featureToggleStore, projectStore, - projectInsightsReadModel, }; }; diff --git a/src/lib/features/project-insights/fake-project-insights-read-model.ts b/src/lib/features/project-insights/fake-project-insights-read-model.ts deleted file mode 100644 index 01084aef2b..0000000000 --- a/src/lib/features/project-insights/fake-project-insights-read-model.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { - ChangeRequestCounts, - IProjectInsightsReadModel, -} from './project-insights-read-model-type'; - -const changeRequestCounts: ChangeRequestCounts = { - total: 0, - approved: 0, - applied: 0, - rejected: 0, - reviewRequired: 0, - scheduled: 0, -}; - -export class FakeProjectInsightsReadModel implements IProjectInsightsReadModel { - private counts: Record = {}; - - async getChangeRequests(projectId: string): Promise { - return this.counts[projectId] ?? changeRequestCounts; - } - - async setChangeRequests(projectId: string, counts: ChangeRequestCounts) { - this.counts[projectId] = counts; - } -} diff --git a/src/lib/features/project-insights/project-insights-read-model-type.ts b/src/lib/features/project-insights/project-insights-read-model-type.ts deleted file mode 100644 index f100d77299..0000000000 --- a/src/lib/features/project-insights/project-insights-read-model-type.ts +++ /dev/null @@ -1,12 +0,0 @@ -export type ChangeRequestCounts = { - total: number; - approved: number; - applied: number; - rejected: number; - reviewRequired: number; - scheduled: number; -}; - -export interface IProjectInsightsReadModel { - getChangeRequests(projectId: string): Promise; -} diff --git a/src/lib/features/project-insights/project-insights-read-model.test.ts b/src/lib/features/project-insights/project-insights-read-model.test.ts deleted file mode 100644 index ff8fdd20b1..0000000000 --- a/src/lib/features/project-insights/project-insights-read-model.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -import dbInit, { type ITestDb } from '../../../test/e2e/helpers/database-init'; -import getLogger from '../../../test/fixtures/no-logger'; -import type { IUser } from '../../types'; -import type { IProjectInsightsReadModel } from './project-insights-read-model-type'; -import { - type ChangeRequestDBState, - ProjectInsightsReadModel, -} from './project-insights-read-model'; - -let projectInsightsReadModel: IProjectInsightsReadModel; -let user: IUser; -let db: ITestDb; -const projectId = 'default'; - -beforeAll(async () => { - db = await dbInit('project_insights_read_model', getLogger); - projectInsightsReadModel = new ProjectInsightsReadModel(db.rawDatabase); - user = await db.stores.userStore.insert({ - username: 'test', - }); -}); - -afterAll(async () => { - await db.destroy(); -}); - -beforeEach(async () => { - await db.rawDatabase.table('change_requests').delete(); -}); - -const createChangeRequest = (id: number, state: string) => - db.rawDatabase.table('change_requests').insert({ - id, - state, - environment: 'default', - project: projectId, - created_by: user.id, - }); - -test('can read change request status counts', async () => { - const states: ChangeRequestDBState[] = [ - 'Approved', - 'Approved', - 'Applied', - 'Rejected', - 'Scheduled', - 'In review', - 'Draft', - 'Cancelled', - ]; - await Promise.all( - states.map((state, id) => createChangeRequest(id, state)), - ); - - const changeRequests = - await projectInsightsReadModel.getChangeRequests(projectId); - - expect(changeRequests).toEqual({ - total: 6, - approved: 2, - applied: 1, - rejected: 1, - reviewRequired: 1, - scheduled: 1, - }); -}); diff --git a/src/lib/features/project-insights/project-insights-read-model.ts b/src/lib/features/project-insights/project-insights-read-model.ts deleted file mode 100644 index e3fbf8e3a3..0000000000 --- a/src/lib/features/project-insights/project-insights-read-model.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { - ChangeRequestCounts, - IProjectInsightsReadModel, -} from './project-insights-read-model-type'; -import type { Db } from '../../db/db'; - -export type ChangeRequestDBState = - | 'Draft' - | 'Cancelled' - | 'Approved' - | 'In review' - | 'Applied' - | 'Scheduled' - | 'Rejected'; - -export class ProjectInsightsReadModel implements IProjectInsightsReadModel { - private db: Db; - - constructor(db: Db) { - this.db = db; - } - - async getChangeRequests(projectId: string): Promise { - const changeRequestCounts: ChangeRequestCounts = { - total: 0, - approved: 0, - applied: 0, - rejected: 0, - reviewRequired: 0, - scheduled: 0, - }; - - const rows: Array<{ state: ChangeRequestDBState; count: string }> = - await this.db('change_requests') - .select('state') - .count('* as count') - .where('project', '=', projectId) - .groupBy('state'); - - return rows.reduce((acc, current) => { - if (current.state === 'Applied') { - acc.applied = Number(current.count); - acc.total += Number(current.count); - } else if (current.state === 'Approved') { - acc.approved = Number(current.count); - acc.total += Number(current.count); - } else if (current.state === 'Rejected') { - acc.rejected = Number(current.count); - acc.total += Number(current.count); - } else if (current.state === 'In review') { - acc.reviewRequired = Number(current.count); - acc.total += Number(current.count); - } else if (current.state === 'Scheduled') { - acc.scheduled = Number(current.count); - acc.total += Number(current.count); - } - - return acc; - }, changeRequestCounts); - } -} diff --git a/src/lib/features/project-insights/project-insights-service.test.ts b/src/lib/features/project-insights/project-insights-service.test.ts index d72c819866..65bcf5ffce 100644 --- a/src/lib/features/project-insights/project-insights-service.test.ts +++ b/src/lib/features/project-insights/project-insights-service.test.ts @@ -6,21 +6,12 @@ test('Return basic insights', async () => { projectStatsStore, featureToggleStore, projectStore, - projectInsightsReadModel, } = createFakeProjectInsightsService(); await featureToggleStore.create('default', { name: 'irrelevant', createdByUserId: 1, type: 'release', }); - await projectInsightsReadModel.setChangeRequests('default', { - total: 5, - approved: 1, - applied: 1, - rejected: 1, - reviewRequired: 1, - scheduled: 1, - }); await projectStore.create({ id: 'default', name: 'irrelevant', @@ -57,14 +48,6 @@ test('Return basic insights', async () => { rating: 100, }, leadTime: { features: [], projectAverage: 0 }, - changeRequests: { - total: 5, - approved: 1, - applied: 1, - rejected: 1, - reviewRequired: 1, - scheduled: 1, - }, members: { currentMembers: 0, change: 0 }, }); }); diff --git a/src/lib/features/project-insights/project-insights-service.ts b/src/lib/features/project-insights/project-insights-service.ts index eed9750dda..e98d134e4e 100644 --- a/src/lib/features/project-insights/project-insights-service.ts +++ b/src/lib/features/project-insights/project-insights-service.ts @@ -13,7 +13,6 @@ import type { ProjectInsightsSchema, } from '../../openapi'; import { calculateProjectHealth } from '../../domain/project-health/project-health'; -import type { IProjectInsightsReadModel } from './project-insights-read-model-type'; import { subDays } from 'date-fns'; export class ProjectInsightsService { @@ -27,31 +26,25 @@ export class ProjectInsightsService { private projectStatsStore: IProjectStatsStore; - private projectInsightsReadModel: IProjectInsightsReadModel; - - constructor( - { - projectStore, - featureToggleStore, - featureTypeStore, - projectStatsStore, - featureStrategiesStore, - }: Pick< - IUnleashStores, - | 'projectStore' - | 'featureToggleStore' - | 'projectStatsStore' - | 'featureTypeStore' - | 'featureStrategiesStore' - >, - projectInsightsReadModel: IProjectInsightsReadModel, - ) { + constructor({ + projectStore, + featureToggleStore, + featureTypeStore, + projectStatsStore, + featureStrategiesStore, + }: Pick< + IUnleashStores, + | 'projectStore' + | 'featureToggleStore' + | 'projectStatsStore' + | 'featureTypeStore' + | 'featureStrategiesStore' + >) { this.projectStore = projectStore; this.featureToggleStore = featureToggleStore; this.featureTypeStore = featureTypeStore; this.featureStrategiesStore = featureStrategiesStore; this.projectStatsStore = projectStatsStore; - this.projectInsightsReadModel = projectInsightsReadModel; } async getDoraMetrics(projectId: string): Promise { @@ -143,31 +136,23 @@ export class ProjectInsightsService { } async getProjectInsights(projectId: string) { - const [ - stats, - featureTypeCounts, - health, - leadTime, - changeRequests, - members, - ] = await Promise.all([ - this.projectStatsStore.getProjectStats(projectId), - this.featureToggleStore.getFeatureTypeCounts({ - projectId, - archived: false, - }), - this.getHealthInsights(projectId), - this.getDoraMetrics(projectId), - this.projectInsightsReadModel.getChangeRequests(projectId), - this.getProjectMembers(projectId), - ]); + const [stats, featureTypeCounts, health, leadTime, members] = + await Promise.all([ + this.projectStatsStore.getProjectStats(projectId), + this.featureToggleStore.getFeatureTypeCounts({ + projectId, + archived: false, + }), + this.getHealthInsights(projectId), + this.getDoraMetrics(projectId), + this.getProjectMembers(projectId), + ]); return { stats, featureTypeCounts, health, leadTime, - changeRequests, members, }; } diff --git a/src/lib/features/project-insights/projects-insights.e2e.test.ts b/src/lib/features/project-insights/projects-insights.e2e.test.ts index 94b93138d7..a7205c1cda 100644 --- a/src/lib/features/project-insights/projects-insights.e2e.test.ts +++ b/src/lib/features/project-insights/projects-insights.e2e.test.ts @@ -53,13 +53,5 @@ test('project insights happy path', async () => { staleCount: 0, rating: 100, }, - changeRequests: { - total: 0, - approved: 0, - applied: 0, - rejected: 0, - reviewRequired: 0, - scheduled: 0, - }, }); }); diff --git a/src/lib/openapi/spec/project-insights-schema.ts b/src/lib/openapi/spec/project-insights-schema.ts index f06ce050f3..f6f97705f3 100644 --- a/src/lib/openapi/spec/project-insights-schema.ts +++ b/src/lib/openapi/spec/project-insights-schema.ts @@ -80,53 +80,6 @@ export const projectInsightsSchema = { }, description: 'Active/inactive users summary', }, - changeRequests: { - type: 'object', - required: [ - 'total', - 'applied', - 'rejected', - 'reviewRequired', - 'approved', - 'scheduled', - ], - properties: { - total: { - type: 'number', - description: - 'The number of total change requests in this project', - example: 10, - }, - applied: { - type: 'number', - description: 'The number of applied change requests', - example: 5, - }, - rejected: { - type: 'number', - description: 'The number of rejected change requests', - example: 2, - }, - reviewRequired: { - type: 'number', - description: - 'The number of change requests awaiting the review', - example: 2, - }, - approved: { - type: 'number', - description: 'The number of approved change requests', - example: 1, - }, - scheduled: { - type: 'number', - description: 'The number of scheduled change requests', - example: 1, - }, - }, - description: - 'Count of change requests in different stages of the [process](https://docs.getunleash.io/reference/change-requests#change-request-flow). Only for enterprise users.', - }, }, components: { schemas: {