diff --git a/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx b/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx index 06111b5dce..aafa227c2d 100644 --- a/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx +++ b/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx @@ -18,13 +18,11 @@ import { DashboardHeader } from './components/DashboardHeader/DashboardHeader'; import { UserStats } from './componentsStat/UserStats/UserStats'; import { FlagStats } from './componentsStat/FlagStats/FlagStats'; import { HealthStats } from './componentsStat/HealthStats/HealthStats'; -import { TimeToProduction } from './componentsStat/TimeToProduction/TimeToProduction'; import { UsersChart } from './componentsChart/UsersChart/UsersChart'; import { FlagsChart } from './componentsChart/FlagsChart/FlagsChart'; import { FlagsProjectChart } from './componentsChart/FlagsProjectChart/FlagsProjectChart'; import { ProjectHealthChart } from './componentsChart/ProjectHealthChart/ProjectHealthChart'; -import { TimeToProductionChart } from './componentsChart/TimeToProductionChart/TimeToProductionChart'; import { MetricsSummaryChart } from './componentsChart/MetricsSummaryChart/MetricsSummaryChart'; import { UsersPerProjectChart } from './componentsChart/UsersPerProjectChart/UsersPerProjectChart'; diff --git a/src/lib/db/segment-store.ts b/src/lib/db/segment-store.ts index ee95b6a32a..f2017b05be 100644 --- a/src/lib/db/segment-store.ts +++ b/src/lib/db/segment-store.ts @@ -116,141 +116,172 @@ export default class SegmentStore implements ISegmentStore { includeChangeRequestUsageData: boolean = false, ): Promise { if (includeChangeRequestUsageData) { - const pendingCRs = await this.db - .select('id', 'project') - .from('change_requests') - .whereNotIn('state', ['Applied', 'Rejected', 'Cancelled']); + return this.getAllWithChangeRequestUsageData(); + } else { + return this.getAllWithoutChangeRequestUsageData(); + } + } - const pendingChangeRequestIds = pendingCRs.map((cr) => cr.id); + private async getAllWithoutChangeRequestUsageData(): Promise { + const rows: ISegmentRow[] = await this.db + .select( + this.prefixColumns(), + 'used_in_projects', + 'used_in_features', + ) + .countDistinct( + `${T.featureStrategies}.project_name AS used_in_projects`, + ) + .countDistinct( + `${T.featureStrategies}.feature_name AS used_in_features`, + ) + .from(T.segments) + .leftJoin( + T.featureStrategySegment, + `${T.segments}.id`, + `${T.featureStrategySegment}.segment_id`, + ) + .leftJoin( + T.featureStrategies, + `${T.featureStrategies}.id`, + `${T.featureStrategySegment}.feature_strategy_id`, + ) + .groupBy(this.prefixColumns()) + .orderBy('name', 'asc'); - const crFeatures = await this.db - .select( - 'payload', - 'feature', - 'change_request_id as changeRequestId', - ) - .from('change_request_events') - .whereIn('change_request_id', pendingChangeRequestIds) - .whereIn('action', ['addStrategy', 'updateStrategy']) - .andWhereRaw("jsonb_array_length(payload -> 'segments') > 0"); + return rows.map(this.mapRow); + } - const changeRequestToProjectMap = pendingCRs.reduce( - (acc, { id, project }) => { - acc[id] = project; - return acc; - }, - {}, + private async getAllWithChangeRequestUsageData(): Promise { + const pendingCRs = await this.db + .select('id', 'project') + .from('change_requests') + .whereNotIn('state', ['Applied', 'Rejected', 'Cancelled']); + + const pendingChangeRequestIds = pendingCRs.map((cr) => cr.id); + + const crFeatures = await this.db + .select( + 'payload', + 'feature', + 'change_request_id as changeRequestId', + ) + .from('change_request_events') + .whereIn('change_request_id', pendingChangeRequestIds) + .whereIn('action', ['addStrategy', 'updateStrategy']) + .andWhereRaw("jsonb_array_length(payload -> 'segments') > 0"); + + const combinedUsageData = this.combineUsageData(pendingCRs, crFeatures); + + const currentSegmentUsage = await this.db + .select( + `${T.featureStrategies}.feature_name as featureName`, + `${T.featureStrategies}.project_name as projectName`, + 'segment_id as segmentId', + ) + .from(T.featureStrategySegment) + .leftJoin( + T.featureStrategies, + `${T.featureStrategies}.id`, + `${T.featureStrategySegment}.feature_strategy_id`, ); - const combinedUsageData = crFeatures.reduce((acc, segmentEvent) => { - const { payload, changeRequestId, feature } = segmentEvent; - const project = changeRequestToProjectMap[changeRequestId]; + this.mergeCurrentUsageWithCombinedData( + combinedUsageData, + currentSegmentUsage, + ); - for (const segmentId of payload.segments) { - const existingData = acc[segmentId]; - if (existingData) { - acc[segmentId] = { - features: existingData.features.add(feature), - projects: existingData.projects.add(project), - }; - } else { - acc[segmentId] = { - features: new Set([feature]), - projects: new Set([project]), - }; - } - } + const rows: ISegmentRow[] = await this.db + .select(this.prefixColumns()) + .from(T.segments) + .leftJoin( + T.featureStrategySegment, + `${T.segments}.id`, + `${T.featureStrategySegment}.segment_id`, + ) + .groupBy(this.prefixColumns()) + .orderBy('name', 'asc'); + + const rowsWithUsageData = this.mapRowsWithUsageData( + rows, + combinedUsageData, + ); + + return rowsWithUsageData.map(this.mapRow); + } + + private mapRowsWithUsageData( + rows: ISegmentRow[], + combinedUsageData: any, + ): ISegmentRow[] { + return rows.map((row) => { + const usageData = combinedUsageData[row.id]; + if (usageData) { + return { + ...row, + used_in_features: usageData.features.size, + used_in_projects: usageData.projects.size, + }; + } else { + return { + ...row, + used_in_features: 0, + used_in_projects: 0, + }; + } + }); + } + + private combineUsageData = (pendingCRs, crFeatures) => { + const changeRequestToProjectMap = pendingCRs.reduce( + (acc, { id, project }) => { + acc[id] = project; return acc; - }, {}); + }, + {}, + ); - const currentSegmentUsage = await this.db - .select( - `${T.featureStrategies}.feature_name as featureName`, - `${T.featureStrategies}.project_name as projectName`, - 'segment_id as segmentId', - ) - .from(T.featureStrategySegment) - .leftJoin( - T.featureStrategies, - `${T.featureStrategies}.id`, - `${T.featureStrategySegment}.feature_strategy_id`, - ); + const combinedUsageData = crFeatures.reduce((acc, segmentEvent) => { + const { payload, changeRequestId, feature } = segmentEvent; + const project = changeRequestToProjectMap[changeRequestId]; - currentSegmentUsage.forEach( - ({ segmentId, featureName, projectName }) => { - const usage = combinedUsageData[segmentId]; - if (usage) { - combinedUsageData[segmentId] = { - features: usage.features.add(featureName), - projects: usage.projects.add(projectName), - }; - } else { - combinedUsageData[segmentId] = { - features: new Set([featureName]), - projects: new Set([projectName]), - }; - } - }, - ); - - const rows: ISegmentRow[] = await this.db - .select(this.prefixColumns()) - .from(T.segments) - .leftJoin( - T.featureStrategySegment, - `${T.segments}.id`, - `${T.featureStrategySegment}.segment_id`, - ) - .groupBy(this.prefixColumns()) - .orderBy('name', 'asc'); - - const rowsWithUsageData: ISegmentRow[] = rows.map((row) => { - const usageData = combinedUsageData[row.id]; - if (usageData) { - return { - ...row, - used_in_features: usageData.features.size, - used_in_projects: usageData.projects.size, + for (const segmentId of payload.segments) { + const existingData = acc[segmentId]; + if (existingData) { + acc[segmentId] = { + features: existingData.features.add(feature), + projects: existingData.projects.add(project), }; } else { - return { - ...row, - used_in_features: 0, - used_in_projects: 0, + acc[segmentId] = { + features: new Set([feature]), + projects: new Set([project]), }; } - }); + } + return acc; + }, {}); + return combinedUsageData; + }; - return rowsWithUsageData.map(this.mapRow); - } else { - const rows: ISegmentRow[] = await this.db - .select( - this.prefixColumns(), - 'used_in_projects', - 'used_in_features', - ) - .countDistinct( - `${T.featureStrategies}.project_name AS used_in_projects`, - ) - .countDistinct( - `${T.featureStrategies}.feature_name AS used_in_features`, - ) - .from(T.segments) - .leftJoin( - T.featureStrategySegment, - `${T.segments}.id`, - `${T.featureStrategySegment}.segment_id`, - ) - .leftJoin( - T.featureStrategies, - `${T.featureStrategies}.id`, - `${T.featureStrategySegment}.feature_strategy_id`, - ) - .groupBy(this.prefixColumns()) - .orderBy('name', 'asc'); - - return rows.map(this.mapRow); - } + private mergeCurrentUsageWithCombinedData( + combinedUsageData: any, + currentSegmentUsage: any[], + ) { + currentSegmentUsage.forEach( + ({ segmentId, featureName, projectName }) => { + const usage = combinedUsageData[segmentId]; + if (usage) { + usage.features.add(featureName); + usage.projects.add(projectName); + } else { + combinedUsageData[segmentId] = { + features: new Set([featureName]), + projects: new Set([projectName]), + }; + } + }, + ); } async getActive(): Promise {