Skip to content

Commit a82cd39

Browse files
authored
feat: TagQuery now provides means of skipping tags (#1078)
feat: TagQuery now provides means of skipping initial tags GitLab supports listing tags starting at a given page number or after a certain tag name. Support this here too.
1 parent 9c41cd9 commit a82cd39

File tree

10 files changed

+200
-2
lines changed

10 files changed

+200
-2
lines changed

NGitLab.Mock.Tests/TagTests.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,71 @@ public async Task SearchTags()
135135
Assert.That(tags.Count(), Is.EqualTo(expectedCount), $"Expected search expression '{searchExpression}' to return {expectedCount} results.");
136136
}
137137
}
138+
139+
[Test]
140+
public async Task EnumerateTags_FivePerPageAndWithStartPageSpecified()
141+
{
142+
// Arrange
143+
using var server = new GitLabConfig()
144+
.WithUser("user1", isDefault: true)
145+
.WithProject("test-project", id: 1, addDefaultUserAsMaintainer: true, configure: project =>
146+
{
147+
project.WithCommit("Commit with tags", tags: Enumerable.Range(0, 20).Select(i => $"1.{i}.0").ToArray());
148+
})
149+
.BuildServer();
150+
151+
var client = server.CreateClient();
152+
var tagClient = client.GetRepository(1).Tags;
153+
154+
var perPage = 5;
155+
156+
(int? StartPage, string[] ExpectedTags)[] testCases =
157+
[
158+
(null, ["1.19.0", "1.18.0", "1.17.0", "1.16.0", "1.15.0"]),
159+
(1, ["1.19.0", "1.18.0", "1.17.0", "1.16.0", "1.15.0"]),
160+
(2, ["1.14.0", "1.13.0", "1.12.0", "1.11.0", "1.10.0"]),
161+
(4, ["1.4.0", "1.3.0", "1.2.0", "1.1.0", "1.0.0"]),
162+
(5, []),
163+
];
164+
165+
foreach (var (startPage, expectedTags) in testCases)
166+
{
167+
// Act
168+
var tags = tagClient.GetAsync(new TagQuery { OrderBy = "version", PerPage = perPage, Page = startPage }).AsEnumerable().Take(perPage).ToArray();
169+
170+
// Assert
171+
Assert.That(tags.Select(t => t.Name), Is.EqualTo(expectedTags));
172+
}
173+
}
174+
175+
[Test]
176+
public async Task EnumerateTags_WithPreviousTagSpecified()
177+
{
178+
// Arrange
179+
using var server = new GitLabConfig()
180+
.WithUser("user1", isDefault: true)
181+
.WithProject("test-project", id: 1, addDefaultUserAsMaintainer: true, configure: project =>
182+
{
183+
project.WithCommit("Commit with tags", tags: Enumerable.Range(0, 10).Select(i => $"1.{i}.0").ToArray());
184+
})
185+
.BuildServer();
186+
187+
var client = server.CreateClient();
188+
var tagClient = client.GetRepository(1).Tags;
189+
190+
(string PreviousTag, string[] ExpectedTags)[] testCases =
191+
[
192+
(null, ["1.9.0", "1.8.0", "1.7.0", "1.6.0", "1.5.0", "1.4.0", "1.3.0", "1.2.0", "1.1.0", "1.0.0"]),
193+
("1.3.0", ["1.2.0", "1.1.0", "1.0.0"]),
194+
];
195+
196+
foreach (var (previousTag, expectedTags) in testCases)
197+
{
198+
// Act
199+
var tags = tagClient.GetAsync(new TagQuery { OrderBy = "version", PageToken = previousTag }).AsEnumerable().ToArray();
200+
201+
// Assert
202+
Assert.That(tags.Select(t => t.Name), Is.EqualTo(expectedTags));
203+
}
204+
}
138205
}

NGitLab.Mock/Clients/TagClient.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,12 +141,25 @@ public Tag ToTagClient(LibGit2Sharp.Tag tag)
141141
if (string.IsNullOrEmpty(direction))
142142
direction = "desc";
143143

144-
return direction switch
144+
tags = direction switch
145145
{
146146
"desc" => tags.Reverse(),
147147
"asc" => tags,
148148
_ => throw new NotSupportedException($"Sort direction must be 'asc' or 'desc', got '{direction}' instead"),
149149
};
150+
151+
if (!string.IsNullOrEmpty(query.PageToken))
152+
{
153+
tags = tags.SkipWhile(t => !string.Equals(t.FriendlyName, query.PageToken, StringComparison.Ordinal)).Skip(1);
154+
}
155+
156+
if (query.Page.HasValue)
157+
{
158+
var skip = (query.Page.Value - 1) * (query.PerPage ?? 20);
159+
tags = tags.Skip(skip);
160+
}
161+
162+
return tags;
150163
}
151164
}
152165

NGitLab.Tests/TagTests.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,76 @@ public async Task GetTag(string tagNameSought, bool expectExistence)
125125
Assert.That(ex.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
126126
}
127127
}
128+
129+
[NGitLabRetry]
130+
[Test]
131+
public async Task EnumerateTags_FivePerPageAndWithStartPageSpecified()
132+
{
133+
// Arrange
134+
using var context = await GitLabTestContext.CreateAsync();
135+
var project = context.CreateProject(initializeWithCommits: true);
136+
var tagClient = context.Client.GetRepository(project.Id).Tags;
137+
var perPage = 5;
138+
139+
for (var i = 0; i < 20; i++)
140+
{
141+
tagClient.Create(new TagCreate
142+
{
143+
Name = $"1.{i}.0",
144+
Ref = project.DefaultBranch,
145+
});
146+
}
147+
148+
(int? StartPage, string[] ExpectedTags)[] testCases =
149+
[
150+
(null, ["1.19.0", "1.18.0", "1.17.0", "1.16.0", "1.15.0"]),
151+
(1, ["1.19.0", "1.18.0", "1.17.0", "1.16.0", "1.15.0"]),
152+
(2, ["1.14.0", "1.13.0", "1.12.0", "1.11.0", "1.10.0"]),
153+
(4, ["1.4.0", "1.3.0", "1.2.0", "1.1.0", "1.0.0"]),
154+
(5, []),
155+
];
156+
157+
foreach (var (startPage, expectedTags) in testCases)
158+
{
159+
// Act
160+
var tags = tagClient.GetAsync(new TagQuery { OrderBy = "version", PerPage = perPage, Page = startPage }).AsEnumerable().Take(perPage).ToArray();
161+
162+
// Assert
163+
Assert.That(tags.Select(t => t.Name), Is.EqualTo(expectedTags));
164+
}
165+
}
166+
167+
[NGitLabRetry]
168+
[Test]
169+
public async Task EnumerateTags_WithPreviousTagSpecified()
170+
{
171+
// Arrange
172+
using var context = await GitLabTestContext.CreateAsync();
173+
var project = context.CreateProject(initializeWithCommits: true);
174+
var tagClient = context.Client.GetRepository(project.Id).Tags;
175+
176+
for (var i = 0; i < 10; i++)
177+
{
178+
tagClient.Create(new TagCreate
179+
{
180+
Name = $"1.{i}.0",
181+
Ref = project.DefaultBranch,
182+
});
183+
}
184+
185+
(string PreviousTag, string[] ExpectedTags)[] testCases =
186+
[
187+
(null, ["1.9.0", "1.8.0", "1.7.0", "1.6.0", "1.5.0", "1.4.0", "1.3.0", "1.2.0", "1.1.0", "1.0.0"]),
188+
("1.3.0", ["1.2.0", "1.1.0", "1.0.0"]),
189+
];
190+
191+
foreach (var (previousTag, expectedTags) in testCases)
192+
{
193+
// Act
194+
var tags = tagClient.GetAsync(new TagQuery { OrderBy = "version", PageToken = previousTag }).AsEnumerable().ToArray();
195+
196+
// Assert
197+
Assert.That(tags.Select(t => t.Name), Is.EqualTo(expectedTags));
198+
}
199+
}
128200
}

NGitLab/Impl/TagClient.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public GitLabCollectionResponse<Tag> GetAsync(TagQuery query)
4444
url = Utils.AddParameter(url, "sort", query.Sort);
4545
url = Utils.AddParameter(url, "per_page", query.PerPage);
4646
url = Utils.AddParameter(url, "search", query.Search);
47+
url = Utils.AddParameter(url, "page", query.Page);
48+
url = Utils.AddParameter(url, "page_token", query.PageToken);
4749
}
4850

4951
return _api.Get().GetAllAsync<Tag>(url);

NGitLab/Models/Tag.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
using System.Text.Json.Serialization;
1+
using System.Diagnostics;
2+
using System.Text.Json.Serialization;
23

34
namespace NGitLab.Models;
45

6+
[DebuggerDisplay("{Name,nq}")]
57
public class Tag
68
{
79
[JsonPropertyName("name")]

NGitLab/Models/TagQuery.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,38 @@
11
namespace NGitLab.Models;
22

3+
/// <summary>
4+
/// A filter and sort query when <see href="https://docs.gitlab.com/ee/api/tags.html#list-all-project-repository-tags">
5+
/// listing all project repository tags</see>.
6+
/// </summary>
37
public class TagQuery
48
{
9+
/// <summary>
10+
/// Specifies how to order tags, i.e. by "name", "updated" or (semantic) "version". Default is "updated".
11+
/// </summary>
512
public string OrderBy { get; set; }
613

14+
/// <summary>
15+
/// Sort order, i.e. "asc" or "desc". Default is "desc".
16+
/// </summary>
717
public string Sort { get; set; }
818

19+
/// <summary>
20+
/// Number of results to return per page. Default is 20.
21+
/// </summary>
922
public int? PerPage { get; set; }
1023

24+
/// <summary>
25+
/// Search criteria. You can use "^term" and "term$" to find tags that begin and end with "term". No other regular expressions are supported.
26+
/// </summary>
1127
public string Search { get; set; }
28+
29+
/// <summary>
30+
/// Start page number. Default is 1.
31+
/// </summary>
32+
public int? Page { get; set; }
33+
34+
/// <summary>
35+
/// Previous tag name, i.e. tag to start the pagination from. Used to fetch the next set of results.
36+
/// </summary>
37+
public string PageToken { get; set; }
1238
}

NGitLab/PublicAPI/net10.0/PublicAPI.Unshipped.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4665,6 +4665,10 @@ NGitLab.Models.TagProtect.TagProtect(string name) -> void
46654665
NGitLab.Models.TagQuery
46664666
NGitLab.Models.TagQuery.OrderBy.get -> string
46674667
NGitLab.Models.TagQuery.OrderBy.set -> void
4668+
NGitLab.Models.TagQuery.Page.get -> int?
4669+
NGitLab.Models.TagQuery.Page.set -> void
4670+
NGitLab.Models.TagQuery.PageToken.get -> string
4671+
NGitLab.Models.TagQuery.PageToken.set -> void
46684672
NGitLab.Models.TagQuery.PerPage.get -> int?
46694673
NGitLab.Models.TagQuery.PerPage.set -> void
46704674
NGitLab.Models.TagQuery.Search.get -> string

NGitLab/PublicAPI/net472/PublicAPI.Unshipped.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4666,6 +4666,10 @@ NGitLab.Models.TagProtect.TagProtect(string name) -> void
46664666
NGitLab.Models.TagQuery
46674667
NGitLab.Models.TagQuery.OrderBy.get -> string
46684668
NGitLab.Models.TagQuery.OrderBy.set -> void
4669+
NGitLab.Models.TagQuery.Page.get -> int?
4670+
NGitLab.Models.TagQuery.Page.set -> void
4671+
NGitLab.Models.TagQuery.PageToken.get -> string
4672+
NGitLab.Models.TagQuery.PageToken.set -> void
46694673
NGitLab.Models.TagQuery.PerPage.get -> int?
46704674
NGitLab.Models.TagQuery.PerPage.set -> void
46714675
NGitLab.Models.TagQuery.Search.get -> string

NGitLab/PublicAPI/net8.0/PublicAPI.Unshipped.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4665,6 +4665,10 @@ NGitLab.Models.TagProtect.TagProtect(string name) -> void
46654665
NGitLab.Models.TagQuery
46664666
NGitLab.Models.TagQuery.OrderBy.get -> string
46674667
NGitLab.Models.TagQuery.OrderBy.set -> void
4668+
NGitLab.Models.TagQuery.Page.get -> int?
4669+
NGitLab.Models.TagQuery.Page.set -> void
4670+
NGitLab.Models.TagQuery.PageToken.get -> string
4671+
NGitLab.Models.TagQuery.PageToken.set -> void
46684672
NGitLab.Models.TagQuery.PerPage.get -> int?
46694673
NGitLab.Models.TagQuery.PerPage.set -> void
46704674
NGitLab.Models.TagQuery.Search.get -> string

NGitLab/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4666,6 +4666,10 @@ NGitLab.Models.TagProtect.TagProtect(string name) -> void
46664666
NGitLab.Models.TagQuery
46674667
NGitLab.Models.TagQuery.OrderBy.get -> string
46684668
NGitLab.Models.TagQuery.OrderBy.set -> void
4669+
NGitLab.Models.TagQuery.Page.get -> int?
4670+
NGitLab.Models.TagQuery.Page.set -> void
4671+
NGitLab.Models.TagQuery.PageToken.get -> string
4672+
NGitLab.Models.TagQuery.PageToken.set -> void
46694673
NGitLab.Models.TagQuery.PerPage.get -> int?
46704674
NGitLab.Models.TagQuery.PerPage.set -> void
46714675
NGitLab.Models.TagQuery.Search.get -> string

0 commit comments

Comments
 (0)