11using DevMetricsPro . Application . Caching ;
22using DevMetricsPro . Application . DTOs . GitHub ;
3+ using DevMetricsPro . Application . DTOs . Common ;
34using DevMetricsPro . Application . Interfaces ;
45using DevMetricsPro . Core . Entities ;
56using DevMetricsPro . Core . Enums ;
@@ -452,7 +453,77 @@ public async Task<IActionResult> SyncCommits(long githubRepositoryId, Cancellati
452453 }
453454
454455 /// <summary>
455- /// Get recent commits across all repositories for the authenticated user
456+ /// Get commits with pagination support
457+ /// </summary>
458+ [ HttpGet ( "commits" ) ]
459+ [ Authorize ( AuthenticationSchemes = "Bearer" ) ]
460+ [ ProducesResponseType ( typeof ( PaginatedResult < object > ) , StatusCodes . Status200OK ) ]
461+ public async Task < IActionResult > GetCommits (
462+ [ FromQuery ] Guid ? repositoryId = null ,
463+ [ FromQuery ] int page = 1 ,
464+ [ FromQuery ] int pageSize = 25 ,
465+ CancellationToken cancellationToken = default )
466+ {
467+ if ( ! TryGetUserId ( out var userId ) )
468+ {
469+ return Unauthorized ( new { error = "User not authenticated" } ) ;
470+ }
471+
472+ // Validate and clamp pagination parameters
473+ page = Math . Max ( 1 , page ) ;
474+ pageSize = Math . Clamp ( pageSize , 10 , 100 ) ;
475+
476+ var commitRepo = _unitOfWork . Repository < Commit > ( ) ;
477+ var query = commitRepo . Query ( )
478+ . Include ( c => c . Developer )
479+ . Include ( c => c . Repository )
480+ . AsQueryable ( ) ;
481+
482+ // Filter by repository if specified
483+ if ( repositoryId . HasValue )
484+ {
485+ query = query . Where ( c => c . RepositoryId == repositoryId . Value ) ;
486+ }
487+
488+ // Get total count before pagination
489+ var totalCount = await query . CountAsync ( cancellationToken ) ;
490+
491+ // Apply pagination
492+ var commits = await query
493+ . OrderByDescending ( c => c . CommittedAt )
494+ . Skip ( ( page - 1 ) * pageSize )
495+ . Take ( pageSize )
496+ . Select ( c => new
497+ {
498+ sha = c . Sha ,
499+ message = c . Message ,
500+ authorName = c . Developer != null ? c . Developer . DisplayName ?? "Unknown Author" : "Unknown Author" ,
501+ committedAt = c . CommittedAt ,
502+ repositoryName = c . Repository != null ? c . Repository . Name ?? "Unknown Repository" : "Unknown Repository" ,
503+ repositoryId = c . RepositoryId ,
504+ linesAdded = c . LinesAdded ,
505+ linesRemoved = c . LinesRemoved ,
506+ filesChanged = c . FilesChanged
507+ } )
508+ . ToListAsync ( cancellationToken ) ;
509+
510+ _logger . LogInformation (
511+ "Fetched page {Page} of commits ({Count} items, {Total} total)" ,
512+ page , commits . Count , totalCount ) ;
513+
514+ var result = new PaginatedResult < object >
515+ {
516+ Items = commits ,
517+ Page = page ,
518+ PageSize = pageSize ,
519+ TotalCount = totalCount
520+ } ;
521+
522+ return Ok ( result ) ;
523+ }
524+
525+ /// <summary>
526+ /// Get recent commits across all repositories for the authenticated user (legacy endpoint for dashboard)
456527 /// </summary>
457528 [ HttpGet ( "commits/recent" ) ]
458529 [ Authorize ( AuthenticationSchemes = "Bearer" ) ]
@@ -943,29 +1014,39 @@ public async Task<IActionResult> TriggerFullSync(
9431014 }
9441015
9451016 /// <summary>
946- /// Get all pull requests from database with optional filtering
1017+ /// Get all pull requests from database with optional filtering and pagination
9471018 /// </summary>
9481019 [ HttpGet ( "pull-requests" ) ]
9491020 [ Authorize ( AuthenticationSchemes = "Bearer" ) ]
950- [ ProducesResponseType ( typeof ( object ) , StatusCodes . Status200OK ) ]
1021+ [ ProducesResponseType ( typeof ( PaginatedResult < object > ) , StatusCodes . Status200OK ) ]
9511022 [ ProducesResponseType ( StatusCodes . Status401Unauthorized ) ]
9521023 public async Task < IActionResult > GetPullRequests (
9531024 [ FromQuery ] Guid ? repositoryId = null ,
9541025 [ FromQuery ] string ? status = null ,
1026+ [ FromQuery ] int page = 1 ,
1027+ [ FromQuery ] int pageSize = 25 ,
9551028 CancellationToken cancellationToken = default )
9561029 {
957- var userId = User . FindFirst ( System . Security . Claims . ClaimTypes . NameIdentifier ) ? . Value ;
958- if ( string . IsNullOrEmpty ( userId ) )
1030+ if ( ! TryGetUserId ( out var userId ) )
9591031 {
9601032 throw new UnauthorizedAccessException ( "User not authenticated" ) ;
9611033 }
9621034
1035+ // Validate and clamp pagination parameters
1036+ page = Math . Max ( 1 , page ) ;
1037+ pageSize = Math . Clamp ( pageSize , 10 , 100 ) ;
1038+
9631039 var prRepository = _unitOfWork . Repository < PullRequest > ( ) ;
964- var pullRequests = await prRepository . GetAllAsync ( cancellationToken ) ;
1040+
1041+ // Build query with filters
1042+ var query = prRepository . Query ( )
1043+ . Include ( pr => pr . Author )
1044+ . Include ( pr => pr . Repository )
1045+ . AsQueryable ( ) ;
9651046
9661047 if ( repositoryId . HasValue )
9671048 {
968- pullRequests = pullRequests . Where ( pr => pr . RepositoryId == repositoryId . Value ) ;
1049+ query = query . Where ( pr => pr . RepositoryId == repositoryId . Value ) ;
9691050 }
9701051
9711052 if ( ! string . IsNullOrEmpty ( status ) && status . ToLower ( ) != "all" )
@@ -980,12 +1061,18 @@ public async Task<IActionResult> GetPullRequests(
9801061
9811062 if ( prStatus . HasValue )
9821063 {
983- pullRequests = pullRequests . Where ( pr => pr . Status == prStatus . Value ) ;
1064+ query = query . Where ( pr => pr . Status == prStatus . Value ) ;
9841065 }
9851066 }
9861067
987- var sortedPRs = pullRequests
1068+ // Get total count before pagination
1069+ var totalCount = await query . CountAsync ( cancellationToken ) ;
1070+
1071+ // Apply pagination
1072+ var pullRequests = await query
9881073 . OrderByDescending ( pr => pr . UpdatedAt )
1074+ . Skip ( ( page - 1 ) * pageSize )
1075+ . Take ( pageSize )
9891076 . Select ( pr => new
9901077 {
9911078 id = pr . Id ,
@@ -994,27 +1081,32 @@ public async Task<IActionResult> GetPullRequests(
9941081 description = pr . Description ,
9951082 status = pr . Status . ToString ( ) ,
9961083 isMerged = pr . Status == PullRequestStatus . Merged ,
997- authorName = pr . Author ? . DisplayName ?? "Unknown" ,
998- authorUsername = pr . Author ? . GitHubUsername ?? "Unknown" ,
999- repositoryName = pr . Repository ? . Name ?? "Unknown" ,
1084+ authorName = pr . Author != null ? pr . Author . DisplayName ?? "Unknown" : "Unknown" ,
1085+ authorUsername = pr . Author != null ? pr . Author . GitHubUsername ?? "Unknown" : "Unknown" ,
1086+ repositoryName = pr . Repository != null ? pr . Repository . Name ?? "Unknown" : "Unknown" ,
10001087 repositoryId = pr . RepositoryId ,
10011088 createdAt = pr . CreatedAt ,
10021089 updatedAt = pr . UpdatedAt ,
10031090 closedAt = pr . ClosedAt ,
10041091 mergedAt = pr . MergedAt ,
1005- url = pr . Repository ? . FullName != null ?
1006- $ "https://github.com/{ pr . Repository ? . FullName } /pull/{ pr . ExternalId } " : null
1092+ url = pr . Repository != null && pr . Repository . FullName != null ?
1093+ $ "https://github.com/{ pr . Repository . FullName } /pull/{ pr . ExternalId } " : null
10071094 } )
1008- . ToList ( ) ;
1095+ . ToListAsync ( cancellationToken ) ;
10091096
1010- _logger . LogInformation ( "Fetched {Count} pull requests from database" , sortedPRs . Count ) ;
1097+ _logger . LogInformation (
1098+ "Fetched page {Page} of pull requests ({Count} items, {Total} total)" ,
1099+ page , pullRequests . Count , totalCount ) ;
10111100
1012- return Ok ( new
1101+ var result = new PaginatedResult < object >
10131102 {
1014- success = true ,
1015- count = sortedPRs . Count ,
1016- pullRequests = sortedPRs
1017- } ) ;
1103+ Items = pullRequests ,
1104+ Page = page ,
1105+ PageSize = pageSize ,
1106+ TotalCount = totalCount
1107+ } ;
1108+
1109+ return Ok ( result ) ;
10181110 }
10191111
10201112}
0 commit comments