88using OpenShock . Common . Errors ;
99using OpenShock . Common . Extensions ;
1010using OpenShock . Common . Models ;
11+ using OpenShock . Common . OpenShockDb ;
1112using OpenShock . Common . Problems ;
1213using OpenShock . Common . Utils ;
14+ using OpenShock . Common . Utils . Pagination ;
1315
1416namespace OpenShock . API . Controller . Shockers ;
1517
1618public sealed partial class ShockerController
1719{
20+ private const string AllLogsDefaultSort = "createdOn" ;
21+
22+ private static readonly IReadOnlyDictionary < string , SortFunc < ShockerControlLog > > AllLogsSorters =
23+ new Dictionary < string , SortFunc < ShockerControlLog > > ( StringComparer . OrdinalIgnoreCase )
24+ {
25+ [ AllLogsDefaultSort ] = ( q , desc ) => desc ? q . OrderByDescending ( x => x . CreatedAt ) : q . OrderBy ( x => x . CreatedAt ) ,
26+ [ "intensity" ] = ( q , desc ) => desc ? q . OrderByDescending ( x => x . Intensity ) : q . OrderBy ( x => x . Intensity ) ,
27+ [ "duration" ] = ( q , desc ) => desc ? q . OrderByDescending ( x => x . Duration ) : q . OrderBy ( x => x . Duration ) ,
28+ [ "type" ] = ( q , desc ) => desc ? q . OrderByDescending ( x => x . Type ) : q . OrderBy ( x => x . Type ) ,
29+ [ "hubName" ] = ( q , desc ) => desc ? q . OrderByDescending ( x => x . Shocker . Device . Name ) : q . OrderBy ( x => x . Shocker . Device . Name ) ,
30+ [ "shockerName" ] = ( q , desc ) => desc ? q . OrderByDescending ( x => x . Shocker . Name ) : q . OrderBy ( x => x . Shocker . Name ) ,
31+ } ;
32+
1833 /// <summary>
1934 /// Get the logs for a shocker
2035 /// </summary>
@@ -69,26 +84,33 @@ public async Task<IActionResult> GetShockerLogs([FromRoute] Guid shockerId, [Fro
6984
7085
7186 /// <summary>
72- /// Get the logs for all shockers
87+ /// Get a paged set of control logs across the caller's shockers.
7388 /// </summary>
74- /// <param name="offset"> </param>
75- /// <param name="limit"> </param>
76- /// <response code="200">The logs</response >
77- /// <response code="404">Shocker does not exist </response>
89+ /// <param name="pagination">Page, sort, and search parameters. Supported sort keys: createdOn (default), intensity, duration, type, hubName, shockerName. </param>
90+ /// <param name="shockerIds">Optional shocker ID filter. When omitted or empty, logs for all of the caller's shockers are returned. </param>
91+ /// <param name="cancellationToken"></param >
92+ /// <response code="200">A page of logs. </response>
7893 [ HttpGet ( "logs" ) ]
7994 [ EnableRateLimiting ( "shocker-logs" ) ]
80- [ ProducesResponseType < ShockerLogsResponse > ( StatusCodes . Status200OK , MediaTypeNames . Application . Json ) ]
81- [ ProducesResponseType < OpenShockProblem > ( StatusCodes . Status404NotFound , MediaTypeNames . Application . ProblemJson ) ]
95+ [ ProducesResponseType < PagedResult < LogEntryWithHub > > ( StatusCodes . Status200OK , MediaTypeNames . Application . Json ) ]
8296 [ MapToApiVersion ( "1" ) ]
83- public async Task < IActionResult > GetAllShockerLogs ( [ FromQuery ( Name = "offset" ) ] uint offset = 0 ,
84- [ FromQuery , Range ( 1 , 500 ) ] uint limit = 100 )
97+ public async Task < PagedResult < LogEntryWithHub > > GetAllShockerLogs (
98+ [ FromQuery ] PaginationQuery pagination ,
99+ [ FromQuery ( Name = "shockerIds" ) ] Guid [ ] ? shockerIds ,
100+ CancellationToken cancellationToken )
85101 {
86- var logs = await _db . ShockerControlLogs
87- . Where ( x => x . Shocker . Device . OwnerId == CurrentUser . Id )
88- . OrderByDescending ( x => x . CreatedAt )
89- . Skip ( ( int ) offset )
90- . Take ( ( int ) limit )
91- . Select ( x => new LogEntryWithHub
102+ var query = _db . ShockerControlLogs
103+ . Where ( x => x . Shocker . Device . OwnerId == CurrentUser . Id ) ;
104+
105+ if ( shockerIds is { Length : > 0 } )
106+ query = query . Where ( x => ( ( IEnumerable < Guid > ) shockerIds ) . Contains ( x . ShockerId ) ) ;
107+
108+ var ordered = query
109+ . ApplySort ( pagination , AllLogsSorters , AllLogsDefaultSort )
110+ . ThenBy ( x => x . Id ) ;
111+
112+ return await ordered . ToPagedResultAsync (
113+ x => new LogEntryWithHub
92114 {
93115 Id = x . Id ,
94116 HubId = x . Shocker . Device . Id ,
@@ -114,12 +136,8 @@ public async Task<IActionResult> GetAllShockerLogs([FromQuery(Name = "offset")]
114136 Image = x . ControlledByUser . GetImageUrl ( ) ,
115137 CustomName = x . CustomName
116138 }
117- } )
118- . ToListAsync ( ) ;
119-
120- return Ok ( new ShockerLogsResponse
121- {
122- Logs = logs
123- } ) ;
139+ } ,
140+ pagination ,
141+ cancellationToken ) ;
124142 }
125143}
0 commit comments