Skip to content

Commit 9dfd40f

Browse files
committed
Adding support for OFFSET/LIMIT pagination in Activity.Api
Signed-off-by: Will Velida <willvelida@hotmail.co.uk>
1 parent ad452e6 commit 9dfd40f

File tree

10 files changed

+463
-40
lines changed

10 files changed

+463
-40
lines changed

infra/apps/activity-api/main.bicep

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,31 @@ resource activityApiGetAll 'Microsoft.ApiManagement/service/apis/operations@2024
109109
displayName: 'GetAllActivities'
110110
method: 'GET'
111111
urlTemplate: '/'
112-
description: 'Get all Activity Summaries'
112+
description: 'Get all Activity Summaries'
113+
request: {
114+
queryParameters: [
115+
{
116+
name: 'pageNumber'
117+
description: 'The page number to retrieve (default: 1)'
118+
type: 'integer'
119+
required: false
120+
defaultValue: '1'
121+
values: [
122+
123+
]
124+
}
125+
{
126+
name: 'pageSize'
127+
description: 'The number of items per page (default: 20, max: 100)'
128+
type: 'integer'
129+
required: false
130+
defaultValue: '20'
131+
values: [
132+
133+
]
134+
}
135+
]
136+
}
113137
}
114138
}
115139

src/Biotrackr.Activity.Api/Biotrackr.Activity.Api.UnitTests/Biotrackr.Activity.Api.UnitTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>net9.0</TargetFramework>

src/Biotrackr.Activity.Api/Biotrackr.Activity.Api.UnitTests/EndpointHandlerTests/ActivityHandlersShould.cs

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,33 +51,81 @@ public async Task GetActivityByDate_ShouldReturnNotFound_WhenActivityIsNotFound(
5151
}
5252

5353
[Fact]
54-
public async Task GetAllActivities_ShouldReturnOk_WhenActivitiesAreFound()
54+
public async Task GetAllActivities_ShouldReturnPaginatedResult_WhenPaginationParametersProvided()
5555
{
5656
// Arrange
5757
var fixture = new Fixture();
58-
var activityDocuments = fixture.CreateMany<ActivityDocument>().ToList();
59-
_cosmosRepositoryMock.Setup(x => x.GetAllActivitySummaries()).ReturnsAsync(activityDocuments);
58+
var activityDocuments = fixture.CreateMany<ActivityDocument>(10).ToList();
59+
var paginatedResponse = new PaginationResponse<ActivityDocument>
60+
{
61+
Items = activityDocuments,
62+
PageNumber = 2,
63+
PageSize = 10,
64+
TotalCount = 50
65+
};
66+
67+
_cosmosRepositoryMock.Setup(x => x.GetAllActivitySummaries(It.IsAny<PaginationRequest>()))
68+
.ReturnsAsync(paginatedResponse);
69+
70+
// Act
71+
var result = await ActivityHandlers.GetAllActivities(_cosmosRepositoryMock.Object, 2, 10);
72+
73+
// Assert
74+
result.Should().BeOfType<Ok<PaginationResponse<ActivityDocument>>>();
75+
var okResult = result as Ok<PaginationResponse<ActivityDocument>>;
76+
okResult.Value.Should().BeEquivalentTo(paginatedResponse);
77+
}
78+
79+
[Fact]
80+
public async Task GetAllActivities_ShouldUseDefaultPageSize_WhenOnlyPageNumberProvided()
81+
{
82+
// Arrange
83+
var fixture = new Fixture();
84+
var paginatedResponse = new PaginationResponse<ActivityDocument>
85+
{
86+
Items = fixture.CreateMany<ActivityDocument>(20).ToList(),
87+
PageNumber = 2,
88+
PageSize = 20,
89+
TotalCount = 100
90+
};
91+
92+
_cosmosRepositoryMock.Setup(x => x.GetAllActivitySummaries(
93+
It.Is<PaginationRequest>(r => r.PageNumber == 2 && r.PageSize == 20)))
94+
.ReturnsAsync(paginatedResponse);
6095

6196
// Act
62-
var result = await ActivityHandlers.GetAllActivities(_cosmosRepositoryMock.Object);
97+
var result = await ActivityHandlers.GetAllActivities(_cosmosRepositoryMock.Object, 2, null);
6398

6499
// Assert
65-
result.Should().BeOfType<Ok<List<ActivityDocument>>>();
66-
result.Value.Should().BeEquivalentTo(activityDocuments);
100+
result.Should().BeOfType<Ok<PaginationResponse<ActivityDocument>>>();
101+
_cosmosRepositoryMock.Verify(x => x.GetAllActivitySummaries(
102+
It.Is<PaginationRequest>(r => r.PageNumber == 2 && r.PageSize == 20)), Times.Once);
67103
}
68104

69105
[Fact]
70-
public async Task GetAllActivities_ShouldReturnOk_WhenActivitiesAreNotFound()
106+
public async Task GetAllActivities_ShouldUseDefaultPageNumber_WhenOnlyPageSizeProvided()
71107
{
72108
// Arrange
73-
_cosmosRepositoryMock.Setup(x => x.GetAllActivitySummaries()).ReturnsAsync(new List<ActivityDocument>());
109+
var fixture = new Fixture();
110+
var paginatedResponse = new PaginationResponse<ActivityDocument>
111+
{
112+
Items = fixture.CreateMany<ActivityDocument>(50).ToList(),
113+
PageNumber = 1,
114+
PageSize = 50,
115+
TotalCount = 100
116+
};
117+
118+
_cosmosRepositoryMock.Setup(x => x.GetAllActivitySummaries(
119+
It.Is<PaginationRequest>(r => r.PageNumber == 1 && r.PageSize == 50)))
120+
.ReturnsAsync(paginatedResponse);
74121

75122
// Act
76-
var result = await ActivityHandlers.GetAllActivities(_cosmosRepositoryMock.Object);
123+
var result = await ActivityHandlers.GetAllActivities(_cosmosRepositoryMock.Object, null, 50);
77124

78125
// Assert
79-
result.Should().BeOfType<Ok<List<ActivityDocument>>>();
80-
result.Value.Should().BeEmpty();
126+
result.Should().BeOfType<Ok<PaginationResponse<ActivityDocument>>>();
127+
_cosmosRepositoryMock.Verify(x => x.GetAllActivitySummaries(
128+
It.Is<PaginationRequest>(r => r.PageNumber == 1 && r.PageSize == 50)), Times.Once);
81129
}
82130
}
83131
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using Biotrackr.Activity.Api.Models;
2+
using FluentAssertions;
3+
4+
namespace Biotrackr.Activity.Api.UnitTests.ModelTests
5+
{
6+
public class PaginationRequestShould
7+
{
8+
[Fact]
9+
public void SetDefaultValues_WhenCreated()
10+
{
11+
// Act
12+
var request = new PaginationRequest();
13+
14+
// Assert
15+
request.PageNumber.Should().Be(1);
16+
request.PageSize.Should().Be(20);
17+
request.Skip.Should().Be(0);
18+
}
19+
20+
[Theory]
21+
[InlineData(0, 1)]
22+
[InlineData(-1, 1)]
23+
[InlineData(-10, 1)]
24+
public void SetPageNumberToOne_WhenInvalidValueProvided(int invalidPageNumber, int expectedPageNumber)
25+
{
26+
// Act
27+
var request = new PaginationRequest { PageNumber = invalidPageNumber };
28+
29+
// Assert
30+
request.PageNumber.Should().Be(expectedPageNumber);
31+
}
32+
33+
[Theory]
34+
[InlineData(0, 20)]
35+
[InlineData(-1, 20)]
36+
[InlineData(101, 100)]
37+
[InlineData(200, 100)]
38+
public void ClampPageSize_WhenInvalidValueProvided(int invalidPageSize, int expectedPageSize)
39+
{
40+
// Act
41+
var request = new PaginationRequest { PageSize = invalidPageSize };
42+
43+
// Assert
44+
request.PageSize.Should().Be(expectedPageSize);
45+
}
46+
47+
[Theory]
48+
[InlineData(1, 20, 0)]
49+
[InlineData(2, 20, 20)]
50+
[InlineData(3, 15, 30)]
51+
[InlineData(5, 10, 40)]
52+
public void CalculateCorrectSkipValue(int pageNumber, int pageSize, int expectedSkip)
53+
{
54+
// Act
55+
var request = new PaginationRequest
56+
{
57+
PageNumber = pageNumber,
58+
PageSize = pageSize
59+
};
60+
61+
// Assert
62+
request.Skip.Should().Be(expectedSkip);
63+
}
64+
}
65+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using Biotrackr.Activity.Api.Models;
2+
using FluentAssertions;
3+
4+
namespace Biotrackr.Activity.Api.UnitTests.ModelTests
5+
{
6+
public class PaginationResponseShould
7+
{
8+
[Theory]
9+
[InlineData(50, 20, 3)] // 50 total, 20 per page = 3 pages
10+
[InlineData(100, 20, 5)] // 100 total, 20 per page = 5 pages
11+
[InlineData(19, 20, 1)] // 19 total, 20 per page = 1 page
12+
[InlineData(0, 20, 0)] // 0 total = 0 pages
13+
public void CalculateCorrectTotalPages(int totalCount, int pageSize, int expectedTotalPages)
14+
{
15+
// Act
16+
var response = new PaginationResponse<string>
17+
{
18+
TotalCount = totalCount,
19+
PageSize = pageSize
20+
};
21+
22+
// Assert
23+
response.TotalPages.Should().Be(expectedTotalPages);
24+
}
25+
26+
[Theory]
27+
[InlineData(1, 20, 100, false)] // First page
28+
[InlineData(2, 20, 100, true)] // Middle page
29+
[InlineData(5, 20, 100, true)] // Last page
30+
public void DetermineHasPreviousPageCorrectly(int pageNumber, int pageSize, int totalCount, bool expectedHasPrevious)
31+
{
32+
// Act
33+
var response = new PaginationResponse<string>
34+
{
35+
PageNumber = pageNumber,
36+
PageSize = pageSize,
37+
TotalCount = totalCount
38+
};
39+
40+
// Assert
41+
response.HasPreviousPage.Should().Be(expectedHasPrevious);
42+
}
43+
44+
[Theory]
45+
[InlineData(1, 20, 100, true)] // First page, more pages available
46+
[InlineData(4, 20, 100, true)] // Middle page, more pages available
47+
[InlineData(5, 20, 100, false)] // Last page, no more pages
48+
[InlineData(1, 20, 15, false)] // Only page
49+
public void DetermineHasNextPageCorrectly(int pageNumber, int pageSize, int totalCount, bool expectedHasNext)
50+
{
51+
// Act
52+
var response = new PaginationResponse<string>
53+
{
54+
PageNumber = pageNumber,
55+
PageSize = pageSize,
56+
TotalCount = totalCount
57+
};
58+
59+
// Assert
60+
response.HasNextPage.Should().Be(expectedHasNext);
61+
}
62+
}
63+
}

0 commit comments

Comments
 (0)