feat: lead time breakdown (#71)#72
Conversation
* feat: lead time breakdown * fix: review feedback * fix: failure rate schema * fix: period copy * feat: info button * fix: docker-compose.yml
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing touches🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile OverviewGreptile SummaryThis PR adds a lead time breakdown feature that provides stage-by-stage analysis of the development pipeline (coding, review, approval, merge, deploy). The implementation includes:
Key changes:
Confidence Score: 4/5
|
| Filename | Overview |
|---|---|
| apps/api/src/app/metrics/services/lead-time-breakdown.service.ts | New service for lead time breakdown metrics with stage-by-stage analysis; includes duplicated filter logic from dora-metrics.service.ts |
| apps/api/src/lib/date.ts | Added getPreviousPeriod utility function for calculating date ranges |
| apps/api/src/app/metrics/services/dora-metrics.service.ts | Refactored to extract buildDeploymentFilters and use shared getPreviousPeriod function; removed isPreviousPeriod parameter |
| apps/web/src/app/metrics-and-insights/lead-time/components/lead-time-breakdown/step.tsx | Step component for breakdown visualization with tooltips and change indicators |
| apps/web/src/providers/date.provider.ts | Added duration formatting utilities and DateTimeRange type |
Sequence Diagram
sequenceDiagram
participant Client as Web Client
participant GraphQL as GraphQL Resolver
participant Service as LeadTimeBreakdown Service
participant DB as Database
participant Transform as Transformer
Client->>GraphQL: Query leadTime.breakdown
GraphQL->>Service: getLeadTimeBreakdown(filters)
Service->>Service: getPreviousPeriod(from, to)
Service->>Service: buildBreakdownAggregateQuery(current period)
Service->>Service: buildBreakdownAggregateQuery(previous period)
Service->>DB: Execute current period query
Service->>DB: Execute previous period query
DB-->>Service: Current results (timeToCode, timeToFirstReview, etc)
DB-->>Service: Previous results
Service->>Service: buildStage for each metric
Service->>Service: calculateChange(current, previous)
Service-->>GraphQL: LeadTimeBreakdownResult
GraphQL->>Transform: transformLeadTimeBreakdown(result)
Transform-->>GraphQL: Formatted breakdown with periods
GraphQL-->>Client: LeadTimeBreakdown with stage metrics
| const buildDeploymentQueryFilters = ( | ||
| filters: DoraMetricsFilters, | ||
| alias: string | ||
| ) => { | ||
| const joins: Prisma.Sql[] = []; | ||
| const conditions: Prisma.Sql[] = []; | ||
|
|
||
| conditions.push( | ||
| Prisma.sql`${Prisma.raw(alias)}."workspaceId" = ${filters.workspaceId}` | ||
| ); | ||
|
|
||
| if (filters.environmentIds && filters.environmentIds.length > 0) { | ||
| conditions.push( | ||
| Prisma.sql`${Prisma.raw(alias)}."environmentId" = ANY(ARRAY[${Prisma.join( | ||
| filters.environmentIds.map((id) => Prisma.sql`${id}`), | ||
| ", " | ||
| )}])` | ||
| ); | ||
| } else { | ||
| joins.push( | ||
| Prisma.sql`INNER JOIN "Environment" e ON e."id" = ${Prisma.raw(alias)}."environmentId" AND e."workspaceId" = ${Prisma.raw(alias)}."workspaceId"` | ||
| ); | ||
| conditions.push( | ||
| Prisma.sql`e."isProduction" = true AND e."archivedAt" IS NULL` | ||
| ); | ||
| } | ||
|
|
||
| if (filters.applicationIds && filters.applicationIds.length > 0) { | ||
| conditions.push( | ||
| Prisma.sql`${Prisma.raw(alias)}."applicationId" = ANY(ARRAY[${Prisma.join( | ||
| filters.applicationIds.map((id) => Prisma.sql`${id}`), | ||
| ", " | ||
| )}])` | ||
| ); | ||
| } | ||
|
|
||
| const needsApplicationJoin = | ||
| (filters.teamIds && filters.teamIds.length > 0) || | ||
| (filters.repositoryIds && filters.repositoryIds.length > 0); | ||
|
|
||
| if (needsApplicationJoin) { | ||
| joins.push( | ||
| Prisma.sql`INNER JOIN "Application" a ON a."id" = ${Prisma.raw(alias)}."applicationId" AND a."workspaceId" = ${Prisma.raw(alias)}."workspaceId" AND a."archivedAt" IS NULL` | ||
| ); | ||
|
|
||
| if (filters.teamIds && filters.teamIds.length > 0) { | ||
| conditions.push( | ||
| Prisma.sql`a."teamId" = ANY(ARRAY[${Prisma.join( | ||
| filters.teamIds.map((id) => Prisma.sql`${id}`), | ||
| ", " | ||
| )}])` | ||
| ); | ||
| } | ||
|
|
||
| if (filters.repositoryIds && filters.repositoryIds.length > 0) { | ||
| conditions.push( | ||
| Prisma.sql`a."repositoryId" = ANY(ARRAY[${Prisma.join( | ||
| filters.repositoryIds.map((id) => Prisma.sql`${id}`), | ||
| ", " | ||
| )}])` | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| return { joins, conditions }; |
There was a problem hiding this comment.
The buildDeploymentQueryFilters function is duplicated from dora-metrics.service.ts (where it's named buildDeploymentFilters). Consider extracting this shared logic to a common utility file to avoid code duplication and ensure consistency.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Summary
feat: lead time breakdown
fix: review feedback
fix: failure rate schema
fix: period copy
feat: info button
fix: docker-compose.yml