From 5c38f36c8e0f0bbb2d73f0e3f12d9318f2b978c0 Mon Sep 17 00:00:00 2001 From: PM Extra Date: Fri, 9 Jun 2023 22:13:26 +0800 Subject: [PATCH 01/57] Remove redundant `projectId` from ContributorClient (#464) https://docs.gitlab.com/ee/api/repositories.html#contributors --- NGitLab/Impl/ContributorClient.cs | 16 ++++++++++------ NGitLab/Impl/RepositoryClient.cs | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/NGitLab/Impl/ContributorClient.cs b/NGitLab/Impl/ContributorClient.cs index 0f88f056..56e6871c 100644 --- a/NGitLab/Impl/ContributorClient.cs +++ b/NGitLab/Impl/ContributorClient.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; -using NGitLab.Extensions; +using System; +using System.Collections.Generic; using NGitLab.Models; namespace NGitLab.Impl @@ -8,18 +8,22 @@ internal sealed class ContributorClient : IContributorClient { private readonly API _api; private readonly string _contributorPath; - private readonly int _projectId; - public ContributorClient(API api, string repoPath, int projectId) + public ContributorClient(API api, string repoPath) { _api = api; _contributorPath = repoPath + Contributor.Url; - _projectId = projectId; + } + + [Obsolete("Argument projectId is redundant, please use ContributorClient(API api, string repoPath) instead.")] + public ContributorClient(API api, string repoPath, int projectId) + : this(api, repoPath) + { } /// /// HACK: We force the order_by and sort due to a pagination bug from GitLab /// - public IEnumerable All => _api.Get().GetAll(_contributorPath + $"?id={_projectId.ToStringInvariant()}&order_by=commits&sort=desc"); + public IEnumerable All => _api.Get().GetAll(_contributorPath + $"?order_by=commits&sort=desc"); } } diff --git a/NGitLab/Impl/RepositoryClient.cs b/NGitLab/Impl/RepositoryClient.cs index 27abfc2f..e66d513c 100644 --- a/NGitLab/Impl/RepositoryClient.cs +++ b/NGitLab/Impl/RepositoryClient.cs @@ -24,7 +24,7 @@ public RepositoryClient(API api, int projectId) public ITagClient Tags => new TagClient(_api, _repoPath); - public IContributorClient Contributors => new ContributorClient(_api, _repoPath, _projectId); + public IContributorClient Contributors => new ContributorClient(_api, _repoPath); public IEnumerable Tree => _api.Get().GetAll(_repoPath + "/tree"); From 49cab0688dbba6556284b3a81f42906db4fd65a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 07:49:13 -0400 Subject: [PATCH 02/57] Bump Microsoft.NET.Test.Sdk from 17.6.1 to 17.6.2 (#467) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.6.1 to 17.6.2. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.6.1...v17.6.2) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj | 2 +- NGitLab.Tests/NGitLab.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj b/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj index a9180fe5..02a3db6f 100644 --- a/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj +++ b/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj @@ -7,7 +7,7 @@ - + diff --git a/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab.Tests/NGitLab.Tests.csproj index 73cb1933..4d1e596c 100644 --- a/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab.Tests/NGitLab.Tests.csproj @@ -14,7 +14,7 @@ - + From 5e2ce5f8129b0ad0f8488f4110f09849eed03f29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 09:28:48 -0400 Subject: [PATCH 03/57] Bump Meziantou.Analyzer from 2.0.57 to 2.0.60 (#468) Bumps [Meziantou.Analyzer](https://github.com/meziantou/Meziantou.Analyzer) from 2.0.57 to 2.0.60. - [Release notes](https://github.com/meziantou/Meziantou.Analyzer/releases) - [Commits](https://github.com/meziantou/Meziantou.Analyzer/compare/2.0.57...2.0.60) --- updated-dependencies: - dependency-name: Meziantou.Analyzer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 82459655..74b95f31 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -42,7 +42,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 4f4d7d0dfed419903e982f4a4417cc73f13505fb Mon Sep 17 00:00:00 2001 From: Louis Zanella Date: Mon, 12 Jun 2023 13:25:31 -0400 Subject: [PATCH 04/57] Run tests on GitLab 15.4.6 (default) & 15.11.6 (#463) --- .github/workflows/ci.yml | 6 +----- NGitLab.Tests/CommitsTests.cs | 8 ++++++-- NGitLab.Tests/Docker/GitLabDockerContainer.cs | 2 +- .../MergeRequestChangesClientTests.cs | 9 ++++++++- .../MergeRequest/MergeRequestClientTests.cs | 19 ++++++++++++++++--- NGitLab.Tests/ProtectedBranchTests.cs | 2 +- 6 files changed, 33 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c3b1a31..36e79fac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,12 +51,8 @@ jobs: # Keep in sync with the version in GitLabDockerContainer.cs # Available tags: https://hub.docker.com/r/gitlab/gitlab-ee/tags gitlab: [ - 'gitlab/gitlab-ee:15.0.5-ee.0', - 'gitlab/gitlab-ee:15.1.6-ee.0', 'gitlab/gitlab-ee:15.4.6-ee.0', - 'gitlab/gitlab-ee:15.6.8-ee.0', - # Several MR-related tests fail against the following version. We need to investigate... - # 'gitlab/gitlab-ee:15.10.0-ee.0', + 'gitlab/gitlab-ee:15.11.6-ee.0', ] configuration: [ Release ] fail-fast: false diff --git a/NGitLab.Tests/CommitsTests.cs b/NGitLab.Tests/CommitsTests.cs index 356965b3..b77dd7d5 100644 --- a/NGitLab.Tests/CommitsTests.cs +++ b/NGitLab.Tests/CommitsTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using NGitLab.Models; @@ -72,7 +73,10 @@ public async Task Test_can_get_merge_request_associated_to_commit() Title = mergeRequestTitle, }); - var mergeRequests = context.Client.GetCommits(project.Id).GetRelatedMergeRequestsAsync(new RelatedMergeRequestsQuery { Sha = commit.Id }); + var mergeRequests = await GitLabTestContext.RetryUntilAsync( + () => context.Client.GetCommits(project.Id).GetRelatedMergeRequestsAsync(new RelatedMergeRequestsQuery { Sha = commit.Id }), + mergeRequests => mergeRequests.Any(), + TimeSpan.FromSeconds(10)); var mergeRequest = mergeRequests.Single(); Assert.AreEqual(mergeRequestTitle, mergeRequest.Title); diff --git a/NGitLab.Tests/Docker/GitLabDockerContainer.cs b/NGitLab.Tests/Docker/GitLabDockerContainer.cs index 5561ed8a..d91bf2e0 100644 --- a/NGitLab.Tests/Docker/GitLabDockerContainer.cs +++ b/NGitLab.Tests/Docker/GitLabDockerContainer.cs @@ -27,7 +27,7 @@ public class GitLabDockerContainer public const string ImageName = "gitlab/gitlab-ee"; // https://hub.docker.com/r/gitlab/gitlab-ee/tags/ - public const string GitLabDockerVersion = "14.10.5-ee.0"; // Keep in sync with .github/workflows/ci.yml + public const string GitLabDockerVersion = "15.4.6-ee.0"; // Keep in sync with .github/workflows/ci.yml private static string s_creationErrorMessage; private static readonly SemaphoreSlim s_setupLock = new(initialCount: 1, maxCount: 1); diff --git a/NGitLab.Tests/MergeRequest/MergeRequestChangesClientTests.cs b/NGitLab.Tests/MergeRequest/MergeRequestChangesClientTests.cs index fcb44a07..90f96152 100644 --- a/NGitLab.Tests/MergeRequest/MergeRequestChangesClientTests.cs +++ b/NGitLab.Tests/MergeRequest/MergeRequestChangesClientTests.cs @@ -1,3 +1,5 @@ +using System; +using System.Linq; using System.Threading.Tasks; using NGitLab.Tests.Docker; using NUnit.Framework; @@ -14,7 +16,12 @@ public async Task GetChangesOnMergeRequest() var (project, mergeRequest) = context.CreateMergeRequest(); var mergeRequestClient = context.Client.GetMergeRequest(project.Id); var mergeRequestChanges = mergeRequestClient.Changes(mergeRequest.Iid); - var changes = mergeRequestChanges.MergeRequestChange.Changes; + + var changes = await GitLabTestContext.RetryUntilAsync( + () => mergeRequestChanges.MergeRequestChange.Changes, + changes => changes.Any(), + TimeSpan.FromSeconds(10)); + Assert.AreEqual(1, changes.Length); Assert.AreEqual(100644, changes[0].AMode); Assert.AreEqual(100644, changes[0].BMode); diff --git a/NGitLab.Tests/MergeRequest/MergeRequestClientTests.cs b/NGitLab.Tests/MergeRequest/MergeRequestClientTests.cs index 3a25c236..666e6d57 100644 --- a/NGitLab.Tests/MergeRequest/MergeRequestClientTests.cs +++ b/NGitLab.Tests/MergeRequest/MergeRequestClientTests.cs @@ -83,7 +83,12 @@ public async Task Test_merge_request_rebase() "There should be a 1-commit divergence between the default branch NOW and its state at the moment the MR was created"); RebaseMergeRequest(mergeRequestClient, mergeRequest); - var commits = mergeRequestClient.Commits(mergeRequest.Iid).All; + + var commits = await GitLabTestContext.RetryUntilAsync( + () => mergeRequestClient.Commits(mergeRequest.Iid).All, + commits => commits.Any(), + TimeSpan.FromSeconds(10)); + Assert.IsTrue(commits.Any(), "Can return the commits"); } @@ -121,7 +126,11 @@ public async Task Test_merge_request_rebaseasync_skip_ci() var rebaseResult = await mergeRequestClient.RebaseAsync(mergeRequest.Iid, new MergeRequestRebase { SkipCi = true }); Assert.IsTrue(rebaseResult.RebaseInProgress); - var commits = mergeRequestClient.Commits(mergeRequest.Iid).All; + var commits = await GitLabTestContext.RetryUntilAsync( + () => mergeRequestClient.Commits(mergeRequest.Iid).All, + commits => commits.Any(), + TimeSpan.FromSeconds(10)); + Assert.IsTrue(commits.Any(), "Can return the commits"); } @@ -276,7 +285,11 @@ public async Task Test_merge_request_versions() var (project, mergeRequest) = context.CreateMergeRequest(); var mergeRequestClient = context.Client.GetMergeRequest(project.Id); - var versions = mergeRequestClient.GetVersionsAsync(mergeRequest.Iid); + var versions = await GitLabTestContext.RetryUntilAsync( + () => mergeRequestClient.GetVersionsAsync(mergeRequest.Iid), + versions => versions.Any(), + TimeSpan.FromSeconds(10)); + var version = versions.First(); Assert.AreEqual(mergeRequest.Sha, version.HeadCommitSha); diff --git a/NGitLab.Tests/ProtectedBranchTests.cs b/NGitLab.Tests/ProtectedBranchTests.cs index eb8055c8..260d72f9 100644 --- a/NGitLab.Tests/ProtectedBranchTests.cs +++ b/NGitLab.Tests/ProtectedBranchTests.cs @@ -33,7 +33,7 @@ public async Task ProtectBranch_Test() { new AccessLevelInfo { - AccessLevel = AccessLevel.NoAccess, + AccessLevel = AccessLevel.Admin, Description = "Example", }, }, From d9a078a5d866bf2bc743d365503a50db3898a389 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 07:41:52 -0400 Subject: [PATCH 05/57] Bump YamlDotNet from 13.1.0 to 13.1.1 (#475) Bumps [YamlDotNet](https://github.com/aaubry/YamlDotNet) from 13.1.0 to 13.1.1. - [Release notes](https://github.com/aaubry/YamlDotNet/releases) - [Commits](https://github.com/aaubry/YamlDotNet/compare/v13.1.0...v13.1.1) --- updated-dependencies: - dependency-name: YamlDotNet dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Mock/NGitLab.Mock.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab.Mock/NGitLab.Mock.csproj b/NGitLab.Mock/NGitLab.Mock.csproj index 4bb1a565..03ea38d3 100644 --- a/NGitLab.Mock/NGitLab.Mock.csproj +++ b/NGitLab.Mock/NGitLab.Mock.csproj @@ -13,7 +13,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 657f04e3cf55ee21646c184b74ebf26d9bd69adb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 08:06:05 -0400 Subject: [PATCH 06/57] Bump Microsoft.CodeAnalysis.NetAnalyzers from 7.0.1 to 7.0.3 (#473) Bumps [Microsoft.CodeAnalysis.NetAnalyzers](https://github.com/dotnet/roslyn-analyzers) from 7.0.1 to 7.0.3. - [Release notes](https://github.com/dotnet/roslyn-analyzers/releases) - [Changelog](https://github.com/dotnet/roslyn-analyzers/blob/main/PostReleaseActivities.md) - [Commits](https://github.com/dotnet/roslyn-analyzers/commits) --- updated-dependencies: - dependency-name: Microsoft.CodeAnalysis.NetAnalyzers dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 74b95f31..0069312f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -37,7 +37,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From ee81f7fe9addaa90753ab358a55a3cfd19c6d101 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 08:34:01 -0400 Subject: [PATCH 07/57] Bump System.Text.Json from 7.0.2 to 7.0.3 (#472) Bumps [System.Text.Json](https://github.com/dotnet/runtime) from 7.0.2 to 7.0.3. - [Release notes](https://github.com/dotnet/runtime/releases) - [Commits](https://github.com/dotnet/runtime/compare/v7.0.2...v7.0.3) --- updated-dependencies: - dependency-name: System.Text.Json dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Tests/NGitLab.Tests.csproj | 2 +- NGitLab/NGitLab.csproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab.Tests/NGitLab.Tests.csproj index 4d1e596c..346c7035 100644 --- a/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab.Tests/NGitLab.Tests.csproj @@ -21,7 +21,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/NGitLab/NGitLab.csproj b/NGitLab/NGitLab.csproj index 4fa8492c..00e52c6f 100644 --- a/NGitLab/NGitLab.csproj +++ b/NGitLab/NGitLab.csproj @@ -1,4 +1,4 @@ - + net461;netstandard2.0 @@ -22,6 +22,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + \ No newline at end of file From ff76aef4ec8ca95783e7611fcdf136942825d318 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 09:13:56 -0400 Subject: [PATCH 08/57] Bump Meziantou.Analyzer from 2.0.60 to 2.0.61 (#470) Bumps [Meziantou.Analyzer](https://github.com/meziantou/Meziantou.Analyzer) from 2.0.60 to 2.0.61. - [Release notes](https://github.com/meziantou/Meziantou.Analyzer/releases) - [Commits](https://github.com/meziantou/Meziantou.Analyzer/compare/2.0.60...2.0.61) --- updated-dependencies: - dependency-name: Meziantou.Analyzer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 0069312f..cb829dbb 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -42,7 +42,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 913578b9ccfd7cf567cc852cb8e36c1fc463c0fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 09:33:58 -0400 Subject: [PATCH 09/57] Bump Polly from 7.2.3 to 7.2.4 (#471) Bumps [Polly](https://github.com/App-vNext/Polly) from 7.2.3 to 7.2.4. - [Release notes](https://github.com/App-vNext/Polly/releases) - [Changelog](https://github.com/App-vNext/Polly/blob/main/CHANGELOG.md) - [Commits](https://github.com/App-vNext/Polly/commits/7.2.4) --- updated-dependencies: - dependency-name: Polly dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Tests/NGitLab.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab.Tests/NGitLab.Tests.csproj index 346c7035..447652e8 100644 --- a/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab.Tests/NGitLab.Tests.csproj @@ -20,7 +20,7 @@ - + all From cb93e94c2ce48d0fad2c7355e23a960917fcad71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 09:55:22 -0400 Subject: [PATCH 10/57] Bump NuGet.Versioning from 6.6.0 to 6.6.1 (#474) Bumps [NuGet.Versioning](https://github.com/NuGet/NuGet.Client) from 6.6.0 to 6.6.1. - [Release notes](https://github.com/NuGet/NuGet.Client/releases) - [Commits](https://github.com/NuGet/NuGet.Client/commits) --- updated-dependencies: - dependency-name: NuGet.Versioning dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Tests/NGitLab.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab.Tests/NGitLab.Tests.csproj index 447652e8..ffe0fbff 100644 --- a/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab.Tests/NGitLab.Tests.csproj @@ -16,7 +16,7 @@ - + From a236ed8f14e7ecf538383a6ce7d882c0f7025682 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jun 2023 07:43:22 -0400 Subject: [PATCH 11/57] Bump Microsoft.Playwright from 1.34.0 to 1.35.0 (#469) Bumps [Microsoft.Playwright](https://github.com/microsoft/playwright-dotnet) from 1.34.0 to 1.35.0. - [Release notes](https://github.com/microsoft/playwright-dotnet/releases) - [Commits](https://github.com/microsoft/playwright-dotnet/compare/v1.34.0...v1.35.0) --- updated-dependencies: - dependency-name: Microsoft.Playwright dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Tests/NGitLab.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab.Tests/NGitLab.Tests.csproj index ffe0fbff..fbf7392a 100644 --- a/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab.Tests/NGitLab.Tests.csproj @@ -15,7 +15,7 @@ - + From 189c9aed3c80f14ff8f51aa0f4dc8dd6681e115b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 07:48:02 -0400 Subject: [PATCH 12/57] Bump StyleCop.Analyzers from 1.2.0-beta.435 to 1.2.0-beta.507 (#476) Bumps [StyleCop.Analyzers](https://github.com/DotNetAnalyzers/StyleCopAnalyzers) from 1.2.0-beta.435 to 1.2.0-beta.507. - [Release notes](https://github.com/DotNetAnalyzers/StyleCopAnalyzers/releases) - [Changelog](https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/KnownChanges.md) - [Commits](https://github.com/DotNetAnalyzers/StyleCopAnalyzers/compare/1.2.0-beta.435...1.2.0-beta.507) --- updated-dependencies: - dependency-name: StyleCop.Analyzers dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index cb829dbb..d0353fdc 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -47,7 +47,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 765b5028abedfeef727ee6c823ecba14aa6bd6ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 08:21:29 -0400 Subject: [PATCH 13/57] Bump Meziantou.Analyzer from 2.0.61 to 2.0.62 (#477) Bumps [Meziantou.Analyzer](https://github.com/meziantou/Meziantou.Analyzer) from 2.0.61 to 2.0.62. - [Release notes](https://github.com/meziantou/Meziantou.Analyzer/releases) - [Commits](https://github.com/meziantou/Meziantou.Analyzer/compare/2.0.61...2.0.62) --- updated-dependencies: - dependency-name: Meziantou.Analyzer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index d0353fdc..c2913c21 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -42,7 +42,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 0244ae795a798d2e35e4d0a1aa3e5e16bfc633a4 Mon Sep 17 00:00:00 2001 From: msrouchou Date: Tue, 27 Jun 2023 13:06:35 -0400 Subject: [PATCH 14/57] Mocked Projects are forkable by default (#478) --- NGitLab.Mock/Project.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab.Mock/Project.cs b/NGitLab.Mock/Project.cs index 197e07e8..fc3c5789 100644 --- a/NGitLab.Mock/Project.cs +++ b/NGitLab.Mock/Project.cs @@ -68,7 +68,7 @@ public string DefaultBranch public Project ForkedFrom { get; internal set; } - public RepositoryAccessLevel ForkingAccessLevel { get; set; } + public RepositoryAccessLevel ForkingAccessLevel { get; set; } = RepositoryAccessLevel.Enabled; public string ImportStatus { get; set; } From d285f683221c981586f2eb4ef0b54f16fe37e148 Mon Sep 17 00:00:00 2001 From: Thomas Cortes <78750681+Toa741@users.noreply.github.com> Date: Tue, 27 Jun 2023 13:37:05 -0400 Subject: [PATCH 15/57] Add Bot property in User mock (#456) * Add Bot property in User mock * Add bot user * Add Project bot creation --- NGitLab.Mock.Tests/BotUserTests.cs | 41 ++++++++++++++++++++++++++++ NGitLab.Mock/Group.cs | 17 ++++++++++++ NGitLab.Mock/Project.cs | 19 +++++++++++++ NGitLab.Mock/PublicAPI.Unshipped.txt | 3 ++ NGitLab.Mock/User.cs | 20 ++++++++++++++ 5 files changed, 100 insertions(+) create mode 100644 NGitLab.Mock.Tests/BotUserTests.cs diff --git a/NGitLab.Mock.Tests/BotUserTests.cs b/NGitLab.Mock.Tests/BotUserTests.cs new file mode 100644 index 00000000..97489197 --- /dev/null +++ b/NGitLab.Mock.Tests/BotUserTests.cs @@ -0,0 +1,41 @@ +using NGitLab.Models; +using NUnit.Framework; + +namespace NGitLab.Mock.Tests +{ + public sealed class BotUserTests + { + [Test] + public void Test_project_bot_user() + { + using var server = new GitLabServer(); + var group = new Group("test"); + var project = new Project("test-project"); + server.Groups.Add(group); + group.Projects.Add(project); + + var bot = project.CreateBotUser("token_name", AccessLevel.Maintainer); + + Assert.That(bot.Bot, Is.True); + Assert.That(bot.Name, Is.EqualTo("token_name")); + var permissions = project.GetEffectivePermissions(); + var botPermission = permissions.GetEffectivePermission(bot); + Assert.That(botPermission.AccessLevel, Is.EqualTo(AccessLevel.Maintainer)); + } + + [Test] + public void Test_group_bot_user() + { + using var server = new GitLabServer(); + var group = new Group("test"); + server.Groups.Add(group); + + var bot = group.CreateBotUser(AccessLevel.Maintainer); + + Assert.That(bot.Bot, Is.True); + var permissions = group.GetEffectivePermissions(); + var botPermission = permissions.GetEffectivePermission(bot); + Assert.That(botPermission.AccessLevel, Is.EqualTo(AccessLevel.Maintainer)); + } + } +} diff --git a/NGitLab.Mock/Group.cs b/NGitLab.Mock/Group.cs index fb9908b8..a1e2bf3b 100644 --- a/NGitLab.Mock/Group.cs +++ b/NGitLab.Mock/Group.cs @@ -254,5 +254,22 @@ public Models.Group ToClientGroup(User currentUser) SharedRunnersMinutesLimit = (int)SharedRunnersLimit.TotalMinutes, }; } + + /// + /// https://docs.gitlab.com/ee/user/group/settings/group_access_tokens.html#bot-users-for-groups + /// + /// AccessLevel to give to the bot user + /// Bot user that have been added to the group + public User CreateBotUser(AccessLevel accessLevel) + { + var botUsername = $"group_{Id}_bot_{Guid.NewGuid():D}"; + var bot = new User(botUsername) + { + Email = $"{botUsername}@noreply.example.com", + }; + Permissions.Add(new Permission(bot, accessLevel)); + Server.Users.Add(bot); + return bot; + } } } diff --git a/NGitLab.Mock/Project.cs b/NGitLab.Mock/Project.cs index fc3c5789..370aa69f 100644 --- a/NGitLab.Mock/Project.cs +++ b/NGitLab.Mock/Project.cs @@ -399,6 +399,25 @@ public Project Fork(Group group, User user, string projectName) return newProject; } + /// + /// https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html#bot-users-for-projects + /// + /// Name of the token + /// AccessLevel to give to the bot user + /// Bot user that have been added to the project + public User CreateBotUser(string tokenName, AccessLevel accessLevel) + { + var botUsername = $"project_{Id}_bot_{Guid.NewGuid():D}"; + var bot = new User(botUsername) + { + Name = tokenName, + Email = $"{botUsername}@noreply.example.com", + }; + Permissions.Add(new Permission(bot, accessLevel)); + Server.Users.Add(bot); + return bot; + } + public Models.Project ToClientProject(User currentUser) { var kind = Group.IsUserNamespace ? "user" : "group"; diff --git a/NGitLab.Mock/PublicAPI.Unshipped.txt b/NGitLab.Mock/PublicAPI.Unshipped.txt index 0410bf1e..eda78948 100644 --- a/NGitLab.Mock/PublicAPI.Unshipped.txt +++ b/NGitLab.Mock/PublicAPI.Unshipped.txt @@ -424,6 +424,7 @@ NGitLab.Mock.Group.CanUserAddProject(NGitLab.Mock.User user) -> bool NGitLab.Mock.Group.CanUserDeleteGroup(NGitLab.Mock.User user) -> bool NGitLab.Mock.Group.CanUserEditGroup(NGitLab.Mock.User user) -> bool NGitLab.Mock.Group.CanUserViewGroup(NGitLab.Mock.User user) -> bool +NGitLab.Mock.Group.CreateBotUser(NGitLab.Models.AccessLevel accessLevel) -> NGitLab.Mock.User NGitLab.Mock.Group.DescendantGroups.get -> System.Collections.Generic.IEnumerable NGitLab.Mock.Group.Description.get -> string NGitLab.Mock.Group.Description.set -> void @@ -787,6 +788,7 @@ NGitLab.Mock.Project.CanUserViewConfidentialIssues(NGitLab.Mock.User user) -> bo NGitLab.Mock.Project.CanUserViewProject(NGitLab.Mock.User user) -> bool NGitLab.Mock.Project.CommitInfos.get -> NGitLab.Mock.CommitInfoCollection NGitLab.Mock.Project.CommitStatuses.get -> NGitLab.Mock.CommitStatusCollection +NGitLab.Mock.Project.CreateBotUser(string tokenName, NGitLab.Models.AccessLevel accessLevel) -> NGitLab.Mock.User NGitLab.Mock.Project.CreateMergeRequest(NGitLab.Mock.User user, string title, string description, string targetBranchName, string sourceBranchName, NGitLab.Mock.Project sourceProject = null) -> NGitLab.Mock.MergeRequest NGitLab.Mock.Project.CreateNewMergeRequest(NGitLab.Mock.User user, string branchName, string targetBranch, string title, string description) -> NGitLab.Mock.MergeRequest NGitLab.Mock.Project.DefaultBranch.get -> string @@ -1113,6 +1115,7 @@ NGitLab.Mock.TextFile.TextFile(string path, string content, System.Text.Encoding NGitLab.Mock.User NGitLab.Mock.User.AvatarUrl.get -> string NGitLab.Mock.User.AvatarUrl.set -> void +NGitLab.Mock.User.Bot.get -> bool NGitLab.Mock.User.CreatedAt.get -> System.DateTime NGitLab.Mock.User.CreatedAt.set -> void NGitLab.Mock.User.Email.get -> string diff --git a/NGitLab.Mock/User.cs b/NGitLab.Mock/User.cs index a42436f8..a5d2f8d7 100644 --- a/NGitLab.Mock/User.cs +++ b/NGitLab.Mock/User.cs @@ -34,6 +34,25 @@ public User(string userName) public string WebUrl => Server.MakeUrl(UserName); + public bool Bot + { + get + { + if (string.IsNullOrEmpty(UserName)) + return false; + + var nameParts = UserName.Split('_'); + + if (nameParts.Length != 4) + return false; + + if (!string.Equals(nameParts[0], "project", StringComparison.Ordinal) && !string.Equals(nameParts[0], "group", StringComparison.Ordinal)) + return false; + + return int.TryParse(nameParts[1], out var _) && string.Equals("bot", nameParts[2], StringComparison.Ordinal); + } + } + public Models.User ToClientUser() { var user = new Models.User(); @@ -59,6 +78,7 @@ private void CopyTo(T instance) instance.AvatarURL = AvatarUrl; instance.CreatedAt = CreatedAt; instance.Identities = Identities; + instance.Bot = Bot; if (IsAdmin) { From 92cf23d9d981e397289329016de6b8382ed8f37f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Jun 2023 21:13:53 +0000 Subject: [PATCH 16/57] Bump LibGit2Sharp from 0.27.0-preview-0182 to 0.27.2 (#424) * Bump LibGit2Sharp from 0.27.0-preview-0182 to 0.27.2 Bumps [LibGit2Sharp](https://github.com/libgit2/libgit2sharp) from 0.27.0-preview-0182 to 0.27.2. - [Release notes](https://github.com/libgit2/libgit2sharp/releases) - [Changelog](https://github.com/libgit2/libgit2sharp/blob/master/CHANGES.md) - [Commits](https://github.com/libgit2/libgit2sharp/commits/0.27.2) --- updated-dependencies: - dependency-name: LibGit2Sharp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Update TFMs * fix: analyzers * Revert some of the previous changes --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Julien Richard Co-authored-by: Louis Zanella --- NGitLab.Mock/NGitLab.Mock.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/NGitLab.Mock/NGitLab.Mock.csproj b/NGitLab.Mock/NGitLab.Mock.csproj index 03ea38d3..c182495d 100644 --- a/NGitLab.Mock/NGitLab.Mock.csproj +++ b/NGitLab.Mock/NGitLab.Mock.csproj @@ -1,14 +1,14 @@ - net461;netstandard2.0 - $(NoWarn);NU5104 + net472;netstandard2.0 + $(NoWarn);NU1701;NU5104 GitLab REST API .NET Client Mock Library - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -16,7 +16,7 @@ - + all runtime; build; native; contentfiles; analyzers From 53e70dfe5a6a54deb6343eb57ee5e89fbd35f3c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 07:42:51 -0400 Subject: [PATCH 17/57] Bump Microsoft.NET.Test.Sdk from 17.6.2 to 17.6.3 (#479) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.6.2 to 17.6.3. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.6.2...v17.6.3) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj | 2 +- NGitLab.Tests/NGitLab.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj b/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj index 02a3db6f..cafc9abd 100644 --- a/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj +++ b/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj @@ -7,7 +7,7 @@ - + diff --git a/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab.Tests/NGitLab.Tests.csproj index fbf7392a..29a1a1e8 100644 --- a/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab.Tests/NGitLab.Tests.csproj @@ -14,7 +14,7 @@ - + From 6c6c4dfab982ce57e262cf8690a716a2bc9965a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 08:07:32 -0400 Subject: [PATCH 18/57] Bump Meziantou.Analyzer from 2.0.62 to 2.0.63 (#483) Bumps [Meziantou.Analyzer](https://github.com/meziantou/Meziantou.Analyzer) from 2.0.62 to 2.0.63. - [Release notes](https://github.com/meziantou/Meziantou.Analyzer/releases) - [Commits](https://github.com/meziantou/Meziantou.Analyzer/compare/2.0.62...2.0.63) --- updated-dependencies: - dependency-name: Meziantou.Analyzer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index c2913c21..4f67fab1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -42,7 +42,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From e0f33e95862a1f38c37d60ec8edd54239c8838e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Myka=C3=ABl=20Lemieux-Lafontaine?= <132388133+mlemieuxlafontaine-ubi@users.noreply.github.com> Date: Mon, 10 Jul 2023 10:09:03 -0400 Subject: [PATCH 19/57] Add Merge Trains Properties in projects (#481) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Merge Trains Properties in projects * Use Getters * Remove test Merge Trains are a Premium feature, not availible in our free docker images --------- Co-authored-by: Gérald Barré --- NGitLab/Models/Project.cs | 6 ++++++ NGitLab/Models/ProjectCreate.cs | 6 ++++++ NGitLab/Models/ProjectUpdate.cs | 6 ++++++ NGitLab/PublicAPI.Unshipped.txt | 8 ++++++++ 4 files changed, 26 insertions(+) diff --git a/NGitLab/Models/Project.cs b/NGitLab/Models/Project.cs index b4fc76d4..4a64c029 100644 --- a/NGitLab/Models/Project.cs +++ b/NGitLab/Models/Project.cs @@ -69,6 +69,9 @@ public class Project [JsonPropertyName("issues_access_level")] public string IssuesAccessLevel; + [JsonPropertyName("merge_pipelines_enabled")] + public bool MergePipelinesEnabled; + [JsonPropertyName("merge_requests_enabled")] [Obsolete("Deprecated by GitLab. Use MergeRequestsAccessLevel instead")] public bool MergeRequestsEnabled; @@ -76,6 +79,9 @@ public class Project [JsonPropertyName("merge_requests_access_level")] public string MergeRequestsAccessLevel; + [JsonPropertyName("merge_trains_enabled")] + public bool MergeTrainsEnabled; + [JsonPropertyName("repository_access_level")] public RepositoryAccessLevel RepositoryAccessLevel; diff --git a/NGitLab/Models/ProjectCreate.cs b/NGitLab/Models/ProjectCreate.cs index 71e9ed70..965968f6 100644 --- a/NGitLab/Models/ProjectCreate.cs +++ b/NGitLab/Models/ProjectCreate.cs @@ -35,6 +35,9 @@ public class ProjectCreate [JsonIgnore] public bool WallEnabled; + [JsonPropertyName("merge_pipelines_enabled")] + public bool MergePipelinesEnabled; + [JsonPropertyName("merge_requests_enabled")] [Obsolete("Deprecated by GitLab. Use MergeRequestsAccessLevel instead")] public bool MergeRequestsEnabled; @@ -42,6 +45,9 @@ public class ProjectCreate [JsonPropertyName("merge_requests_access_level")] public string MergeRequestsAccessLevel; + [JsonPropertyName("merge_trains_enabled")] + public bool MergeTrainsEnabled; + [JsonPropertyName("snippets_enabled")] [Obsolete("Deprecated by GitLab. Use SnippetsAccessLevel instead")] public bool SnippetsEnabled; diff --git a/NGitLab/Models/ProjectUpdate.cs b/NGitLab/Models/ProjectUpdate.cs index 0eda8afa..cd78f8b9 100644 --- a/NGitLab/Models/ProjectUpdate.cs +++ b/NGitLab/Models/ProjectUpdate.cs @@ -25,6 +25,9 @@ public sealed class ProjectUpdate [JsonPropertyName("issues_access_level")] public string IssuesAccessLeve { get; set; } + [JsonPropertyName("merge_pipelines_enabled")] + public bool MergePipelinesEnabled { get; set; } + [JsonPropertyName("merge_requests_enabled")] [Obsolete("Deprecated by GitLab. Use MergeRequestsAccessLevel instead")] public bool? MergeRequestsEnabled { get; set; } @@ -32,6 +35,9 @@ public sealed class ProjectUpdate [JsonPropertyName("merge_requests_access_level")] public string MergeRequestsAccessLevel { get; set; } + [JsonPropertyName("merge_trains_enabled")] + public bool MergeTrainsEnabled { get; set; } + [JsonPropertyName("jobs_enabled")] [Obsolete("Deprecated by GitLab. Use BuildsAccessLevel instead")] public bool? JobsEnabled { get; set; } diff --git a/NGitLab/PublicAPI.Unshipped.txt b/NGitLab/PublicAPI.Unshipped.txt index 28e05a85..56e8ab64 100644 --- a/NGitLab/PublicAPI.Unshipped.txt +++ b/NGitLab/PublicAPI.Unshipped.txt @@ -2532,8 +2532,10 @@ NGitLab.Models.Project.LastActivityAt -> System.DateTime NGitLab.Models.Project.LfsEnabled -> bool NGitLab.Models.Project.Links -> NGitLab.Models.ProjectLinks NGitLab.Models.Project.MergeMethod -> string +NGitLab.Models.Project.MergePipelinesEnabled -> bool NGitLab.Models.Project.MergeRequestsAccessLevel -> string NGitLab.Models.Project.MergeRequestsEnabled -> bool +NGitLab.Models.Project.MergeTrainsEnabled -> bool NGitLab.Models.Project.Mirror -> bool NGitLab.Models.Project.MirrorOverwritesDivergedBranches -> bool NGitLab.Models.Project.MirrorTriggerBuilds -> bool @@ -2580,8 +2582,10 @@ NGitLab.Models.ProjectCreate.Description -> string NGitLab.Models.ProjectCreate.ImportUrl -> string NGitLab.Models.ProjectCreate.IssuesAccessLevel -> string NGitLab.Models.ProjectCreate.IssuesEnabled -> bool +NGitLab.Models.ProjectCreate.MergePipelinesEnabled -> bool NGitLab.Models.ProjectCreate.MergeRequestsAccessLevel -> string NGitLab.Models.ProjectCreate.MergeRequestsEnabled -> bool +NGitLab.Models.ProjectCreate.MergeTrainsEnabled -> bool NGitLab.Models.ProjectCreate.Name -> string NGitLab.Models.ProjectCreate.NamespaceId -> string NGitLab.Models.ProjectCreate.Path -> string @@ -2748,10 +2752,14 @@ NGitLab.Models.ProjectUpdate.JobsEnabled.get -> bool? NGitLab.Models.ProjectUpdate.JobsEnabled.set -> void NGitLab.Models.ProjectUpdate.LfsEnabled.get -> bool? NGitLab.Models.ProjectUpdate.LfsEnabled.set -> void +NGitLab.Models.ProjectUpdate.MergePipelinesEnabled.get -> bool +NGitLab.Models.ProjectUpdate.MergePipelinesEnabled.set -> void NGitLab.Models.ProjectUpdate.MergeRequestsAccessLevel.get -> string NGitLab.Models.ProjectUpdate.MergeRequestsAccessLevel.set -> void NGitLab.Models.ProjectUpdate.MergeRequestsEnabled.get -> bool? NGitLab.Models.ProjectUpdate.MergeRequestsEnabled.set -> void +NGitLab.Models.ProjectUpdate.MergeTrainsEnabled.get -> bool +NGitLab.Models.ProjectUpdate.MergeTrainsEnabled.set -> void NGitLab.Models.ProjectUpdate.Name.get -> string NGitLab.Models.ProjectUpdate.Name.set -> void NGitLab.Models.ProjectUpdate.OnlyAllowMergeIfAllDiscussionsAreResolved.get -> bool? From 1f6b60df43acd9cd1423992fe2d9733f633a77ce Mon Sep 17 00:00:00 2001 From: Louis Zanella Date: Mon, 10 Jul 2023 10:46:22 -0400 Subject: [PATCH 20/57] Run tests on 15.11.9 (#484) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36e79fac..dbaeed57 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,7 +52,7 @@ jobs: # Available tags: https://hub.docker.com/r/gitlab/gitlab-ee/tags gitlab: [ 'gitlab/gitlab-ee:15.4.6-ee.0', - 'gitlab/gitlab-ee:15.11.6-ee.0', + 'gitlab/gitlab-ee:15.11.9-ee.0', ] configuration: [ Release ] fail-fast: false From 08bdbdd8bf24271e9ae763fb206f68f4a465d9b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Tue, 11 Jul 2023 07:36:00 -0400 Subject: [PATCH 21/57] Use PackageLicenseExpression (#485) --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 4f67fab1..24271c77 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,7 +16,7 @@ true - LICENSE + MIT README.md From 04c632b4912d0249d79f210f7d677f2d2f15d901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Tue, 11 Jul 2023 16:41:05 -0400 Subject: [PATCH 22/57] Workaround for an exception in libgit2sharp (#486) --- .../Clients/LibGit2SharpExtensions.cs | 21 ++++++++++++++++++- NGitLab.Mock/Repository.cs | 2 +- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/NGitLab.Mock/Clients/LibGit2SharpExtensions.cs b/NGitLab.Mock/Clients/LibGit2SharpExtensions.cs index d58a3845..6b86a8c8 100644 --- a/NGitLab.Mock/Clients/LibGit2SharpExtensions.cs +++ b/NGitLab.Mock/Clients/LibGit2SharpExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using NGitLab.Models; @@ -62,7 +63,25 @@ public static Models.CommitInfo ToCommitInfo(this LibGit2Sharp.Commit commit) internal static LibGit2Sharp.Commit GetLastCommitForFileChanges(this LibGit2Sharp.Repository repository, string filePath) { - return repository.Commits.QueryBy(filePath).FirstOrDefault()?.Commit; + try + { + return repository.Commits.QueryBy(filePath).FirstOrDefault()?.Commit; + } + catch (KeyNotFoundException) + { + // LibGit2Sharp sometimes fails with the following exception + // System.Collections.Generic.KeyNotFoundException: The given key '1d08df45e551942eaa70d9f5ab6f5f7665a3f5b3' was not present in the dictionary. + // at System.Collections.Generic.Dictionary`2.get_Item(TKey key) + // at LibGit2Sharp.Core.FileHistory.FullHistory(IRepository repo, String path, CommitFilter filter)+MoveNext() in /_/LibGit2Sharp/Core/FileHistory.cs:line 120 + // at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Boolean& found) + // at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source) + // at NGitLab.Mock.Clients.LibGit2SharpExtensions.GetLastCommitForFileChanges(Repository repository, String filePath) in /_/NGitLab.Mock/Clients/LibGit2SharpExtensions.cs:line 65 + // at NGitLab.Mock.Repository.GetFile(String filePath, String ref) in /_/NGitLab.Mock/Repository.cs:line 485 + // at NGitLab.Mock.Clients.FileClient.Get(String filePath, String ref) in /_/NGitLab.Mock/Clients/FileClient.cs:line 77 + // at NGitLab.Mock.Clients.FileClient.GetAsync(String filePath, String ref, CancellationToken cancellationToken) in /_/NGitLab.Mock/Clients/FileClient.cs:line 125 + } + + return null; } } } diff --git a/NGitLab.Mock/Repository.cs b/NGitLab.Mock/Repository.cs index fb7183f9..9808a9c2 100644 --- a/NGitLab.Mock/Repository.cs +++ b/NGitLab.Mock/Repository.cs @@ -492,7 +492,7 @@ public FileData GetFile(string filePath, string @ref) Ref = @ref, BlobId = ((Blob)commit[filePath].Target).Id.ToString(), CommitId = commit.Sha, - LastCommitId = repo.GetLastCommitForFileChanges(filePath).Sha, + LastCommitId = repo.GetLastCommitForFileChanges(filePath)?.Sha, }; } From 279ac08a363468856b272f9a65df860ef3806527 Mon Sep 17 00:00:00 2001 From: Louis Zanella Date: Wed, 12 Jul 2023 10:27:46 -0400 Subject: [PATCH 23/57] Add support for detailed_merge_status (#487) * Add support for detailed_merge_status * Modify a test to validate presence of detailed_merge_status --- Directory.Build.props | 2 +- NGitLab.Tests/Docker/GitLabTestContext.cs | 19 +- .../MergeRequest/MergeRequestClientTests.cs | 8 +- .../ProjectLevelApprovalRulesClientTests.cs | 2 +- NGitLab/Models/DetailedMergeStatus.cs | 36 ++++ NGitLab/Models/MergeRequest.cs | 189 +++++++++--------- NGitLab/PublicAPI.Unshipped.txt | 16 ++ 7 files changed, 167 insertions(+), 105 deletions(-) create mode 100644 NGitLab/Models/DetailedMergeStatus.cs diff --git a/Directory.Build.props b/Directory.Build.props index 24271c77..39cc8e79 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -8,7 +8,7 @@ $(Company), NGitLab contributors - 9.0 + 10.0 true strict true diff --git a/NGitLab.Tests/Docker/GitLabTestContext.cs b/NGitLab.Tests/Docker/GitLabTestContext.cs index be631d67..e1c1e8c8 100644 --- a/NGitLab.Tests/Docker/GitLabTestContext.cs +++ b/NGitLab.Tests/Docker/GitLabTestContext.cs @@ -255,13 +255,20 @@ public int GetRandomNumber() return RandomNumberGenerator.GetInt32(0, int.MaxValue); } - public void ReportTestAsInconclusiveIfVersionOutOfRange(VersionRange versionRange) + public bool IsGitLabVersionInRange(VersionRange versionRange, out string gitLabVersion) { - var gitLabVersion = Client.Version.Get(); - if (!NuGetVersion.TryParse(gitLabVersion.Version, out var currentVersion)) - return; - if (!versionRange.Satisfies(currentVersion)) - Assert.Inconclusive($"Test supported in version range '{versionRange}', but currently running against '{currentVersion}'"); + var currentVersion = Client.Version.Get(); + gitLabVersion = currentVersion.Version; + + // Although a GitLab version is not a NuGet version, let's consider it as one to determine range inclusion + return NuGetVersion.TryParse(gitLabVersion, out var nuGetVersion) && + versionRange.Satisfies(nuGetVersion); + } + + public void ReportTestAsInconclusiveIfGitLabVersionOutOfRange(VersionRange versionRange) + { + if (!IsGitLabVersionInRange(versionRange, out var gitLabVersion)) + Assert.Inconclusive($"Test supported by GitLab '{versionRange}', but currently running against '{gitLabVersion}'"); } private IGitLabClient CreateClient(string token) diff --git a/NGitLab.Tests/MergeRequest/MergeRequestClientTests.cs b/NGitLab.Tests/MergeRequest/MergeRequestClientTests.cs index 666e6d57..0586acf0 100644 --- a/NGitLab.Tests/MergeRequest/MergeRequestClientTests.cs +++ b/NGitLab.Tests/MergeRequest/MergeRequestClientTests.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Net; using System.Threading.Tasks; -using Meziantou.Framework.Versioning; using NGitLab.Models; using NGitLab.Tests.Docker; using NuGet.Versioning; @@ -49,6 +48,11 @@ await GitLabTestContext.RetryUntilAsync( // .ConfigureAwait(false); var commits = mergeRequestClient.Commits(mergeRequest.Iid).All; Assert.IsTrue(commits.Any(), "Can return the commits"); + + if (context.IsGitLabVersionInRange(VersionRange.Parse("[15.6,)"), out _)) + Assert.IsNotNull(mergeRequest.DetailedMergeStatus.EnumValue); + else + Assert.IsNull(mergeRequest.DetailedMergeStatus.EnumValue); } [Test] @@ -187,7 +191,7 @@ public async Task Test_merge_request_approvers() using var context = await GitLabTestContext.CreateAsync(); // https://about.gitlab.com/releases/2021/04/22/gitlab-13-11-released/#removal-of-merge-request-approvers-endpoint-in-favor-of-approval-rules-api - context.ReportTestAsInconclusiveIfVersionOutOfRange(VersionRange.Parse("[,13.11)")); + context.ReportTestAsInconclusiveIfGitLabVersionOutOfRange(VersionRange.Parse("[,13.11)")); var (project, mergeRequest) = context.CreateMergeRequest(); var mergeRequestClient = context.Client.GetMergeRequest(project.Id); diff --git a/NGitLab.Tests/ProjectLevelApprovalRulesClientTests.cs b/NGitLab.Tests/ProjectLevelApprovalRulesClientTests.cs index 3383c59a..846c3031 100644 --- a/NGitLab.Tests/ProjectLevelApprovalRulesClientTests.cs +++ b/NGitLab.Tests/ProjectLevelApprovalRulesClientTests.cs @@ -16,7 +16,7 @@ public class ProjectLevelApprovalRulesClientTests public async Task SetUp() { context = await GitLabTestContext.CreateAsync(); - context.ReportTestAsInconclusiveIfVersionOutOfRange(SupportedVersionRange); + context.ReportTestAsInconclusiveIfGitLabVersionOutOfRange(SupportedVersionRange); } [TearDown] diff --git a/NGitLab/Models/DetailedMergeStatus.cs b/NGitLab/Models/DetailedMergeStatus.cs new file mode 100644 index 00000000..54f3504e --- /dev/null +++ b/NGitLab/Models/DetailedMergeStatus.cs @@ -0,0 +1,36 @@ +using System.Runtime.Serialization; + +namespace NGitLab.Models; + +/// +/// Values that represent the 'detailed_merge_status' potential values. +/// +public enum DetailedMergeStatus +{ + [EnumMember(Value = "blocked_status")] + BlockedStatus, + [EnumMember(Value = "broken_status")] + BrokenStatus, + [EnumMember(Value = "checking")] + Checking, + [EnumMember(Value = "unchecked")] + Unchecked, + [EnumMember(Value = "ci_must_pass")] + CiMustPass, + [EnumMember(Value = "ci_still_running")] + CiStillRunning, + [EnumMember(Value = "discussions_not_resolved")] + DiscussionsNotResolved, + [EnumMember(Value = "draft_status")] + DraftStatus, + [EnumMember(Value = "external_status_checks")] + ExternalStatusChecks, + [EnumMember(Value = "mergeable")] + Mergeable, + [EnumMember(Value = "not_approved")] + NotApproved, + [EnumMember(Value = "not_open")] + NotOpen, + [EnumMember(Value = "policies_denied")] + PoliciesDenied, +} diff --git a/NGitLab/Models/MergeRequest.cs b/NGitLab/Models/MergeRequest.cs index 5700a68e..ff8095f8 100644 --- a/NGitLab/Models/MergeRequest.cs +++ b/NGitLab/Models/MergeRequest.cs @@ -2,144 +2,143 @@ using System.Text.Json.Serialization; using NGitLab.Extensions; -namespace NGitLab.Models +namespace NGitLab.Models; + +public class MergeRequest { - public class MergeRequest - { - public const string Url = "/merge_requests"; + public const string Url = "/merge_requests"; + + [JsonPropertyName("id")] + public int Id; - [JsonPropertyName("id")] - public int Id; + [JsonPropertyName("iid")] + public int Iid; - [JsonPropertyName("iid")] - public int Iid; + [JsonPropertyName("state")] + public string State; - [JsonPropertyName("state")] - public string State; + [JsonPropertyName("title")] + public string Title; - [JsonPropertyName("title")] - public string Title; + [JsonPropertyName("assignee")] + public User Assignee; - [JsonPropertyName("assignee")] - public User Assignee; + [JsonPropertyName("author")] + public User Author; - [JsonPropertyName("author")] - public User Author; + [JsonPropertyName("created_at")] + public DateTime CreatedAt; - [JsonPropertyName("created_at")] - public DateTime CreatedAt; + [JsonPropertyName("description")] + public string Description; - [JsonPropertyName("description")] - public string Description; + [JsonPropertyName("downvotes")] + public int Downvotes; - [JsonPropertyName("downvotes")] - public int Downvotes; + [JsonPropertyName("upvotes")] + public int Upvotes; - [JsonPropertyName("upvotes")] - public int Upvotes; + [JsonPropertyName("updated_at")] + public DateTime UpdatedAt; - [JsonPropertyName("updated_at")] - public DateTime UpdatedAt; + [JsonPropertyName("target_branch")] + public string TargetBranch; - [JsonPropertyName("target_branch")] - public string TargetBranch; + [JsonPropertyName("source_branch")] + public string SourceBranch; - [JsonPropertyName("source_branch")] - public string SourceBranch; + [JsonPropertyName("project_id")] + public int ProjectId; - [JsonPropertyName("project_id")] - public int ProjectId; + [JsonPropertyName("source_project_id")] + public int SourceProjectId; - [JsonPropertyName("source_project_id")] - public int SourceProjectId; + [JsonPropertyName("target_project_id")] + public int TargetProjectId; - [JsonPropertyName("target_project_id")] - public int TargetProjectId; + [JsonPropertyName("work_in_progress")] + public bool? WorkInProgress; - [JsonPropertyName("work_in_progress")] - public bool? WorkInProgress; + [JsonPropertyName("milestone")] + public Milestone Milestone; - [JsonPropertyName("milestone")] - public Milestone Milestone; + [JsonPropertyName("labels")] + public string[] Labels; - [JsonPropertyName("labels")] - public string[] Labels; + [JsonPropertyName("merge_when_pipeline_succeeds")] + public bool MergeWhenPipelineSucceeds; - [JsonPropertyName("merge_when_pipeline_succeeds")] - public bool MergeWhenPipelineSucceeds; + [JsonPropertyName("merge_status")] + public string MergeStatus; - [JsonPropertyName("merge_status")] - public string MergeStatus; + [JsonPropertyName("sha")] + public string Sha; - [JsonPropertyName("sha")] - public string Sha; + [JsonPropertyName("merge_commit_sha")] + public string MergeCommitSha; - [JsonPropertyName("merge_commit_sha")] - public string MergeCommitSha; + [JsonPropertyName("squash_commit_sha")] + public string SquashCommitSha; - [JsonPropertyName("squash_commit_sha")] - public string SquashCommitSha; + [JsonPropertyName("diff_refs")] + public DiffRefs DiffRefs; - [JsonPropertyName("diff_refs")] - public DiffRefs DiffRefs; + [JsonPropertyName("should_remove_source_branch")] + public bool? ShouldRemoveSourceBranch; - [JsonPropertyName("should_remove_source_branch")] - public bool? ShouldRemoveSourceBranch; + [JsonPropertyName("force_remove_source_branch")] + public bool ForceRemoveSourceBranch; - [JsonPropertyName("force_remove_source_branch")] - public bool ForceRemoveSourceBranch; + [JsonPropertyName("squash")] + public bool Squash; - [JsonPropertyName("squash")] - public bool Squash; + [JsonPropertyName("changes_count")] + public string ChangesCount; - [JsonPropertyName("changes_count")] - public string ChangesCount; + [JsonPropertyName("web_url")] + public string WebUrl; - [JsonPropertyName("web_url")] - public string WebUrl; + [JsonPropertyName("merged_by")] + public User MergedBy; - [JsonPropertyName("merged_by")] - public User MergedBy; + [JsonPropertyName("merged_at")] + public DateTime? MergedAt; - [JsonPropertyName("merged_at")] - public DateTime? MergedAt; + [JsonPropertyName("closed_at")] + public DateTime? ClosedAt; - [JsonPropertyName("closed_at")] - public DateTime? ClosedAt; + [JsonPropertyName("closed_by")] + public User ClosedBy; - [JsonPropertyName("closed_by")] - public User ClosedBy; + [JsonPropertyName("assignees")] + public User[] Assignees; - [JsonPropertyName("assignees")] - public User[] Assignees; + [JsonPropertyName("reviewers")] + public User[] Reviewers; - [JsonPropertyName("reviewers")] - public User[] Reviewers; + [JsonPropertyName("allow_collaboration")] + public bool? AllowCollaboration; - [JsonPropertyName("allow_collaboration")] - public bool? AllowCollaboration; + [JsonPropertyName("head_pipeline")] + public Pipeline HeadPipeline; - [JsonPropertyName("head_pipeline")] - public Pipeline HeadPipeline; + [JsonPropertyName("rebase_in_progress")] + public bool RebaseInProgress; - [JsonPropertyName("rebase_in_progress")] - public bool RebaseInProgress; + [JsonPropertyName("diverged_commits_count")] + public int? DivergedCommitsCount { get; set; } - [JsonPropertyName("diverged_commits_count")] - public int? DivergedCommitsCount { get; set; } + [JsonPropertyName("has_conflicts")] + public bool HasConflicts { get; set; } - [JsonPropertyName("has_conflicts")] - public bool HasConflicts { get; set; } + [JsonPropertyName("blocking_discussions_resolved")] + public bool BlockingDiscussionsResolved { get; set; } - [JsonPropertyName("blocking_discussions_resolved")] - public bool BlockingDiscussionsResolved { get; set; } + [JsonPropertyName("user")] + public MergeRequestUserInfo User { get; set; } - [JsonPropertyName("user")] - public MergeRequestUserInfo User { get; set; } + [JsonPropertyName("detailed_merge_status")] + public DynamicEnum DetailedMergeStatus { get; set; } - public override string ToString() - { - return $"!{Id.ToStringInvariant()}: {Title}"; - } - } + public override string ToString() => $"!{Id.ToStringInvariant()}: {Title}"; } diff --git a/NGitLab/PublicAPI.Unshipped.txt b/NGitLab/PublicAPI.Unshipped.txt index 56e8ab64..b6740306 100644 --- a/NGitLab/PublicAPI.Unshipped.txt +++ b/NGitLab/PublicAPI.Unshipped.txt @@ -1365,6 +1365,20 @@ NGitLab.Models.DeploymentStatus.created = 0 -> NGitLab.Models.DeploymentStatus NGitLab.Models.DeploymentStatus.failed = 3 -> NGitLab.Models.DeploymentStatus NGitLab.Models.DeploymentStatus.running = 1 -> NGitLab.Models.DeploymentStatus NGitLab.Models.DeploymentStatus.success = 2 -> NGitLab.Models.DeploymentStatus +NGitLab.Models.DetailedMergeStatus +NGitLab.Models.DetailedMergeStatus.BlockedStatus = 0 -> NGitLab.Models.DetailedMergeStatus +NGitLab.Models.DetailedMergeStatus.BrokenStatus = 1 -> NGitLab.Models.DetailedMergeStatus +NGitLab.Models.DetailedMergeStatus.Checking = 2 -> NGitLab.Models.DetailedMergeStatus +NGitLab.Models.DetailedMergeStatus.CiMustPass = 4 -> NGitLab.Models.DetailedMergeStatus +NGitLab.Models.DetailedMergeStatus.CiStillRunning = 5 -> NGitLab.Models.DetailedMergeStatus +NGitLab.Models.DetailedMergeStatus.DiscussionsNotResolved = 6 -> NGitLab.Models.DetailedMergeStatus +NGitLab.Models.DetailedMergeStatus.DraftStatus = 7 -> NGitLab.Models.DetailedMergeStatus +NGitLab.Models.DetailedMergeStatus.ExternalStatusChecks = 8 -> NGitLab.Models.DetailedMergeStatus +NGitLab.Models.DetailedMergeStatus.Mergeable = 9 -> NGitLab.Models.DetailedMergeStatus +NGitLab.Models.DetailedMergeStatus.NotApproved = 10 -> NGitLab.Models.DetailedMergeStatus +NGitLab.Models.DetailedMergeStatus.NotOpen = 11 -> NGitLab.Models.DetailedMergeStatus +NGitLab.Models.DetailedMergeStatus.PoliciesDenied = 12 -> NGitLab.Models.DetailedMergeStatus +NGitLab.Models.DetailedMergeStatus.Unchecked = 3 -> NGitLab.Models.DetailedMergeStatus NGitLab.Models.Diff NGitLab.Models.Diff.AMode -> string NGitLab.Models.Diff.BMode -> string @@ -2049,6 +2063,8 @@ NGitLab.Models.MergeRequest.ClosedAt -> System.DateTime? NGitLab.Models.MergeRequest.ClosedBy -> NGitLab.Models.User NGitLab.Models.MergeRequest.CreatedAt -> System.DateTime NGitLab.Models.MergeRequest.Description -> string +NGitLab.Models.MergeRequest.DetailedMergeStatus.get -> NGitLab.DynamicEnum +NGitLab.Models.MergeRequest.DetailedMergeStatus.set -> void NGitLab.Models.MergeRequest.DiffRefs -> NGitLab.Models.DiffRefs NGitLab.Models.MergeRequest.DivergedCommitsCount.get -> int? NGitLab.Models.MergeRequest.DivergedCommitsCount.set -> void From f0503ab7af1723132d2d985f4df0f653c4395773 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jul 2023 09:20:49 -0400 Subject: [PATCH 24/57] Bump Meziantou.Analyzer from 2.0.63 to 2.0.66 (#489) Bumps [Meziantou.Analyzer](https://github.com/meziantou/Meziantou.Analyzer) from 2.0.63 to 2.0.66. - [Release notes](https://github.com/meziantou/Meziantou.Analyzer/releases) - [Commits](https://github.com/meziantou/Meziantou.Analyzer/compare/2.0.63...2.0.66) --- updated-dependencies: - dependency-name: Meziantou.Analyzer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 39cc8e79..8a9c09ee 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -42,7 +42,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 5d4b6da17015d0f589a80b913181b7fa5bc1bb03 Mon Sep 17 00:00:00 2001 From: Lior Banai <36262995+LiorBanai@users.noreply.github.com> Date: Tue, 25 Jul 2023 20:35:41 +0300 Subject: [PATCH 25/57] Add LinkedTo method to IIssueClient Interface (#488) --- .editorconfig | 29 +++++++++++++++++++++-------- NGitLab.Mock/Clients/IssueClient.cs | 10 ++++++++++ NGitLab.Tests/IssueTests.cs | 18 ++++++++++++++++++ NGitLab/IIssueClient.cs | 17 +++++++++++++++++ NGitLab/Impl/IssueClient.cs | 24 +++++++++++++++++++++++- NGitLab/PublicAPI.Unshipped.txt | 4 ++++ 6 files changed, 93 insertions(+), 9 deletions(-) diff --git a/.editorconfig b/.editorconfig index af10f470..dc8e66ff 100644 --- a/.editorconfig +++ b/.editorconfig @@ -73,10 +73,10 @@ csharp_style_var_for_built_in_types = true : suggestion csharp_style_var_when_type_is_apparent = true : warning # Expression-Bodied members -csharp_style_expression_bodied_accessors = true : suggestion -csharp_style_expression_bodied_indexers = true : suggestion -csharp_style_expression_bodied_operators = true : suggestion -csharp_style_expression_bodied_properties = true : suggestion +csharp_style_expression_bodied_accessors = true:suggestion +csharp_style_expression_bodied_indexers = true:suggestion +csharp_style_expression_bodied_operators = true:suggestion +csharp_style_expression_bodied_properties = true:suggestion # Explicitly disabled due to difference in coding style between source and tests #csharp_style_expression_bodied_constructors = true : warning #csharp_style_expression_bodied_methods = true : warning @@ -101,7 +101,7 @@ csharp_style_conditional_delegate_call = true : warning csharp_style_throw_expression = true : warning # Code block preferences -csharp_prefer_braces = when_multiline : suggestion +csharp_prefer_braces = when_multiline:suggestion ## Formatting conventions # Dotnet formatting settings: @@ -141,6 +141,16 @@ csharp_space_between_method_call_empty_parameter_list_parentheses = false # Wrapping options csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = false +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +dotnet_diagnostic.SA1507.severity = error ## Naming conventions [*.{cs,vb}] @@ -159,7 +169,7 @@ dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case # Constants are PascalCase dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants -dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style +dotnet_naming_rule.constants_should_be_pascal_case.style = non_private_static_field_style dotnet_naming_symbols.constants.applicable_kinds = field, local dotnet_naming_symbols.constants.required_modifiers = const @@ -198,7 +208,7 @@ dotnet_naming_style.camel_case_style.capitalization = camel_case # Local functions are PascalCase dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions -dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style +dotnet_naming_rule.local_functions_should_be_pascal_case.style = non_private_static_field_style dotnet_naming_symbols.local_functions.applicable_kinds = local_function dotnet_naming_style.local_function_style.capitalization = pascal_case @@ -216,7 +226,7 @@ dotnet_naming_symbols.type_parameter_symbol.applicable_accessibilities = * # By default, name items with PascalCase dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members -dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style +dotnet_naming_rule.members_should_be_pascal_case.style = non_private_static_field_style dotnet_naming_symbols.all_members.applicable_kinds = * @@ -373,3 +383,6 @@ dotnet_diagnostic.SA1636.severity = none # SA1649: File name should match first type name dotnet_diagnostic.SA1649.severity = none +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +end_of_line = crlf diff --git a/NGitLab.Mock/Clients/IssueClient.cs b/NGitLab.Mock/Clients/IssueClient.cs index d38712b2..bc1a342b 100644 --- a/NGitLab.Mock/Clients/IssueClient.cs +++ b/NGitLab.Mock/Clients/IssueClient.cs @@ -302,6 +302,16 @@ public Models.Issue Get(int projectId, int issueId) return GetById(issueId); } + public GitLabCollectionResponse LinkedToAsync(int projectId, int issueId) + { + throw new NotImplementedException(); + } + + public bool CreateLinkBetweenIssues(int sourceProjectId, int sourceIssueId, int targetProjectId, int targetIssueId) + { + throw new NotImplementedException(); + } + public Models.Issue GetById(int issueId) { using (Context.BeginOperationScope()) diff --git a/NGitLab.Tests/IssueTests.cs b/NGitLab.Tests/IssueTests.cs index b82e1113..cb975f6d 100644 --- a/NGitLab.Tests/IssueTests.cs +++ b/NGitLab.Tests/IssueTests.cs @@ -293,5 +293,23 @@ public async Task Test_get_new_and_updated_issue_with_duedate() Assert.AreEqual(updatedDueDate, updatedIssue.DueDate); } + + [Test] + [NGitLabRetry] + public async Task Test_get_linked_issue() + { + using var context = await GitLabTestContext.CreateAsync(); + var project = context.CreateProject(); + var issuesClient = context.Client.Issues; + + var issue1 = await issuesClient.CreateAsync(new IssueCreate { ProjectId = project.Id, Title = "title1" }); + var issue2 = await issuesClient.CreateAsync(new IssueCreate { ProjectId = project.Id, Title = "title2", Description = "related to #1" }); + var linked = issuesClient.CreateLinkBetweenIssues(project.Id, issue1.IssueId, project.Id, issue2.IssueId); + Assert.IsTrue(linked, "Expected true for create Link between issues"); + var issues = issuesClient.LinkedToAsync(project.Id, issue1.IssueId).ToList(); + + // for now, no API to link issues so not links exist but API should not throw + Assert.AreEqual(1, issues.Count, "Expected 1. Got {0}", issues.Count); + } } } diff --git a/NGitLab/IIssueClient.cs b/NGitLab/IIssueClient.cs index 12e23ce9..a5145f6b 100644 --- a/NGitLab/IIssueClient.cs +++ b/NGitLab/IIssueClient.cs @@ -134,6 +134,23 @@ public interface IIssueClient /// The list of MR that are related this issue. IEnumerable RelatedTo(int projectId, int issueIid); + /// + /// Get all Issues that are linked to a particular issue of particular project. + /// + /// The project id. + /// The id of the issue in the project's scope. + /// The list of Issues linked to this issue. + GitLabCollectionResponse LinkedToAsync(int projectId, int issueId); + + /// + /// Create links between Issues. + /// + /// The project id. + /// The id of the issue in the project's scope. + /// The target project id. + /// The target id of the issue to link to. + bool CreateLinkBetweenIssues(int sourceProjectId, int sourceIssueId, int targetProjectId, int targetIssueId); + GitLabCollectionResponse RelatedToAsync(int projectId, int issueIid); /// diff --git a/NGitLab/Impl/IssueClient.cs b/NGitLab/Impl/IssueClient.cs index 4a1bb103..7835988e 100644 --- a/NGitLab/Impl/IssueClient.cs +++ b/NGitLab/Impl/IssueClient.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Globalization; using System.Threading; using System.Threading.Tasks; @@ -10,6 +11,8 @@ public class IssueClient : IIssueClient { private const string IssuesUrl = "/issues"; private const string IssueByIdUrl = "/issues/{0}"; + private const string LinkedIssuesByIdUrl = "/projects/{0}/issues/{1}/links"; + private const string CreateLinkBetweenIssuesUrl = "/projects/{0}/issues/{1}/links?target_project_id={2}&target_issue_iid={3}"; private const string GroupIssuesUrl = "/groups/{0}/issues"; private const string ProjectIssuesUrl = "/projects/{0}/issues"; private const string SingleIssueUrl = "/projects/{0}/issues/{1}"; @@ -145,6 +148,25 @@ public IEnumerable RelatedTo(int projectId, int issueIid) return _api.Get().GetAll(string.Format(CultureInfo.InvariantCulture, RelatedToUrl, projectId, issueIid)); } + public GitLabCollectionResponse LinkedToAsync(int projectId, int issueId) + { + return _api.Get().GetAllAsync(string.Format(CultureInfo.InvariantCulture, LinkedIssuesByIdUrl, projectId, issueId)); + } + + public bool CreateLinkBetweenIssues(int sourceProjectId, int sourceIssueId, int targetProjectId, + int targetIssueId) + { + try + { + _api.Post().Execute(string.Format(CultureInfo.InvariantCulture, CreateLinkBetweenIssuesUrl, sourceProjectId, sourceIssueId, targetProjectId, targetIssueId)); + return true; + } + catch (Exception) + { + return false; + } + } + public GitLabCollectionResponse RelatedToAsync(int projectId, int issueIid) { return _api.Get().GetAllAsync(string.Format(CultureInfo.InvariantCulture, RelatedToUrl, projectId, issueIid)); diff --git a/NGitLab/PublicAPI.Unshipped.txt b/NGitLab/PublicAPI.Unshipped.txt index b6740306..82e0304d 100644 --- a/NGitLab/PublicAPI.Unshipped.txt +++ b/NGitLab/PublicAPI.Unshipped.txt @@ -249,6 +249,7 @@ NGitLab.IIssueClient.ClosedBy(int projectId, int issueIid) -> System.Collections NGitLab.IIssueClient.ClosedByAsync(int projectId, int issueIid) -> NGitLab.GitLabCollectionResponse NGitLab.IIssueClient.Create(NGitLab.Models.IssueCreate issueCreate) -> NGitLab.Models.Issue NGitLab.IIssueClient.CreateAsync(NGitLab.Models.IssueCreate issueCreate, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +NGitLab.IIssueClient.CreateLinkBetweenIssues(int sourceProjectId, int sourceIssueId, int targetProjectId, int targetIssueId) -> bool NGitLab.IIssueClient.Edit(NGitLab.Models.IssueEdit issueEdit) -> NGitLab.Models.Issue NGitLab.IIssueClient.EditAsync(NGitLab.Models.IssueEdit issueEdit, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task NGitLab.IIssueClient.ForGroupsAsync(int groupId) -> NGitLab.GitLabCollectionResponse @@ -263,6 +264,7 @@ NGitLab.IIssueClient.GetAsync(int projectId, NGitLab.Models.IssueQuery query) -> NGitLab.IIssueClient.GetAsync(NGitLab.Models.IssueQuery query) -> NGitLab.GitLabCollectionResponse NGitLab.IIssueClient.GetById(int issueId) -> NGitLab.Models.Issue NGitLab.IIssueClient.GetByIdAsync(int issueId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +NGitLab.IIssueClient.LinkedToAsync(int projectId, int issueId) -> NGitLab.GitLabCollectionResponse NGitLab.IIssueClient.Owned.get -> System.Collections.Generic.IEnumerable NGitLab.IIssueClient.RelatedTo(int projectId, int issueIid) -> System.Collections.Generic.IEnumerable NGitLab.IIssueClient.RelatedToAsync(int projectId, int issueIid) -> NGitLab.GitLabCollectionResponse @@ -498,6 +500,7 @@ NGitLab.Impl.IssueClient.ClosedBy(int projectId, int issueIid) -> System.Collect NGitLab.Impl.IssueClient.ClosedByAsync(int projectId, int issueIid) -> NGitLab.GitLabCollectionResponse NGitLab.Impl.IssueClient.Create(NGitLab.Models.IssueCreate issueCreate) -> NGitLab.Models.Issue NGitLab.Impl.IssueClient.CreateAsync(NGitLab.Models.IssueCreate issueCreate, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +NGitLab.Impl.IssueClient.CreateLinkBetweenIssues(int sourceProjectId, int sourceIssueId, int targetProjectId, int targetIssueId) -> bool NGitLab.Impl.IssueClient.Edit(NGitLab.Models.IssueEdit issueEdit) -> NGitLab.Models.Issue NGitLab.Impl.IssueClient.EditAsync(NGitLab.Models.IssueEdit issueEdit, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task NGitLab.Impl.IssueClient.ForGroupsAsync(int groupId) -> NGitLab.GitLabCollectionResponse @@ -513,6 +516,7 @@ NGitLab.Impl.IssueClient.GetAsync(NGitLab.Models.IssueQuery query) -> NGitLab.Gi NGitLab.Impl.IssueClient.GetById(int issueId) -> NGitLab.Models.Issue NGitLab.Impl.IssueClient.GetByIdAsync(int issueId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task NGitLab.Impl.IssueClient.IssueClient(NGitLab.Impl.API api) -> void +NGitLab.Impl.IssueClient.LinkedToAsync(int projectId, int issueId) -> NGitLab.GitLabCollectionResponse NGitLab.Impl.IssueClient.Owned.get -> System.Collections.Generic.IEnumerable NGitLab.Impl.IssueClient.RelatedTo(int projectId, int issueIid) -> System.Collections.Generic.IEnumerable NGitLab.Impl.IssueClient.RelatedToAsync(int projectId, int issueIid) -> NGitLab.GitLabCollectionResponse From 19c27e28c88a51f045e407a0b44724a2fa742700 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Jul 2023 07:35:43 -0400 Subject: [PATCH 26/57] Bump Meziantou.Analyzer from 2.0.66 to 2.0.78 (#494) Bumps [Meziantou.Analyzer](https://github.com/meziantou/Meziantou.Analyzer) from 2.0.66 to 2.0.78. - [Release notes](https://github.com/meziantou/Meziantou.Analyzer/releases) - [Commits](https://github.com/meziantou/Meziantou.Analyzer/compare/2.0.66...2.0.78) --- updated-dependencies: - dependency-name: Meziantou.Analyzer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 8a9c09ee..0e7ab823 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -42,7 +42,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 07341faf117795fa33315f75eabd2910e35ff184 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Jul 2023 08:25:34 -0400 Subject: [PATCH 27/57] Bump Microsoft.Playwright from 1.35.0 to 1.36.0 (#490) Bumps [Microsoft.Playwright](https://github.com/microsoft/playwright-dotnet) from 1.35.0 to 1.36.0. - [Release notes](https://github.com/microsoft/playwright-dotnet/releases) - [Commits](https://github.com/microsoft/playwright-dotnet/compare/v1.35.0...v1.36.0) --- updated-dependencies: - dependency-name: Microsoft.Playwright dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Tests/NGitLab.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab.Tests/NGitLab.Tests.csproj index 29a1a1e8..fcf81ae3 100644 --- a/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab.Tests/NGitLab.Tests.csproj @@ -15,7 +15,7 @@ - + From 65ff9745626731307fed3f3b78c4ba0fce7b8701 Mon Sep 17 00:00:00 2001 From: PM Extra Date: Mon, 31 Jul 2023 22:05:03 +0800 Subject: [PATCH 28/57] Add project_id and group_id properties for the Milestone model (#465) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add projectId and groupId properties for the Milestone model Co-authored-by: Gérald Barré --- .../Milestone/MilestoneClientTests.cs | 24 +++++++++++++------ NGitLab/Models/Milestone.cs | 6 +++++ NGitLab/PublicAPI.Unshipped.txt | 2 ++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/NGitLab.Tests/Milestone/MilestoneClientTests.cs b/NGitLab.Tests/Milestone/MilestoneClientTests.cs index 24ad7444..a1a1412b 100644 --- a/NGitLab.Tests/Milestone/MilestoneClientTests.cs +++ b/NGitLab.Tests/Milestone/MilestoneClientTests.cs @@ -79,6 +79,8 @@ private static Models.Milestone CreateMilestone(GitLabTestContext context, Miles Assert.That(milestone.Description, Is.EqualTo($"{title} description")); Assert.That(milestone.StartDate, Is.EqualTo("2017-08-20")); Assert.That(milestone.DueDate, Is.EqualTo("2017-09-20")); + Assert.That(milestone.ProjectId, scope == MilestoneScope.Projects ? Is.EqualTo(id) : Is.Null); + Assert.That(milestone.GroupId, scope == MilestoneScope.Groups ? Is.EqualTo(id) : Is.Null); return milestone; } @@ -100,6 +102,8 @@ private static Models.Milestone UpdateMilestone(GitLabTestContext context, Miles Assert.That(updatedMilestone.StartDate, Is.EqualTo("2018-08-20")); Assert.That(updatedMilestone.DueDate, Is.EqualTo("2018-09-20")); Assert.That(updatedMilestone.State, Is.EqualTo(milestone.State)); + Assert.That(milestone.ProjectId, scope == MilestoneScope.Projects ? Is.EqualTo(id) : Is.Null); + Assert.That(milestone.GroupId, scope == MilestoneScope.Groups ? Is.EqualTo(id) : Is.Null); return updatedMilestone; } @@ -118,6 +122,8 @@ private static Models.Milestone UpdatePartialMilestone(GitLabTestContext context Assert.That(updatedMilestone.StartDate, Is.EqualTo(milestone.StartDate)); Assert.That(updatedMilestone.DueDate, Is.EqualTo(milestone.DueDate)); Assert.That(updatedMilestone.State, Is.EqualTo(milestone.State)); + Assert.That(updatedMilestone.ProjectId, scope == MilestoneScope.Projects ? Is.EqualTo(id) : Is.Null); + Assert.That(updatedMilestone.GroupId, scope == MilestoneScope.Groups ? Is.EqualTo(id) : Is.Null); return updatedMilestone; } @@ -125,15 +131,17 @@ private static Models.Milestone UpdatePartialMilestone(GitLabTestContext context private static Models.Milestone ActivateMilestone(GitLabTestContext context, MilestoneScope scope, int id, Models.Milestone milestone) { var milestoneClient = scope == MilestoneScope.Projects ? context.Client.GetMilestone(id) : context.Client.GetGroupMilestone(id); - var closedMilestone = milestoneClient.Activate(milestone.Id); + var activeMilestone = milestoneClient.Activate(milestone.Id); - Assert.That(closedMilestone.State, Is.EqualTo(nameof(MilestoneState.active))); - Assert.That(closedMilestone.Title, Is.EqualTo(milestone.Title)); - Assert.That(closedMilestone.Description, Is.EqualTo(milestone.Description)); - Assert.That(closedMilestone.StartDate, Is.EqualTo(milestone.StartDate)); - Assert.That(closedMilestone.DueDate, Is.EqualTo(milestone.DueDate)); + Assert.That(activeMilestone.State, Is.EqualTo(nameof(MilestoneState.active))); + Assert.That(activeMilestone.Title, Is.EqualTo(milestone.Title)); + Assert.That(activeMilestone.Description, Is.EqualTo(milestone.Description)); + Assert.That(activeMilestone.StartDate, Is.EqualTo(milestone.StartDate)); + Assert.That(activeMilestone.DueDate, Is.EqualTo(milestone.DueDate)); + Assert.That(activeMilestone.ProjectId, scope == MilestoneScope.Projects ? Is.EqualTo(id) : Is.Null); + Assert.That(activeMilestone.GroupId, scope == MilestoneScope.Groups ? Is.EqualTo(id) : Is.Null); - return closedMilestone; + return activeMilestone; } private static Models.Milestone CloseMilestone(GitLabTestContext context, MilestoneScope scope, int id, Models.Milestone milestone) @@ -146,6 +154,8 @@ private static Models.Milestone CloseMilestone(GitLabTestContext context, Milest Assert.That(closedMilestone.Description, Is.EqualTo(milestone.Description)); Assert.That(closedMilestone.StartDate, Is.EqualTo(milestone.StartDate)); Assert.That(closedMilestone.DueDate, Is.EqualTo(milestone.DueDate)); + Assert.That(closedMilestone.ProjectId, scope == MilestoneScope.Projects ? Is.EqualTo(id) : Is.Null); + Assert.That(closedMilestone.GroupId, scope == MilestoneScope.Groups ? Is.EqualTo(id) : Is.Null); return closedMilestone; } diff --git a/NGitLab/Models/Milestone.cs b/NGitLab/Models/Milestone.cs index 6d9b8e87..2f7ed799 100644 --- a/NGitLab/Models/Milestone.cs +++ b/NGitLab/Models/Milestone.cs @@ -20,6 +20,12 @@ public class Milestone [JsonPropertyName("due_date")] public string DueDate; + [JsonPropertyName("group_id")] + public int? GroupId; + + [JsonPropertyName("project_id")] + public int? ProjectId; + [JsonPropertyName("start_date")] public string StartDate; diff --git a/NGitLab/PublicAPI.Unshipped.txt b/NGitLab/PublicAPI.Unshipped.txt index 82e0304d..2892ff19 100644 --- a/NGitLab/PublicAPI.Unshipped.txt +++ b/NGitLab/PublicAPI.Unshipped.txt @@ -2325,9 +2325,11 @@ NGitLab.Models.Milestone NGitLab.Models.Milestone.CreatedAt -> System.DateTime NGitLab.Models.Milestone.Description -> string NGitLab.Models.Milestone.DueDate -> string +NGitLab.Models.Milestone.GroupId -> int? NGitLab.Models.Milestone.Id -> int NGitLab.Models.Milestone.Iid -> int NGitLab.Models.Milestone.Milestone() -> void +NGitLab.Models.Milestone.ProjectId -> int? NGitLab.Models.Milestone.StartDate -> string NGitLab.Models.Milestone.State -> string NGitLab.Models.Milestone.Title -> string From 97ba85b905aea3fc67ead75c60d1ac99588c8ac6 Mon Sep 17 00:00:00 2001 From: Louis Zanella Date: Mon, 31 Jul 2023 13:46:55 -0400 Subject: [PATCH 29/57] Minor improvements to README (#495) --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 561643b1..608a6ea3 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ ## What is NGitLab? -*NGitLab* is a .NET REST client implementation of GitLab API with no external dependencies. +`NGitLab` is a .NET REST client implementation for the GitLab API. ## Usage -It's a wrapper of REST api. Read the [GitLab docs](https://github.com/gitlabhq/gitlabhq/tree/master/doc/api) and start using by creating a GitLabClient instance: +Start by creating a `GitLabClient` instance: ```csharp var client = new GitLabClient("https://mygitlab.example.com", "your_private_token"); @@ -14,13 +14,17 @@ var client = new GitLabClient("https://mygitlab.example.com", "your_private_toke Then use its properties. You can obtain the private token in your account page. You may want to create a custom user for the API usage. +For further info about the GitLab API, refer to [the official documentation](https://docs.gitlab.com/ee/api/rest/) + ## Where can I get it? -Get it from [NuGet](https://www.nuget.org/packages/NGitLab). You can simply install it with the Package Manager console: +Get it from [nuget.org](https://www.nuget.org/packages/NGitLab). You can simply install it using the `dotnet` CLI: - PM> Install-Package NGitLab +```PowerShell +dotnet add package NGitLab +``` -## Unit-Test +## Running Unit Tests locally - Install Docker on your machine - It's recommended to use WSL version 2: https://docs.microsoft.com/en-us/windows/wsl/install-win10 From 61b8823c08ecbf991ac3659a297b529d0074ee5c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 07:23:10 -0400 Subject: [PATCH 30/57] Bump Microsoft.NET.Test.Sdk from 17.6.3 to 17.7.0 (#497) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.6.3 to 17.7.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.6.3...v17.7.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj | 2 +- NGitLab.Tests/NGitLab.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj b/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj index cafc9abd..7abea563 100644 --- a/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj +++ b/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj @@ -7,7 +7,7 @@ - + diff --git a/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab.Tests/NGitLab.Tests.csproj index fcf81ae3..d4912a71 100644 --- a/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab.Tests/NGitLab.Tests.csproj @@ -14,7 +14,7 @@ - + From 924e3c89cc43288b56a297c11e62ff380f39e6e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 11:41:42 +0000 Subject: [PATCH 31/57] Bump Meziantou.Analyzer from 2.0.78 to 2.0.80 (#496) Bumps [Meziantou.Analyzer](https://github.com/meziantou/Meziantou.Analyzer) from 2.0.78 to 2.0.80. - [Release notes](https://github.com/meziantou/Meziantou.Analyzer/releases) - [Commits](https://github.com/meziantou/Meziantou.Analyzer/compare/2.0.78...2.0.80) --- updated-dependencies: - dependency-name: Meziantou.Analyzer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 0e7ab823..f8f60741 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -42,7 +42,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 59834f1f85ce152ed042145db6f6a14a033a671a Mon Sep 17 00:00:00 2001 From: Steve Ryan Date: Sat, 12 Aug 2023 00:02:32 +1000 Subject: [PATCH 32/57] Implements support for OAuth tokens (#498) --- NGitLab.Tests/HttpRequestorTests.cs | 16 ++++++++++++++++ NGitLab/Impl/HttpRequestor.GitLabRequest.cs | 6 +++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/NGitLab.Tests/HttpRequestorTests.cs b/NGitLab.Tests/HttpRequestorTests.cs index 85288e50..83b9a515 100644 --- a/NGitLab.Tests/HttpRequestorTests.cs +++ b/NGitLab.Tests/HttpRequestorTests.cs @@ -125,6 +125,22 @@ public async Task Test_impersonation_via_sudo_and_user_id() Assert.AreEqual(commonUserSession.Id, issue.Author.Id); } + [Test] + public async Task Test_authorization_header_uses_bearer() + { + // Arrange + using var context = await GitLabTestContext.CreateAsync(); + var commonUserClient = context.Client; + string expectedHeaderValue = string.Concat("Bearer ", context.DockerContainer.Credentials.UserToken); + + // Act + var project = commonUserClient.Projects.Accessible.First(); + + // Assert + var actualHeaderValue = context.LastRequest.Headers[HttpRequestHeader.Authorization]; + Assert.AreEqual(expectedHeaderValue, actualHeaderValue); + } + private sealed class MockRequestOptions : RequestOptions { public string HttpRequestSudoHeader { get; set; } diff --git a/NGitLab/Impl/HttpRequestor.GitLabRequest.cs b/NGitLab/Impl/HttpRequestor.GitLabRequest.cs index 850980f3..54972426 100644 --- a/NGitLab/Impl/HttpRequestor.GitLabRequest.cs +++ b/NGitLab/Impl/HttpRequestor.GitLabRequest.cs @@ -48,7 +48,11 @@ public GitLabRequest(Uri url, MethodType method, object data, string apiToken, R if (apiToken != null) { - Headers.Add("Private-Token", apiToken); + // Use the 'Authorization: Bearer token' header as this provides flexibility to use + // personal, project, group and OAuth tokens. The 'PRIVATE-TOKEN' header does not + // provide OAuth token support. + // Reference: https://docs.gitlab.com/ee/api/rest/#personalprojectgroup-access-tokens + Headers.Add(HttpRequestHeader.Authorization, string.Concat("Bearer ", apiToken)); } if (!string.IsNullOrEmpty(options?.UserAgent)) From caee72bf2c755d5f3d347c9e629d464cc4925728 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 07:43:29 -0400 Subject: [PATCH 33/57] Bump YamlDotNet from 13.1.1 to 13.2.0 (#499) Bumps [YamlDotNet](https://github.com/aaubry/YamlDotNet) from 13.1.1 to 13.2.0. - [Release notes](https://github.com/aaubry/YamlDotNet/releases) - [Commits](https://github.com/aaubry/YamlDotNet/compare/v13.1.1...v13.2.0) --- updated-dependencies: - dependency-name: YamlDotNet dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Mock/NGitLab.Mock.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab.Mock/NGitLab.Mock.csproj b/NGitLab.Mock/NGitLab.Mock.csproj index c182495d..1e2f8222 100644 --- a/NGitLab.Mock/NGitLab.Mock.csproj +++ b/NGitLab.Mock/NGitLab.Mock.csproj @@ -13,7 +13,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From a4a6e5f76214318b0575ea32089db823073bf3e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 10:59:30 -0400 Subject: [PATCH 34/57] Bump NuGet.Versioning from 6.6.1 to 6.7.0 (#500) Bumps [NuGet.Versioning](https://github.com/NuGet/NuGet.Client) from 6.6.1 to 6.7.0. - [Release notes](https://github.com/NuGet/NuGet.Client/releases) - [Commits](https://github.com/NuGet/NuGet.Client/commits) --- updated-dependencies: - dependency-name: NuGet.Versioning dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Tests/NGitLab.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab.Tests/NGitLab.Tests.csproj index d4912a71..67abf2da 100644 --- a/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab.Tests/NGitLab.Tests.csproj @@ -16,7 +16,7 @@ - + From 5737c23ab34bafafbb384741a7f3677ec129bf9c Mon Sep 17 00:00:00 2001 From: Louis Zanella Date: Mon, 14 Aug 2023 14:39:13 -0400 Subject: [PATCH 35/57] Allow devs using Rancher Desktop to launch tests from their PCs (#502) This is to ignore https://github.com/rancher-sandbox/rancher-desktop/issues/5145 and carry on. --- NGitLab.Tests/Docker/GitLabDockerContainer.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NGitLab.Tests/Docker/GitLabDockerContainer.cs b/NGitLab.Tests/Docker/GitLabDockerContainer.cs index d91bf2e0..faa595f7 100644 --- a/NGitLab.Tests/Docker/GitLabDockerContainer.cs +++ b/NGitLab.Tests/Docker/GitLabDockerContainer.cs @@ -107,6 +107,10 @@ private static async Task ValidateDockerIsEnabled(DockerClient client) { await client.Images.ListImagesAsync(new ImagesListParameters()).ConfigureAwait(false); } + catch (ArgumentOutOfRangeException ex) when (ex.Message.StartsWith("The added or subtracted value results in an un-representable DateTime.", StringComparison.Ordinal)) + { + // Ignore https://github.com/rancher-sandbox/rancher-desktop/issues/5145 + } catch (Exception ex) { s_creationErrorMessage = "Cannot connect to Docker service. Make sure it's running on your machine before launching any tests.\nDetails: " + ex; From 9628a61768cf69addadb16ec96ea3107a954390e Mon Sep 17 00:00:00 2001 From: PM Extra Date: Fri, 18 Aug 2023 22:14:56 +0800 Subject: [PATCH 36/57] fix: NamespaceId is not required when create project (#503) --- NGitLab/Models/ProjectCreate.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/NGitLab/Models/ProjectCreate.cs b/NGitLab/Models/ProjectCreate.cs index 965968f6..71dd1963 100644 --- a/NGitLab/Models/ProjectCreate.cs +++ b/NGitLab/Models/ProjectCreate.cs @@ -11,7 +11,6 @@ public class ProjectCreate [JsonPropertyName("name")] public string Name; - [Required] [JsonPropertyName("namespace_id")] public string NamespaceId; From 208e565f86baaf599cf4a42e5238c4bd1858294c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 07:15:59 -0400 Subject: [PATCH 37/57] Bump Microsoft.NET.Test.Sdk from 17.7.0 to 17.7.1 (#507) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.7.0 to 17.7.1. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.7.0...v17.7.1) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj | 2 +- NGitLab.Tests/NGitLab.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj b/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj index 7abea563..ee727082 100644 --- a/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj +++ b/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj @@ -7,7 +7,7 @@ - + diff --git a/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab.Tests/NGitLab.Tests.csproj index 67abf2da..d858a897 100644 --- a/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab.Tests/NGitLab.Tests.csproj @@ -14,7 +14,7 @@ - + From 8902264b3465c80c9b5d402b5c1b5dd10e04cd0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 07:51:21 -0400 Subject: [PATCH 38/57] Bump Microsoft.Playwright from 1.36.0 to 1.37.1 (#508) Bumps [Microsoft.Playwright](https://github.com/microsoft/playwright-dotnet) from 1.36.0 to 1.37.1. - [Release notes](https://github.com/microsoft/playwright-dotnet/releases) - [Commits](https://github.com/microsoft/playwright-dotnet/compare/v1.36.0...v1.37.1) --- updated-dependencies: - dependency-name: Microsoft.Playwright dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Tests/NGitLab.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab.Tests/NGitLab.Tests.csproj index d858a897..d0cdbcbc 100644 --- a/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab.Tests/NGitLab.Tests.csproj @@ -15,7 +15,7 @@ - + From 9e6fcbb2a8131ef8425d5078f5c27eff3f75ab59 Mon Sep 17 00:00:00 2001 From: PM Extra Date: Mon, 21 Aug 2023 23:00:48 +0800 Subject: [PATCH 39/57] Support add_labels and remove_labels when update a merge request (#505) Support AddLabels and RemoveLabels when update a merge request Co-authored-by: Thomas Cortes <78750681+Toa741@users.noreply.github.com> --- .../MergeRequest/MergeRequestClientTests.cs | 15 +++++++++++++++ NGitLab/Models/MergeRequestUpdate.cs | 6 ++++++ NGitLab/PublicAPI.Unshipped.txt | 2 ++ 3 files changed, 23 insertions(+) diff --git a/NGitLab.Tests/MergeRequest/MergeRequestClientTests.cs b/NGitLab.Tests/MergeRequest/MergeRequestClientTests.cs index 0586acf0..859fb59b 100644 --- a/NGitLab.Tests/MergeRequest/MergeRequestClientTests.cs +++ b/NGitLab.Tests/MergeRequest/MergeRequestClientTests.cs @@ -25,6 +25,7 @@ public async Task Test_merge_request_api() ListMergeRequest(mergeRequestClient, mergeRequest); mergeRequest = UpdateMergeRequest(mergeRequestClient, mergeRequest); + Test_can_update_labels_with_delta(mergeRequestClient, mergeRequest); Test_can_update_a_subset_of_merge_request_fields(mergeRequestClient, mergeRequest); await GitLabTestContext.RetryUntilAsync( @@ -352,6 +353,20 @@ private static void Test_can_update_a_subset_of_merge_request_fields(IMergeReque Assert.AreEqual(mergeRequest.Description, updated.Description); } + private static void Test_can_update_labels_with_delta(IMergeRequestClient mergeRequestClient, MergeRequest mergeRequest) + { + // Ensure original labels are "a,b" + CollectionAssert.AreEqual(new[] { "a", "b" }, mergeRequest.Labels); + + var updated = mergeRequestClient.Update(mergeRequest.Iid, new MergeRequestUpdate + { + RemoveLabels = "b", + AddLabels = "c,d", + }); + + CollectionAssert.AreEqual(new[] { "a", "c", "d" }, updated.Labels); + } + public static void AcceptMergeRequest(IMergeRequestClient mergeRequestClient, MergeRequest request) { Policy diff --git a/NGitLab/Models/MergeRequestUpdate.cs b/NGitLab/Models/MergeRequestUpdate.cs index 7d048d7e..6a71ce36 100644 --- a/NGitLab/Models/MergeRequestUpdate.cs +++ b/NGitLab/Models/MergeRequestUpdate.cs @@ -31,6 +31,12 @@ public class MergeRequestUpdate [JsonPropertyName("labels")] public string Labels; + [JsonPropertyName("add_labels")] + public string AddLabels; + + [JsonPropertyName("remove_labels")] + public string RemoveLabels; + [JsonPropertyName("milestone_id")] public int? MilestoneId; diff --git a/NGitLab/PublicAPI.Unshipped.txt b/NGitLab/PublicAPI.Unshipped.txt index 2892ff19..0671f69d 100644 --- a/NGitLab/PublicAPI.Unshipped.txt +++ b/NGitLab/PublicAPI.Unshipped.txt @@ -2281,6 +2281,7 @@ NGitLab.Models.MergeRequestStateEvent.close = 0 -> NGitLab.Models.MergeRequestSt NGitLab.Models.MergeRequestStateEvent.merge = 2 -> NGitLab.Models.MergeRequestStateEvent NGitLab.Models.MergeRequestStateEvent.reopen = 1 -> NGitLab.Models.MergeRequestStateEvent NGitLab.Models.MergeRequestUpdate +NGitLab.Models.MergeRequestUpdate.AddLabels -> string NGitLab.Models.MergeRequestUpdate.AllowCollaboration -> bool? NGitLab.Models.MergeRequestUpdate.AssigneeId -> int? NGitLab.Models.MergeRequestUpdate.AssigneeIds -> int[] @@ -2289,6 +2290,7 @@ NGitLab.Models.MergeRequestUpdate.Labels -> string NGitLab.Models.MergeRequestUpdate.MergeRequestUpdate() -> void NGitLab.Models.MergeRequestUpdate.MilestoneId -> int? NGitLab.Models.MergeRequestUpdate.NewState -> string +NGitLab.Models.MergeRequestUpdate.RemoveLabels -> string NGitLab.Models.MergeRequestUpdate.RemoveSourceBranch -> bool? NGitLab.Models.MergeRequestUpdate.ReviewerIds -> int[] NGitLab.Models.MergeRequestUpdate.SourceBranch -> string From 5f7cea351b2af43b45caa681bb30d28336ed91e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 20:38:56 +0000 Subject: [PATCH 40/57] Bump Meziantou.Analyzer from 2.0.80 to 2.0.82 (#501) Bumps [Meziantou.Analyzer](https://github.com/meziantou/Meziantou.Analyzer) from 2.0.80 to 2.0.82. - [Release notes](https://github.com/meziantou/Meziantou.Analyzer/releases) - [Commits](https://github.com/meziantou/Meziantou.Analyzer/compare/2.0.80...2.0.82) --- updated-dependencies: - dependency-name: Meziantou.Analyzer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index f8f60741..6ddd2e36 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -42,7 +42,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 6545bf85df23d0f07cf497076f1ec9812dc6422a Mon Sep 17 00:00:00 2001 From: PM Extra Date: Wed, 23 Aug 2023 22:55:39 +0800 Subject: [PATCH 41/57] Support for getting merge requests that assigned to specified milestone (#504) * Support for getting merge requests that assigned to specified milestone * Tests for getting merge requests that assigned to specified milestone * Update NGitLab.Tests/Milestone/MilestoneClientTests.cs Co-authored-by: Thomas Cortes <78750681+Toa741@users.noreply.github.com> * Update NGitLab.Tests/Milestone/MilestoneClientTests.cs Co-authored-by: Thomas Cortes <78750681+Toa741@users.noreply.github.com> * Improve exception for unexpected milestone scope * Fix missing configure method * Improve tests * Fix missing return statement for CreateMilestone * Fix missing null check for WithMilestone * Fix build * Improve tests --------- Co-authored-by: Thomas Cortes <78750681+Toa741@users.noreply.github.com> --- NGitLab.Mock.Tests/MilestonesMockTests.cs | 42 +++++ NGitLab.Mock/Clients/MilestoneClient.cs | 154 +++++++++--------- NGitLab.Mock/Config/GitLabGroup.cs | 3 + NGitLab.Mock/Config/GitLabHelpers.cs | 89 +++++++++- NGitLab.Mock/Config/GitLabMergeRequest.cs | 2 + NGitLab.Mock/Config/GitLabMilestone.cs | 5 + NGitLab.Mock/Group.cs | 2 + NGitLab.Mock/MergeRequest.cs | 2 + NGitLab.Mock/Milestone.cs | 2 + NGitLab.Mock/PublicAPI.Unshipped.txt | 13 +- NGitLab.Tests/Docker/GitLabTestContext.cs | 11 +- .../Milestone/MilestoneClientTests.cs | 35 ++++ NGitLab/IMilestoneClient.cs | 2 + NGitLab/Impl/MilestoneClient.cs | 3 + NGitLab/PublicAPI.Unshipped.txt | 2 + 15 files changed, 284 insertions(+), 83 deletions(-) diff --git a/NGitLab.Mock.Tests/MilestonesMockTests.cs b/NGitLab.Mock.Tests/MilestonesMockTests.cs index 4339d90f..074dd2a7 100644 --- a/NGitLab.Mock.Tests/MilestonesMockTests.cs +++ b/NGitLab.Mock.Tests/MilestonesMockTests.cs @@ -99,5 +99,47 @@ public void Test_milestones_can_be_closed_and_activated_from_project() Assert.AreEqual(1, activeMilestones.Length, "Active milestones count is invalid"); Assert.AreEqual(0, closedMilestones.Length, "Closed milestones count is invalid"); } + + [Test] + public void Test_projects_merge_request_can_be_found_from_milestone() + { + const int ProjectId = 1; + const int MilestoneId = 1; + using var server = new GitLabConfig() + .WithUser("user1", isDefault: true) + .WithProject("Test", id: ProjectId, addDefaultUserAsMaintainer: true, configure: project => project + .WithMilestone("Milestone 1", id: MilestoneId) + .WithMergeRequest("branch-01", title: "Merge request 1", milestone: "Milestone 1") + .WithMergeRequest("branch-02", title: "Merge request 2", milestone: "Milestone 1") + .WithMergeRequest("branch-03", title: "Merge request 3", milestone: "Milestone 2")) + .BuildServer(); + + var client = server.CreateClient(); + var mergeRequests = client.GetMilestone(ProjectId).GetMergeRequests(MilestoneId).ToArray(); + Assert.AreEqual(2, mergeRequests.Length, "Merge requests count is invalid"); + } + + [Test] + public void Test_groups_merge_request_can_be_found_from_milestone() + { + const int projectId = 1; + const int milestoneId = 1; + using var server = new GitLabConfig() + .WithUser("user1", isDefault: true) + .WithGroup("parentGroup", id: projectId, configure: group => group + .WithMilestone("Milestone 1", id: milestoneId)) + .WithGroup("subGroup1", 2, @namespace: "parentGroup") + .WithGroup("subGroup2", 3, @namespace: "parentGroup") + .WithProject("project1", @namespace: "parentGroup/subGroup1", addDefaultUserAsMaintainer: true, configure: project => project + .WithMergeRequest("branch-01", title: "Merge request 1", milestone: "Milestone 1") + .WithMergeRequest("branch-02", title: "Merge request 2", milestone: "Milestone 2")) + .WithProject("project2", @namespace: "parentGroup/subGroup2", addDefaultUserAsMaintainer: true, configure: project => project + .WithMergeRequest("branch-03", title: "Merge request 3", milestone: "Milestone 1")) + .BuildServer(); + + var client = server.CreateClient(); + var mergeRequests = client.GetGroupMilestone(projectId).GetMergeRequests(milestoneId).ToArray(); + Assert.AreEqual(2, mergeRequests.Length, "Merge requests count is invalid"); + } } } diff --git a/NGitLab.Mock/Clients/MilestoneClient.cs b/NGitLab.Mock/Clients/MilestoneClient.cs index 5a265a20..0e688f6d 100644 --- a/NGitLab.Mock/Clients/MilestoneClient.cs +++ b/NGitLab.Mock/Clients/MilestoneClient.cs @@ -11,13 +11,12 @@ namespace NGitLab.Mock internal sealed class MilestoneClient : ClientBase, IMilestoneClient { private readonly int _resourceId; - private readonly MilestoneScope _scope; public MilestoneClient(ClientContext context, int id, MilestoneScope scope) : base(context) { _resourceId = id; - _scope = scope; + Scope = scope; } public Models.Milestone this[int id] @@ -26,35 +25,46 @@ public Models.Milestone this[int id] { using (Context.BeginOperationScope()) { - var project = GetProject(_resourceId, ProjectPermission.View); - return FindMilestone(id, project)?.ToClientMilestone(); + return GetMilestone(id, false).ToClientMilestone(); } } } public IEnumerable All => Get(new MilestoneQuery()); - public MilestoneScope Scope => throw new NotImplementedException(); + public MilestoneScope Scope { get; } public Models.Milestone Activate(int milestoneId) { using (Context.BeginOperationScope()) { - var milestone = new Milestone(); + var milestone = GetMilestone(milestoneId, true); + milestone.State = MilestoneState.active; + return milestone.ToClientMilestone(); + } + } - if (_scope == MilestoneScope.Groups) - { - var group = GetGroup(_resourceId, GroupPermission.Edit); - milestone = FindMilestone(milestoneId, group) ?? throw new GitLabNotFoundException($"Cannot find milestone with ID {milestoneId}"); - } - else if (_scope == MilestoneScope.Projects) + public IEnumerable GetMergeRequests(int milestoneId) + { + using (Context.BeginOperationScope()) + { + var milestone = GetMilestone(milestoneId, false); + IEnumerable mergeRequests; + + switch (Scope) { - var project = GetProject(_resourceId, ProjectPermission.Edit); - milestone = FindMilestone(milestoneId, project) ?? throw new GitLabNotFoundException($"Cannot find milestone with ID {milestoneId}"); + case MilestoneScope.Groups: + mergeRequests = milestone.Group.MergeRequests; + break; + case MilestoneScope.Projects: + mergeRequests = milestone.Project.MergeRequests; + break; + default: + throw new NotSupportedException($"{Scope} milestone is not supported yet."); } - milestone.State = MilestoneState.active; - return milestone.ToClientMilestone(); + mergeRequests = mergeRequests.Where(mr => mr.Milestone == milestone); + return mergeRequests.Select(mr => mr.ToMergeRequestClient()); } } @@ -67,19 +77,7 @@ public Models.Milestone Close(int milestoneId) { using (Context.BeginOperationScope()) { - var milestone = new Milestone(); - - if (_scope == MilestoneScope.Groups) - { - var group = GetGroup(_resourceId, GroupPermission.Edit); - milestone = FindMilestone(milestoneId, group) ?? throw new GitLabNotFoundException($"Cannot find milestone with ID {milestoneId}"); - } - else if (_scope == MilestoneScope.Projects) - { - var project = GetProject(_resourceId, ProjectPermission.Edit); - milestone = FindMilestone(milestoneId, project) ?? throw new GitLabNotFoundException($"Cannot find milestone with ID {milestoneId}"); - } - + var milestone = GetMilestone(milestoneId, true); milestone.State = MilestoneState.closed; return milestone.ToClientMilestone(); } @@ -97,15 +95,18 @@ public Models.Milestone Create(MilestoneCreate milestone) StartDate = string.IsNullOrEmpty(milestone.StartDate) ? DateTimeOffset.UtcNow : DateTimeOffset.Parse(milestone.StartDate), }; - if (_scope == MilestoneScope.Groups) + switch (Scope) { - var group = GetGroup(_resourceId, GroupPermission.Edit); - group.Milestones.Add(ms); - } - else if (_scope == MilestoneScope.Projects) - { - var project = GetProject(_resourceId, ProjectPermission.Edit); - project.Milestones.Add(ms); + case MilestoneScope.Groups: + var group = GetGroup(_resourceId, GroupPermission.Edit); + group.Milestones.Add(ms); + break; + case MilestoneScope.Projects: + var project = GetProject(_resourceId, ProjectPermission.Edit); + project.Milestones.Add(ms); + break; + default: + throw new NotSupportedException($"{Scope} milestone is not supported yet."); } return ms.ToClientMilestone(); @@ -116,17 +117,17 @@ public void Delete(int milestoneId) { using (Context.BeginOperationScope()) { - if (_scope == MilestoneScope.Groups) - { - var group = GetGroup(_resourceId, GroupPermission.Edit); - var milestone = FindMilestone(milestoneId, group) ?? throw new GitLabNotFoundException($"Cannot find milestone with ID {milestoneId}"); - group.Milestones.Remove(milestone); - } - else if (_scope == MilestoneScope.Projects) - { - var project = GetProject(_resourceId, ProjectPermission.Edit); - var milestone = FindMilestone(milestoneId, project) ?? throw new GitLabNotFoundException($"Cannot find milestone with ID {milestoneId}"); - project.Milestones.Remove(milestone); + var milestone = GetMilestone(milestoneId, true); + switch (Scope) + { + case MilestoneScope.Groups: + milestone.Group.Milestones.Remove(milestone); + break; + case MilestoneScope.Projects: + milestone.Project.Milestones.Remove(milestone); + break; + default: + throw new NotSupportedException($"{Scope} milestone is not supported yet."); } } } @@ -135,17 +136,20 @@ public void Delete(int milestoneId) { using (Context.BeginOperationScope()) { - IEnumerable milestones = new List(); + IEnumerable milestones; - if (_scope == MilestoneScope.Groups) + switch (Scope) { - var group = GetGroup(_resourceId, GroupPermission.View); - milestones = milestones.Concat(group.Milestones); - } - else if (_scope == MilestoneScope.Projects) - { - var project = GetProject(_resourceId, ProjectPermission.View); - milestones = milestones.Concat(project.Milestones); + case MilestoneScope.Groups: + var group = GetGroup(_resourceId, GroupPermission.View); + milestones = group.Milestones; + break; + case MilestoneScope.Projects: + var project = GetProject(_resourceId, ProjectPermission.View); + milestones = project.Milestones; + break; + default: + throw new NotSupportedException($"{Scope} milestone is not supported yet."); } if (query.State != null) @@ -166,18 +170,7 @@ public Models.Milestone Update(int milestoneId, MilestoneUpdate milestone) { using (Context.BeginOperationScope()) { - var ms = new Milestone(); - - if (_scope == MilestoneScope.Groups) - { - var group = GetGroup(_resourceId, GroupPermission.Edit); - ms = FindMilestone(milestoneId, group) ?? throw new GitLabNotFoundException($"Cannot find milestone with ID {milestoneId}"); - } - else if (_scope == MilestoneScope.Projects) - { - var project = GetProject(_resourceId, ProjectPermission.Edit); - ms = FindMilestone(milestoneId, project) ?? throw new GitLabNotFoundException($"Cannot find milestone with ID {milestoneId}"); - } + var ms = GetMilestone(milestoneId, true); if (!string.IsNullOrEmpty(milestone.Title)) { @@ -203,14 +196,25 @@ public Models.Milestone Update(int milestoneId, MilestoneUpdate milestone) } } - private static Milestone FindMilestone(int id, Project project) + private Milestone GetMilestone(int milestoneId, bool editing) { - return project.Milestones.FirstOrDefault(x => x.Id == id); - } + Milestone milestone; - private static Milestone FindMilestone(int id, Group group) - { - return group.Milestones.FirstOrDefault(x => x.Id == id); + switch (Scope) + { + case MilestoneScope.Groups: + var group = GetGroup(_resourceId, editing ? GroupPermission.Edit : GroupPermission.View); + milestone = group.Milestones.FirstOrDefault(x => x.Id == milestoneId); + break; + case MilestoneScope.Projects: + var project = GetProject(_resourceId, editing ? ProjectPermission.Edit : ProjectPermission.View); + milestone = project.Milestones.FirstOrDefault(x => x.Id == milestoneId); + break; + default: + throw new NotSupportedException($"{Scope} milestone is not supported yet."); + } + + return milestone ?? throw new GitLabNotFoundException($"Cannot find milestone with ID {milestoneId}"); } } } diff --git a/NGitLab.Mock/Config/GitLabGroup.cs b/NGitLab.Mock/Config/GitLabGroup.cs index 9b88e7c4..06746af5 100644 --- a/NGitLab.Mock/Config/GitLabGroup.cs +++ b/NGitLab.Mock/Config/GitLabGroup.cs @@ -11,6 +11,7 @@ public GitLabGroup() { Labels = new GitLabLabelsCollection(this); Permissions = new GitLabPermissionsCollection(this); + Milestones = new GitLabMilestonesCollection(this); } /// @@ -30,6 +31,8 @@ public GitLabGroup() public GitLabLabelsCollection Labels { get; } public GitLabPermissionsCollection Permissions { get; } + + public GitLabMilestonesCollection Milestones { get; } } public class GitLabGroupsCollection : GitLabCollection diff --git a/NGitLab.Mock/Config/GitLabHelpers.cs b/NGitLab.Mock/Config/GitLabHelpers.cs index e5d7eb51..f73a8833 100644 --- a/NGitLab.Mock/Config/GitLabHelpers.cs +++ b/NGitLab.Mock/Config/GitLabHelpers.cs @@ -582,8 +582,9 @@ public static GitLabProject WithMergeRequest(this GitLabProject project, string? /// Merge date time. /// Approvers usernames. /// Labels names. + /// Milestone name. /// Configuration method - public static GitLabProject WithMergeRequest(this GitLabProject project, string? sourceBranch = null, string? title = null, int id = default, string? targetBranch = null, string? description = null, string? author = null, string? assignee = null, DateTime? createdAt = null, DateTime? updatedAt = null, DateTime? closedAt = null, DateTime? mergedAt = null, IEnumerable? approvers = null, IEnumerable? labels = null, Action? configure = null) + public static GitLabProject WithMergeRequest(this GitLabProject project, string? sourceBranch = null, string? title = null, int id = default, string? targetBranch = null, string? description = null, string? author = null, string? assignee = null, DateTime? createdAt = null, DateTime? updatedAt = null, DateTime? closedAt = null, DateTime? mergedAt = null, IEnumerable? approvers = null, IEnumerable? labels = null, string? milestone = null, Action? configure = null) { return WithMergeRequest(project, sourceBranch, title, author, mergeRequest => { @@ -597,6 +598,8 @@ public static GitLabProject WithMergeRequest(this GitLabProject project, string? mergeRequest.UpdatedAt = updatedAt; mergeRequest.ClosedAt = closedAt; mergeRequest.MergedAt = mergedAt; + mergeRequest.Milestone = milestone; + if (labels != null) { foreach (var label in labels) @@ -723,6 +726,57 @@ public static GitLabProject WithGroupPermission(this GitLabProject project, stri }); } + /// + /// Add milestone in group + /// + /// Group. + /// Title (required) + /// Configuration method + public static GitLabGroup WithMilestone(this GitLabGroup group, string title, Action configure) + { + return Configure(group, _ => + { + var milestone = new GitLabMilestone + { + Title = title ?? throw new ArgumentNullException(nameof(title)), + }; + + group.Milestones.Add(milestone); + configure(milestone); + }); + } + + /// + /// Add milestone in group + /// + /// Group. + /// Title (required) + /// Explicit ID (config increment) + /// Description. + /// Due date time. + /// Start date time. + /// Creation date time. + /// Update date time. + /// Close date time. + /// Configuration method + public static GitLabGroup WithMilestone(this GitLabGroup group, string title, int id = default, string? description = null, DateTime? dueDate = null, DateTime? startDate = null, DateTime? createdAt = null, DateTime? updatedAt = null, DateTime? closedAt = null, Action? configure = null) + { + return WithMilestone(group, title, milestone => + { + if (id != default) + milestone.Id = id; + + milestone.Description = description; + milestone.DueDate = dueDate; + milestone.StartDate = startDate; + milestone.CreatedAt = createdAt; + milestone.UpdatedAt = updatedAt; + milestone.ClosedAt = closedAt; + + configure?.Invoke(milestone); + }); + } + /// /// Add milestone in project /// @@ -769,6 +823,8 @@ public static GitLabProject WithMilestone(this GitLabProject project, string tit milestone.CreatedAt = createdAt; milestone.UpdatedAt = updatedAt; milestone.ClosedAt = closedAt; + + configure?.Invoke(milestone); }); } @@ -1094,6 +1150,11 @@ private static void CreateGroup(GitLabServer server, GitLabGroup group) { CreatePermission(server, grp, permission); } + + foreach (var milestone in group.Milestones) + { + CreateMilestone(grp, milestone); + } } private static void CreateProject(GitLabServer server, GitLabProject project) @@ -1346,6 +1407,7 @@ private static void CreateMergeRequest(GitLabServer server, Project project, Git ClosedAt = mergeRequest.ClosedAt, MergedAt = mergeRequest.MergedAt, SourceProject = project, + Milestone = string.IsNullOrEmpty(mergeRequest.Milestone) ? null : GetOrCreateMilestone(project, mergeRequest.Milestone), }; var endedAt = request.MergedAt ?? request.ClosedAt; @@ -1398,7 +1460,19 @@ private static Permission CreatePermission(GitLabServer server, GitLabPermission : new Permission(GetOrCreateUser(server, permission.User), permission.Level); } + private static void CreateMilestone(Group group, GitLabMilestone milestone) + { + var mlt = CreateMilestone(milestone); + group.Milestones.Add(mlt); + } + private static void CreateMilestone(Project project, GitLabMilestone milestone) + { + var mlt = CreateMilestone(milestone); + project.Milestones.Add(mlt); + } + + private static Milestone CreateMilestone(GitLabMilestone milestone) { var mlt = new Milestone { @@ -1410,10 +1484,13 @@ private static void CreateMilestone(Project project, GitLabMilestone milestone) UpdatedAt = milestone.UpdatedAt ?? DateTimeOffset.UtcNow, ClosedAt = milestone.ClosedAt, }; - project.Milestones.Add(mlt); if (mlt.ClosedAt != null && mlt.UpdatedAt > mlt.ClosedAt) + { mlt.UpdatedAt = (DateTimeOffset)mlt.ClosedAt; + } + + return mlt; } private static void CreateComment(GitLabServer server, Issue issue, GitLabComment comment) @@ -1569,6 +1646,14 @@ private static User GetOrCreateUser(GitLabServer server, string username) private static Milestone GetOrCreateMilestone(Project project, string title) { var milestone = project.Milestones.FirstOrDefault(x => string.Equals(x.Title, title, StringComparison.OrdinalIgnoreCase)); + + var group = project.Group; + while (milestone == null && group != null) + { + milestone = group.Milestones.FirstOrDefault(x => string.Equals(x.Title, title, StringComparison.OrdinalIgnoreCase)); + group = group.Parent; + } + if (milestone == null) { milestone = new Milestone diff --git a/NGitLab.Mock/Config/GitLabMergeRequest.cs b/NGitLab.Mock/Config/GitLabMergeRequest.cs index 3b48152d..d07f720d 100644 --- a/NGitLab.Mock/Config/GitLabMergeRequest.cs +++ b/NGitLab.Mock/Config/GitLabMergeRequest.cs @@ -53,6 +53,8 @@ public GitLabMergeRequest() public DateTime? MergedAt { get; set; } public DateTime? ClosedAt { get; set; } + + public string Milestone { get; set; } } public class GitLabMergeRequestsCollection : GitLabCollection diff --git a/NGitLab.Mock/Config/GitLabMilestone.cs b/NGitLab.Mock/Config/GitLabMilestone.cs index f6869085..f8a108bd 100644 --- a/NGitLab.Mock/Config/GitLabMilestone.cs +++ b/NGitLab.Mock/Config/GitLabMilestone.cs @@ -34,6 +34,11 @@ internal GitLabMilestonesCollection(GitLabProject parent) { } + internal GitLabMilestonesCollection(GitLabGroup parent) + : base(parent) + { + } + internal override void SetItem(GitLabMilestone item) { if (item == null) diff --git a/NGitLab.Mock/Group.cs b/NGitLab.Mock/Group.cs index a1e2bf3b..3da65769 100644 --- a/NGitLab.Mock/Group.cs +++ b/NGitLab.Mock/Group.cs @@ -76,6 +76,8 @@ public string Name public MilestoneCollection Milestones { get; } + public IEnumerable MergeRequests => AllProjects.SelectMany(project => project.MergeRequests); + public string Path { get diff --git a/NGitLab.Mock/MergeRequest.cs b/NGitLab.Mock/MergeRequest.cs index a06e9a48..d48238bc 100644 --- a/NGitLab.Mock/MergeRequest.cs +++ b/NGitLab.Mock/MergeRequest.cs @@ -147,6 +147,8 @@ public Pipeline HeadPipeline } } + public Milestone Milestone { get; set; } + public IList Labels { get; } = new List(); public NoteCollection Comments { get; } diff --git a/NGitLab.Mock/Milestone.cs b/NGitLab.Mock/Milestone.cs index 15bca089..8a652278 100644 --- a/NGitLab.Mock/Milestone.cs +++ b/NGitLab.Mock/Milestone.cs @@ -7,6 +7,8 @@ public sealed class Milestone : GitLabObject { public Project Project => Parent as Project; + public Group Group => Parent as Group; + public int Id { get; set; } public int Iid { get; set; } diff --git a/NGitLab.Mock/PublicAPI.Unshipped.txt b/NGitLab.Mock/PublicAPI.Unshipped.txt index eda78948..16f91ab0 100644 --- a/NGitLab.Mock/PublicAPI.Unshipped.txt +++ b/NGitLab.Mock/PublicAPI.Unshipped.txt @@ -1,4 +1,4 @@ -abstract NGitLab.Mock.File.Content.get -> byte[] +abstract NGitLab.Mock.File.Content.get -> byte[] abstract NGitLab.Mock.Note.NoteableType.get -> string abstract NGitLab.Mock.Note.NoticableId.get -> int abstract NGitLab.Mock.Note.NoticableIid.get -> int @@ -133,6 +133,7 @@ NGitLab.Mock.Config.GitLabConfig.DefaultVisibility.set -> void NGitLab.Mock.Config.GitLabConfig.Serialize() -> string NGitLab.Mock.Config.GitLabConfig.Url.get -> string NGitLab.Mock.Config.GitLabConfig.Url.set -> void +NGitLab.Mock.Config.GitLabGroup.Milestones.get -> NGitLab.Mock.Config.GitLabMilestonesCollection NGitLab.Mock.Config.GitLabGroup.Visibility.get -> NGitLab.Models.VisibilityLevel? NGitLab.Mock.Config.GitLabIssue.Comments.get -> NGitLab.Mock.Config.GitLabCommentsCollection NGitLab.Mock.Config.GitLabIssue.CreatedAt.get -> System.DateTime? @@ -162,6 +163,8 @@ NGitLab.Mock.Config.GitLabJob.TagList.set -> void NGitLab.Mock.Config.GitLabJobsCollection NGitLab.Mock.Config.GitLabMergeRequest.Comments.get -> NGitLab.Mock.Config.GitLabCommentsCollection NGitLab.Mock.Config.GitLabMergeRequest.CreatedAt.get -> System.DateTime? +NGitLab.Mock.Config.GitLabMergeRequest.Milestone.get -> string +NGitLab.Mock.Config.GitLabMergeRequest.Milestone.set -> void NGitLab.Mock.Config.GitLabMergeRequest.UpdatedAt.get -> System.DateTime? NGitLab.Mock.Config.GitLabMilestone NGitLab.Mock.Config.GitLabMilestone.ClosedAt.get -> System.DateTime? @@ -443,6 +446,7 @@ NGitLab.Mock.Group.IsUserOwner(NGitLab.Mock.User user) -> bool NGitLab.Mock.Group.Labels.get -> NGitLab.Mock.LabelsCollection NGitLab.Mock.Group.LfsEnabled.get -> bool NGitLab.Mock.Group.LfsEnabled.set -> void +NGitLab.Mock.Group.MergeRequests.get -> System.Collections.Generic.IEnumerable NGitLab.Mock.Group.Milestones.get -> NGitLab.Mock.MilestoneCollection NGitLab.Mock.Group.Name.get -> string NGitLab.Mock.Group.Name.set -> void @@ -613,6 +617,8 @@ NGitLab.Mock.MergeRequest.MergedAt.set -> void NGitLab.Mock.MergeRequest.MergeRequest() -> void NGitLab.Mock.MergeRequest.MergeWhenPipelineSucceeds.get -> bool NGitLab.Mock.MergeRequest.MergeWhenPipelineSucceeds.set -> void +NGitLab.Mock.MergeRequest.Milestone.get -> NGitLab.Mock.Milestone +NGitLab.Mock.MergeRequest.Milestone.set -> void NGitLab.Mock.MergeRequest.Project.get -> NGitLab.Mock.Project NGitLab.Mock.MergeRequest.Rebase(NGitLab.Mock.User user) -> NGitLab.Models.RebaseResult NGitLab.Mock.MergeRequest.RebaseInProgress.get -> bool @@ -659,6 +665,7 @@ NGitLab.Mock.Milestone.Description.get -> string NGitLab.Mock.Milestone.Description.set -> void NGitLab.Mock.Milestone.DueDate.get -> System.DateTimeOffset NGitLab.Mock.Milestone.DueDate.set -> void +NGitLab.Mock.Milestone.Group.get -> NGitLab.Mock.Group NGitLab.Mock.Milestone.Id.get -> int NGitLab.Mock.Milestone.Id.set -> void NGitLab.Mock.Milestone.Iid.get -> int @@ -1212,8 +1219,10 @@ static NGitLab.Mock.Config.GitLabHelpers.WithIssue(this NGitLab.Mock.Config.GitL static NGitLab.Mock.Config.GitLabHelpers.WithIssue(this NGitLab.Mock.Config.GitLabProject project, string title, string author, System.Action configure) -> NGitLab.Mock.Config.GitLabProject static NGitLab.Mock.Config.GitLabHelpers.WithJob(this NGitLab.Mock.Config.GitLabPipeline pipeline, string name = null, string stage = null, NGitLab.JobStatus status = NGitLab.JobStatus.Unknown, System.DateTime? createdAt = null, System.DateTime? startedAt = null, System.DateTime? finishedAt = null, bool allowFailure = false, NGitLab.Mock.Config.GitLabPipeline downstreamPipeline = null, System.Action configure = null) -> NGitLab.Mock.Config.GitLabPipeline static NGitLab.Mock.Config.GitLabHelpers.WithMergeCommit(this NGitLab.Mock.Config.GitLabProject project, string sourceBranch, string targetBranch = null, string user = null, bool deleteSourceBranch = false, System.Collections.Generic.IEnumerable tags = null, System.Action configure = null) -> NGitLab.Mock.Config.GitLabProject -static NGitLab.Mock.Config.GitLabHelpers.WithMergeRequest(this NGitLab.Mock.Config.GitLabProject project, string sourceBranch = null, string title = null, int id = 0, string targetBranch = null, string description = null, string author = null, string assignee = null, System.DateTime? createdAt = null, System.DateTime? updatedAt = null, System.DateTime? closedAt = null, System.DateTime? mergedAt = null, System.Collections.Generic.IEnumerable approvers = null, System.Collections.Generic.IEnumerable labels = null, System.Action configure = null) -> NGitLab.Mock.Config.GitLabProject +static NGitLab.Mock.Config.GitLabHelpers.WithMergeRequest(this NGitLab.Mock.Config.GitLabProject project, string sourceBranch = null, string title = null, int id = 0, string targetBranch = null, string description = null, string author = null, string assignee = null, System.DateTime? createdAt = null, System.DateTime? updatedAt = null, System.DateTime? closedAt = null, System.DateTime? mergedAt = null, System.Collections.Generic.IEnumerable approvers = null, System.Collections.Generic.IEnumerable labels = null, string milestone = null, System.Action configure = null) -> NGitLab.Mock.Config.GitLabProject static NGitLab.Mock.Config.GitLabHelpers.WithMergeRequest(this NGitLab.Mock.Config.GitLabProject project, string sourceBranch, string title, string author, System.Action configure) -> NGitLab.Mock.Config.GitLabProject +static NGitLab.Mock.Config.GitLabHelpers.WithMilestone(this NGitLab.Mock.Config.GitLabGroup group, string title, int id = 0, string description = null, System.DateTime? dueDate = null, System.DateTime? startDate = null, System.DateTime? createdAt = null, System.DateTime? updatedAt = null, System.DateTime? closedAt = null, System.Action configure = null) -> NGitLab.Mock.Config.GitLabGroup +static NGitLab.Mock.Config.GitLabHelpers.WithMilestone(this NGitLab.Mock.Config.GitLabGroup group, string title, System.Action configure) -> NGitLab.Mock.Config.GitLabGroup static NGitLab.Mock.Config.GitLabHelpers.WithMilestone(this NGitLab.Mock.Config.GitLabProject project, string title, int id = 0, string description = null, System.DateTime? dueDate = null, System.DateTime? startDate = null, System.DateTime? createdAt = null, System.DateTime? updatedAt = null, System.DateTime? closedAt = null, System.Action configure = null) -> NGitLab.Mock.Config.GitLabProject static NGitLab.Mock.Config.GitLabHelpers.WithMilestone(this NGitLab.Mock.Config.GitLabProject project, string title, System.Action configure) -> NGitLab.Mock.Config.GitLabProject static NGitLab.Mock.Config.GitLabHelpers.WithPipeline(this NGitLab.Mock.Config.GitLabProject project, string ref, System.Action configure) -> NGitLab.Mock.Config.GitLabProject diff --git a/NGitLab.Tests/Docker/GitLabTestContext.cs b/NGitLab.Tests/Docker/GitLabTestContext.cs index e1c1e8c8..421c64cd 100644 --- a/NGitLab.Tests/Docker/GitLabTestContext.cs +++ b/NGitLab.Tests/Docker/GitLabTestContext.cs @@ -199,10 +199,10 @@ public Group CreateGroup(Action configure = null) return client.Groups.Create(groupCreate); } - public (Project Project, MergeRequest MergeRequest) CreateMergeRequest() + public (Project Project, MergeRequest MergeRequest) CreateMergeRequest(Action configure = null, Action configureProject = null) { var client = Client; - var project = CreateProject(initializeWithCommits: true); + var project = CreateProject(configureProject, initializeWithCommits: true); const string BranchForMRName = "branch-for-mr"; s_gitlabRetryPolicy.Execute(() => client.GetRepository(project.Id).Files.Create(new FileUpsert { Branch = project.DefaultBranch, CommitMessage = "test", Content = "test", Path = "test.md" })); @@ -224,12 +224,15 @@ public Group CreateGroup(Action configure = null) s_gitlabRetryPolicy.Execute(() => client.GetRepository(project.Id).Files.Update(new FileUpsert { Branch = BranchForMRName, CommitMessage = "test", Content = "test2", Path = "test.md" })); - var mr = client.GetMergeRequest(project.Id).Create(new MergeRequestCreate + var mergeRequestCreate = new MergeRequestCreate { SourceBranch = BranchForMRName, TargetBranch = project.DefaultBranch, Title = "test", - }); + }; + + configure?.Invoke(mergeRequestCreate); + var mr = client.GetMergeRequest(project.Id).Create(mergeRequestCreate); return (project, mr); } diff --git a/NGitLab.Tests/Milestone/MilestoneClientTests.cs b/NGitLab.Tests/Milestone/MilestoneClientTests.cs index a1a1412b..b5cc7e5a 100644 --- a/NGitLab.Tests/Milestone/MilestoneClientTests.cs +++ b/NGitLab.Tests/Milestone/MilestoneClientTests.cs @@ -63,6 +63,41 @@ public async Task Test_group_milestone_api() DeleteMilestone(context, MilestoneScope.Groups, group.Id, milestone); } + [Test] + [NGitLabRetry] + public async Task Test_project_milestone_merge_requests() + { + using var context = await GitLabTestContext.CreateAsync(); + var (project, mergeRequest) = context.CreateMergeRequest(); + + var milestoneClient = context.Client.GetMilestone(project.Id); + var milestone = CreateMilestone(context, MilestoneScope.Projects, project.Id, "my-super-milestone"); + + var mergeRequestClient = context.Client.GetMergeRequest(project.Id); + mergeRequestClient.Update(mergeRequest.Iid, new MergeRequestUpdate { MilestoneId = milestone.Id }); + + var mergeRequests = milestoneClient.GetMergeRequests(milestone.Id).ToArray(); + Assert.AreEqual(1, mergeRequests.Length, "The query retrieved all merged requests that assigned to the milestone."); + } + + [Test] + [NGitLabRetry] + public async Task Test_group_milestone_merge_requests() + { + using var context = await GitLabTestContext.CreateAsync(); + var group = context.CreateGroup(); + var (project, mergeRequest) = context.CreateMergeRequest(configureProject: project => project.NamespaceId = group.Id.ToString()); + + var milestoneClient = context.Client.GetGroupMilestone(group.Id); + var milestone = CreateMilestone(context, MilestoneScope.Groups, group.Id, "my-super-milestone"); + + var mergeRequestClient = context.Client.GetMergeRequest(project.Id); + mergeRequestClient.Update(mergeRequest.Iid, new MergeRequestUpdate { MilestoneId = milestone.Id }); + + var mergeRequests = milestoneClient.GetMergeRequests(milestone.Id).ToArray(); + Assert.AreEqual(1, mergeRequests.Length, "The query retrieved all merged requests that assigned to the milestone."); + } + private static Models.Milestone CreateMilestone(GitLabTestContext context, MilestoneScope scope, int id, string title) { var milestoneClient = scope == MilestoneScope.Projects ? context.Client.GetMilestone(id) : context.Client.GetGroupMilestone(id); diff --git a/NGitLab/IMilestoneClient.cs b/NGitLab/IMilestoneClient.cs index fbeec980..d4fe4fbf 100644 --- a/NGitLab/IMilestoneClient.cs +++ b/NGitLab/IMilestoneClient.cs @@ -25,5 +25,7 @@ public interface IMilestoneClient Milestone Close(int milestoneId); Milestone Activate(int milestoneId); + + IEnumerable GetMergeRequests(int milestoneId); } } diff --git a/NGitLab/Impl/MilestoneClient.cs b/NGitLab/Impl/MilestoneClient.cs index 22387e36..5e0c00e6 100644 --- a/NGitLab/Impl/MilestoneClient.cs +++ b/NGitLab/Impl/MilestoneClient.cs @@ -57,6 +57,9 @@ public Milestone Activate(int milestoneId) => _api .Put().With(new MilestoneUpdateState { NewState = nameof(MilestoneStateEvent.activate) }) .To($"{_milestonePath}/{milestoneId.ToStringInvariant()}"); + public IEnumerable GetMergeRequests(int milestoneId) => _api + .Get().GetAll($"{_milestonePath}/{milestoneId.ToStringInvariant()}/merge_requests"); + public void Delete(int milestoneId) => _api .Delete() .Execute($"{_milestonePath}/{milestoneId.ToStringInvariant()}"); diff --git a/NGitLab/PublicAPI.Unshipped.txt b/NGitLab/PublicAPI.Unshipped.txt index 0671f69d..a64495c6 100644 --- a/NGitLab/PublicAPI.Unshipped.txt +++ b/NGitLab/PublicAPI.Unshipped.txt @@ -373,6 +373,7 @@ NGitLab.IMilestoneClient.Close(int milestoneId) -> NGitLab.Models.Milestone NGitLab.IMilestoneClient.Create(NGitLab.Models.MilestoneCreate milestone) -> NGitLab.Models.Milestone NGitLab.IMilestoneClient.Delete(int milestoneId) -> void NGitLab.IMilestoneClient.Get(NGitLab.Models.MilestoneQuery query) -> System.Collections.Generic.IEnumerable +NGitLab.IMilestoneClient.GetMergeRequests(int milestoneId) -> System.Collections.Generic.IEnumerable NGitLab.IMilestoneClient.Scope.get -> NGitLab.Impl.MilestoneScope NGitLab.IMilestoneClient.this[int id].get -> NGitLab.Models.Milestone NGitLab.IMilestoneClient.Update(int milestoneId, NGitLab.Models.MilestoneUpdate milestone) -> NGitLab.Models.Milestone @@ -642,6 +643,7 @@ NGitLab.Impl.MilestoneClient.Close(int milestoneId) -> NGitLab.Models.Milestone NGitLab.Impl.MilestoneClient.Create(NGitLab.Models.MilestoneCreate milestone) -> NGitLab.Models.Milestone NGitLab.Impl.MilestoneClient.Delete(int milestoneId) -> void NGitLab.Impl.MilestoneClient.Get(NGitLab.Models.MilestoneQuery query) -> System.Collections.Generic.IEnumerable +NGitLab.Impl.MilestoneClient.GetMergeRequests(int milestoneId) -> System.Collections.Generic.IEnumerable NGitLab.Impl.MilestoneClient.MilestoneClient(NGitLab.Impl.API api, int projectId) -> void NGitLab.Impl.MilestoneClient.Scope.get -> NGitLab.Impl.MilestoneScope NGitLab.Impl.MilestoneClient.this[int id].get -> NGitLab.Models.Milestone From 7591087bdcce550d442c7136d87fc1acd978216a Mon Sep 17 00:00:00 2001 From: PM Extra Date: Thu, 24 Aug 2023 02:40:56 +0800 Subject: [PATCH 42/57] Support get single discussion by id for a merge request (#506) --- .../Clients/MergeRequestDiscussionClient.cs | 21 +++++++++++++++++-- .../MergeRequestDiscussionsClientTests.cs | 20 ++++++++++++++++++ NGitLab/IMergeRequestDiscussionClient.cs | 6 ++++++ NGitLab/Impl/MergeRequestDiscussionClient.cs | 6 ++++++ NGitLab/PublicAPI.Unshipped.txt | 4 ++++ 5 files changed, 55 insertions(+), 2 deletions(-) diff --git a/NGitLab.Mock/Clients/MergeRequestDiscussionClient.cs b/NGitLab.Mock/Clients/MergeRequestDiscussionClient.cs index a10e304c..82ad734f 100644 --- a/NGitLab.Mock/Clients/MergeRequestDiscussionClient.cs +++ b/NGitLab.Mock/Clients/MergeRequestDiscussionClient.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using NGitLab.Models; namespace NGitLab.Mock.Clients @@ -30,6 +32,21 @@ public IEnumerable All } } + public MergeRequestDiscussion Get(string id) + { + using (Context.BeginOperationScope()) + { + var discussions = GetMergeRequest().GetDiscussions(); + var discussion = discussions.FirstOrDefault(x => string.Equals(x.Id, id, StringComparison.Ordinal)); + return discussion ?? throw new GitLabNotFoundException(); + } + } + + public Task GetAsync(string id, CancellationToken cancellationToken = default) + { + return Task.FromResult(Get(id)); + } + public MergeRequestDiscussion Add(Models.MergeRequestComment comment) { return Add(new MergeRequestDiscussionCreate @@ -71,7 +88,7 @@ public MergeRequestDiscussion Resolve(MergeRequestDiscussionResolve resolve) using (Context.BeginOperationScope()) { var discussions = GetMergeRequest().GetDiscussions(); - var discussion = discussions.First(x => string.Equals(x.Id, resolve.Id, StringComparison.Ordinal)); + var discussion = discussions.FirstOrDefault(x => string.Equals(x.Id, resolve.Id, StringComparison.Ordinal)); if (discussion == null) throw new GitLabNotFoundException(); @@ -89,7 +106,7 @@ public void Delete(string discussionId, long noteId) using (Context.BeginOperationScope()) { var discussions = GetMergeRequest().GetDiscussions(); - var discussion = discussions.First(x => string.Equals(x.Id, discussionId, StringComparison.Ordinal)); + var discussion = discussions.FirstOrDefault(x => string.Equals(x.Id, discussionId, StringComparison.Ordinal)); if (discussion == null) throw new GitLabNotFoundException(); diff --git a/NGitLab.Tests/MergeRequest/MergeRequestDiscussionsClientTests.cs b/NGitLab.Tests/MergeRequest/MergeRequestDiscussionsClientTests.cs index fca5277a..bb4200ce 100644 --- a/NGitLab.Tests/MergeRequest/MergeRequestDiscussionsClientTests.cs +++ b/NGitLab.Tests/MergeRequest/MergeRequestDiscussionsClientTests.cs @@ -34,6 +34,26 @@ public async Task AddDiscussionToMergeRequest_DiscussionCreated() CollectionAssert.IsNotEmpty(discussions); } + [Test] + [NGitLabRetry] + public async Task GetDiscussion_DiscussionFound() + { + using var context = await GitLabTestContext.CreateAsync(); + var (project, mergeRequest) = context.CreateMergeRequest(); + var mergeRequestClient = context.Client.GetMergeRequest(project.Id); + var mergeRequestDiscussions = mergeRequestClient.Discussions(mergeRequest.Iid); + + const string discussionMessage = "Discussion for MR"; + var newDiscussion = new MergeRequestDiscussionCreate + { + Body = discussionMessage, + }; + var discussion = mergeRequestDiscussions.Add(newDiscussion); + + var gotDiscussion = await mergeRequestDiscussions.GetAsync(discussion.Id); + Assert.NotNull(gotDiscussion); + } + [Test] [NGitLabRetry] public async Task EditCommentFromDiscussion_CommentEdited() diff --git a/NGitLab/IMergeRequestDiscussionClient.cs b/NGitLab/IMergeRequestDiscussionClient.cs index 65c9d3f9..0b735a49 100644 --- a/NGitLab/IMergeRequestDiscussionClient.cs +++ b/NGitLab/IMergeRequestDiscussionClient.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using NGitLab.Models; namespace NGitLab @@ -7,6 +9,10 @@ public interface IMergeRequestDiscussionClient { IEnumerable All { get; } + MergeRequestDiscussion Get(string id); + + Task GetAsync(string id, CancellationToken cancellationToken = default); + MergeRequestDiscussion Add(MergeRequestDiscussionCreate discussion); MergeRequestDiscussion Resolve(MergeRequestDiscussionResolve resolve); diff --git a/NGitLab/Impl/MergeRequestDiscussionClient.cs b/NGitLab/Impl/MergeRequestDiscussionClient.cs index 48db25df..7f0d457d 100644 --- a/NGitLab/Impl/MergeRequestDiscussionClient.cs +++ b/NGitLab/Impl/MergeRequestDiscussionClient.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.Globalization; +using System.Threading; +using System.Threading.Tasks; using NGitLab.Models; namespace NGitLab.Impl @@ -17,6 +19,10 @@ public MergeRequestDiscussionClient(API api, string projectPath, int mergeReques public IEnumerable All => _api.Get().GetAll(_discussionsPath); + public MergeRequestDiscussion Get(string id) => _api.Get().To($"{_discussionsPath}/{id}"); + + public Task GetAsync(string id, CancellationToken cancellationToken = default) => _api.Get().ToAsync($"{_discussionsPath}/{id}", cancellationToken); + public MergeRequestDiscussion Add(MergeRequestDiscussionCreate comment) => _api.Post().With(comment).To(_discussionsPath); public MergeRequestDiscussion Resolve(MergeRequestDiscussionResolve resolve) => _api.Put().With(resolve).To(_discussionsPath + "/" + resolve.Id); diff --git a/NGitLab/PublicAPI.Unshipped.txt b/NGitLab/PublicAPI.Unshipped.txt index a64495c6..d28176f9 100644 --- a/NGitLab/PublicAPI.Unshipped.txt +++ b/NGitLab/PublicAPI.Unshipped.txt @@ -364,6 +364,8 @@ NGitLab.IMergeRequestDiscussionClient NGitLab.IMergeRequestDiscussionClient.Add(NGitLab.Models.MergeRequestDiscussionCreate discussion) -> NGitLab.Models.MergeRequestDiscussion NGitLab.IMergeRequestDiscussionClient.All.get -> System.Collections.Generic.IEnumerable NGitLab.IMergeRequestDiscussionClient.Delete(string discussionId, long commentId) -> void +NGitLab.IMergeRequestDiscussionClient.Get(string id) -> NGitLab.Models.MergeRequestDiscussion +NGitLab.IMergeRequestDiscussionClient.GetAsync(string id, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task NGitLab.IMergeRequestDiscussionClient.Resolve(NGitLab.Models.MergeRequestDiscussionResolve resolve) -> NGitLab.Models.MergeRequestDiscussion NGitLab.IMilestoneClient NGitLab.IMilestoneClient.Activate(int milestoneId) -> NGitLab.Models.Milestone @@ -624,6 +626,8 @@ NGitLab.Impl.MergeRequestDiscussionClient NGitLab.Impl.MergeRequestDiscussionClient.Add(NGitLab.Models.MergeRequestDiscussionCreate comment) -> NGitLab.Models.MergeRequestDiscussion NGitLab.Impl.MergeRequestDiscussionClient.All.get -> System.Collections.Generic.IEnumerable NGitLab.Impl.MergeRequestDiscussionClient.Delete(string discussionId, long commentId) -> void +NGitLab.Impl.MergeRequestDiscussionClient.Get(string id) -> NGitLab.Models.MergeRequestDiscussion +NGitLab.Impl.MergeRequestDiscussionClient.GetAsync(string id, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task NGitLab.Impl.MergeRequestDiscussionClient.MergeRequestDiscussionClient(NGitLab.Impl.API api, string projectPath, int mergeRequestIid) -> void NGitLab.Impl.MergeRequestDiscussionClient.Resolve(NGitLab.Models.MergeRequestDiscussionResolve resolve) -> NGitLab.Models.MergeRequestDiscussion NGitLab.Impl.MethodType From 40c73e27d877f571fa11f77113427172bd183106 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 07:51:40 -0400 Subject: [PATCH 43/57] Bump Microsoft.CodeAnalysis.NetAnalyzers from 7.0.3 to 7.0.4 (#509) Bumps [Microsoft.CodeAnalysis.NetAnalyzers](https://github.com/dotnet/roslyn-analyzers) from 7.0.3 to 7.0.4. - [Release notes](https://github.com/dotnet/roslyn-analyzers/releases) - [Changelog](https://github.com/dotnet/roslyn-analyzers/blob/main/PostReleaseActivities.md) - [Commits](https://github.com/dotnet/roslyn-analyzers/commits) --- updated-dependencies: - dependency-name: Microsoft.CodeAnalysis.NetAnalyzers dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 6ddd2e36..732166e0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -37,7 +37,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From c5e8fa6c9c832439033655926e25e6c0418dd3d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 08:35:45 -0400 Subject: [PATCH 44/57] Bump FluentAssertions from 6.11.0 to 6.12.0 (#510) Bumps [FluentAssertions](https://github.com/fluentassertions/fluentassertions) from 6.11.0 to 6.12.0. - [Release notes](https://github.com/fluentassertions/fluentassertions/releases) - [Changelog](https://github.com/fluentassertions/fluentassertions/blob/develop/AcceptApiChanges.ps1) - [Commits](https://github.com/fluentassertions/fluentassertions/compare/6.11.0...6.12.0) --- updated-dependencies: - dependency-name: FluentAssertions dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj b/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj index ee727082..d7e11b03 100644 --- a/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj +++ b/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj @@ -10,6 +10,6 @@ - + From 57d2bdabf52bf6bacf7b24a89d4df15bfff628e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Myka=C3=ABl=20Lemieux-Lafontaine?= <132388133+mlemieuxlafontaine-ubi@users.noreply.github.com> Date: Mon, 4 Sep 2023 13:32:26 -0400 Subject: [PATCH 45/57] Implement "IncludeInheritedMembers" in Membership client mock (#514) * Implement "IncludeInheritedMembers" in Membership client mock * Offload inheritence check to effective perms * Revert unused conversion method --- NGitLab.Mock.Tests/MembersMockTests.cs | 86 ++++++++++++++++++++++++++ NGitLab.Mock/Clients/MembersClient.cs | 4 +- NGitLab.Mock/Group.cs | 10 +-- NGitLab.Mock/Project.cs | 13 ++-- NGitLab.Mock/PublicAPI.Unshipped.txt | 2 + 5 files changed, 105 insertions(+), 10 deletions(-) create mode 100644 NGitLab.Mock.Tests/MembersMockTests.cs diff --git a/NGitLab.Mock.Tests/MembersMockTests.cs b/NGitLab.Mock.Tests/MembersMockTests.cs new file mode 100644 index 00000000..f973c2e0 --- /dev/null +++ b/NGitLab.Mock.Tests/MembersMockTests.cs @@ -0,0 +1,86 @@ +using System.Linq; +using NGitLab.Mock.Config; +using NUnit.Framework; + +namespace NGitLab.Mock.Tests +{ + public class MembersMockTests + { + [Test] + public void Test_members_group_all_direct([Values] bool isDefault) + { + using var server = new GitLabConfig() + .WithUser("user1", isDefault: true) + .WithUser("user2") + .WithGroup("G1", 1, addDefaultUserAsMaintainer: true) + .WithGroup("G2", 2, @namespace: "G1", configure: g => g.WithUserPermission("user2", Models.AccessLevel.Maintainer)) + .BuildServer(); + + var client = server.CreateClient("user1"); + var members = isDefault + ? client.Members.OfGroup("2") + : client.Members.OfGroup("2", includeInheritedMembers: false); + + Assert.AreEqual(1, members.Count(), "Membership found are invalid"); + } + + [Test] + public void Test_members_group_all_inherited() + { + using var server = new GitLabConfig() + .WithUser("user1", isDefault: true) + .WithUser("user2") + .WithProject("Test") + .WithGroup("G1", 1, configure: g => g.WithUserPermission("user1", Models.AccessLevel.Maintainer)) + .WithGroup("G2", 2, @namespace: "G1", configure: g => g.WithUserPermission("user2", Models.AccessLevel.Maintainer)) + .BuildServer(); + + var client = server.CreateClient("user1"); + var members = client.Members.OfGroup("2", includeInheritedMembers: true); + + Assert.AreEqual(2, members.Count(), "Membership found are invalid"); + } + + [Test] + public void Test_members_project_all_direct([Values] bool isDefault) + { + using var server = new GitLabConfig() + .WithUser("user1", isDefault: true) + .WithUser("user2") + .WithUser("user3") + .WithGroup("G1", 1, addDefaultUserAsMaintainer: true) + .WithGroup("G2", 2, @namespace: "G1", configure: g => g.WithUserPermission("user2", Models.AccessLevel.Maintainer)) + .WithProject("Project", @namespace: "G1", configure: g => + g.WithUserPermission("user3", Models.AccessLevel.Maintainer) + .WithGroupPermission("G2", Models.AccessLevel.Developer)) + .BuildServer(); + + var client = server.CreateClient("user1"); + var members = isDefault + ? client.Members.OfProject("1") + : client.Members.OfProject("1", includeInheritedMembers: false); + + Assert.AreEqual(1, members.Count(), "Membership found are invalid"); + } + + [Test] + public void Test_members_project_all_inherited() + { + using var server = new GitLabConfig() + .WithUser("user1", isDefault: true) + .WithUser("user2") + .WithUser("user3") + .WithGroup("G1", addDefaultUserAsMaintainer: true) + .WithGroup("G2", @namespace: "G1", configure: g => g.WithUserPermission("user2", Models.AccessLevel.Maintainer)) + .WithProject("Project", 1, @namespace: "G1", configure: g => + g.WithUserPermission("user3", Models.AccessLevel.Maintainer) + .WithGroupPermission("G1/G2", Models.AccessLevel.Developer)) + .BuildServer(); + + var client = server.CreateClient("user1"); + var members = client.Members.OfProject("1", includeInheritedMembers: true); + + Assert.AreEqual(3, members.Count(), "Membership found are invalid"); + } + } +} diff --git a/NGitLab.Mock/Clients/MembersClient.cs b/NGitLab.Mock/Clients/MembersClient.cs index a516dfe2..7b33a3e2 100644 --- a/NGitLab.Mock/Clients/MembersClient.cs +++ b/NGitLab.Mock/Clients/MembersClient.cs @@ -98,7 +98,7 @@ public IEnumerable OfGroup(string groupId, bool includeInheritedMemb using (Context.BeginOperationScope()) { var group = GetGroup(groupId, GroupPermission.View); - var members = group.GetEffectivePermissions().Permissions; + var members = group.GetEffectivePermissions(includeInheritedMembers).Permissions; return members.Select(member => member.ToMembershipClient()); } } @@ -118,7 +118,7 @@ public IEnumerable OfProject(string projectId, bool includeInherited using (Context.BeginOperationScope()) { var project = GetProject(projectId, ProjectPermission.View); - var members = project.GetEffectivePermissions().Permissions; + var members = project.GetEffectivePermissions(includeInheritedMembers).Permissions; return members.Select(member => member.ToMembershipClient()); } } diff --git a/NGitLab.Mock/Group.cs b/NGitLab.Mock/Group.cs index 3da65769..aea0de2d 100644 --- a/NGitLab.Mock/Group.cs +++ b/NGitLab.Mock/Group.cs @@ -117,13 +117,15 @@ public IEnumerable DescendantGroups public IEnumerable AllProjects => Projects.Concat(DescendantGroups.SelectMany(group => group.Projects)); - public EffectivePermissions GetEffectivePermissions() + public EffectivePermissions GetEffectivePermissions() => GetEffectivePermissions(includeInheritedPermissions: true); + + public EffectivePermissions GetEffectivePermissions(bool includeInheritedPermissions) { var result = new Dictionary(); - if (Parent != null) + if (Parent != null && includeInheritedPermissions) { - foreach (var effectivePermission in Parent.GetEffectivePermissions().Permissions) + foreach (var effectivePermission in Parent.GetEffectivePermissions(includeInheritedPermissions).Permissions) { Add(effectivePermission.User, effectivePermission.AccessLevel); } @@ -137,7 +139,7 @@ public EffectivePermissions GetEffectivePermissions() } else { - foreach (var effectivePermission in permission.Group.GetEffectivePermissions().Permissions) + foreach (var effectivePermission in permission.Group.GetEffectivePermissions(includeInheritedPermissions).Permissions) { Add(effectivePermission.User, effectivePermission.AccessLevel); } diff --git a/NGitLab.Mock/Project.cs b/NGitLab.Mock/Project.cs index 370aa69f..86f99ba9 100644 --- a/NGitLab.Mock/Project.cs +++ b/NGitLab.Mock/Project.cs @@ -154,13 +154,18 @@ public void Remove() Group.Projects.Remove(this); } - public EffectivePermissions GetEffectivePermissions() + public EffectivePermissions GetEffectivePermissions() => GetEffectivePermissions(includeInheritedPermissions: true); + + public EffectivePermissions GetEffectivePermissions(bool includeInheritedPermissions) { var result = new Dictionary(); - foreach (var effectivePermission in Group.GetEffectivePermissions().Permissions) + if (includeInheritedPermissions) { - Add(effectivePermission.User, effectivePermission.AccessLevel); + foreach (var effectivePermission in Group.GetEffectivePermissions(includeInheritedPermissions).Permissions) + { + Add(effectivePermission.User, effectivePermission.AccessLevel); + } } foreach (var permission in Permissions) @@ -171,7 +176,7 @@ public EffectivePermissions GetEffectivePermissions() } else { - foreach (var effectivePermission in permission.Group.GetEffectivePermissions().Permissions) + foreach (var effectivePermission in permission.Group.GetEffectivePermissions(includeInheritedPermissions).Permissions) { Add(effectivePermission.User, effectivePermission.AccessLevel); } diff --git a/NGitLab.Mock/PublicAPI.Unshipped.txt b/NGitLab.Mock/PublicAPI.Unshipped.txt index 16f91ab0..796103c3 100644 --- a/NGitLab.Mock/PublicAPI.Unshipped.txt +++ b/NGitLab.Mock/PublicAPI.Unshipped.txt @@ -435,6 +435,7 @@ NGitLab.Mock.Group.ExtraSharedRunnersLimit.get -> System.TimeSpan NGitLab.Mock.Group.ExtraSharedRunnersLimit.set -> void NGitLab.Mock.Group.FullName.get -> string NGitLab.Mock.Group.GetEffectivePermissions() -> NGitLab.Mock.EffectivePermissions +NGitLab.Mock.Group.GetEffectivePermissions(bool includeInheritedPermissions) -> NGitLab.Mock.EffectivePermissions NGitLab.Mock.Group.Group() -> void NGitLab.Mock.Group.Group(NGitLab.Mock.User user) -> void NGitLab.Mock.Group.Group(string name) -> void @@ -810,6 +811,7 @@ NGitLab.Mock.Project.ForkingAccessLevel.get -> NGitLab.Models.RepositoryAccessLe NGitLab.Mock.Project.ForkingAccessLevel.set -> void NGitLab.Mock.Project.FullName.get -> string NGitLab.Mock.Project.GetEffectivePermissions() -> NGitLab.Mock.EffectivePermissions +NGitLab.Mock.Project.GetEffectivePermissions(bool includeInheritedPermissions) -> NGitLab.Mock.EffectivePermissions NGitLab.Mock.Project.Group.get -> NGitLab.Mock.Group NGitLab.Mock.Project.Hooks.get -> NGitLab.Mock.ProjectHookCollection NGitLab.Mock.Project.HttpUrl.get -> string From 15c8c77854a3d4d762b527e796e4e0e8a9981a13 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 19:11:16 +0000 Subject: [PATCH 46/57] Bump YamlDotNet from 13.2.0 to 13.3.1 (#518) Bumps [YamlDotNet](https://github.com/aaubry/YamlDotNet) from 13.2.0 to 13.3.1. - [Release notes](https://github.com/aaubry/YamlDotNet/releases) - [Commits](https://github.com/aaubry/YamlDotNet/compare/v13.2.0...v13.3.1) --- updated-dependencies: - dependency-name: YamlDotNet dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Mock/NGitLab.Mock.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab.Mock/NGitLab.Mock.csproj b/NGitLab.Mock/NGitLab.Mock.csproj index 1e2f8222..a47732f3 100644 --- a/NGitLab.Mock/NGitLab.Mock.csproj +++ b/NGitLab.Mock/NGitLab.Mock.csproj @@ -13,7 +13,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From fde8c62576f82c4df09a6584edcc48fb59d3c3f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 09:10:18 -0400 Subject: [PATCH 47/57] Bump Meziantou.Analyzer from 2.0.82 to 2.0.83 (#517) Bumps [Meziantou.Analyzer](https://github.com/meziantou/Meziantou.Analyzer) from 2.0.82 to 2.0.83. - [Release notes](https://github.com/meziantou/Meziantou.Analyzer/releases) - [Commits](https://github.com/meziantou/Meziantou.Analyzer/compare/2.0.82...2.0.83) --- updated-dependencies: - dependency-name: Meziantou.Analyzer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 732166e0..961d54e6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -42,7 +42,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From a6333c1a66f6b05accec3e40ae5d4ce688e4256f Mon Sep 17 00:00:00 2001 From: Steve Ryan Date: Thu, 7 Sep 2023 00:52:25 +1000 Subject: [PATCH 48/57] Implements since and until parameters when retrieving commit list (#516) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Implements since and until parameters when retrieving commit list * Replaces WebUtility.UrlEncode with Uri.EscapeDataString --------- Co-authored-by: Gérald Barré --- .../RepositoryClient/RepositoryClientTests.cs | 96 +++++++++++++++++++ NGitLab/GetCommitsRequest.cs | 8 +- NGitLab/Impl/RepositoryClient.cs | 11 +++ NGitLab/PublicAPI.Unshipped.txt | 4 + 4 files changed, 118 insertions(+), 1 deletion(-) diff --git a/NGitLab.Tests/RepositoryClient/RepositoryClientTests.cs b/NGitLab.Tests/RepositoryClient/RepositoryClientTests.cs index 125adc57..0f756ccb 100644 --- a/NGitLab.Tests/RepositoryClient/RepositoryClientTests.cs +++ b/NGitLab.Tests/RepositoryClient/RepositoryClientTests.cs @@ -130,6 +130,102 @@ public async Task GetCommitBySha1Range() Assert.AreEqual(allCommits[3].Id, commits[1].Id); } + [Test] + [NGitLabRetry] + public async Task GetCommitsSince() + { + // Arrange + using var context = await RepositoryClientTestsContext.CreateAsync(commitCount: 5); + + var defaultBranch = context.Project.DefaultBranch; + var since = DateTime.UtcNow; + var expectedSinceValue = Uri.EscapeDataString(since.ToString("s", CultureInfo.InvariantCulture)); + var commitRequest = new GetCommitsRequest + { + RefName = defaultBranch, + Since = since, + }; + + // Act + var commits = context.RepositoryClient.GetCommits(commitRequest).ToArray(); + + // Assert + var lastRequestQueryString = context.Context.LastRequest.RequestUri.Query; + + Assert.True(lastRequestQueryString.Contains($"since={expectedSinceValue}")); + } + + [Test] + [NGitLabRetry] + public async Task GetCommitsDoesntIncludeSinceWhenNotSpecified() + { + // Arrange + using var context = await RepositoryClientTestsContext.CreateAsync(commitCount: 5); + + var defaultBranch = context.Project.DefaultBranch; + var commitRequest = new GetCommitsRequest + { + RefName = defaultBranch, + Since = null, + }; + + // Act + var commits = context.RepositoryClient.GetCommits(commitRequest).ToArray(); + + // Assert + var lastRequestQueryString = context.Context.LastRequest.RequestUri.Query; + + Assert.False(lastRequestQueryString.Contains("since=")); + } + + [Test] + [NGitLabRetry] + public async Task GetCommitsUntil() + { + // Arrange + using var context = await RepositoryClientTestsContext.CreateAsync(commitCount: 5); + + var defaultBranch = context.Project.DefaultBranch; + var until = DateTime.UtcNow; + var expectedUntilValue = Uri.EscapeDataString(until.ToString("s", CultureInfo.InvariantCulture)); + var commitRequest = new GetCommitsRequest + { + RefName = defaultBranch, + Until = until, + }; + + // Act + var commits = context.RepositoryClient.GetCommits(commitRequest).ToArray(); + + // Assert + var lastRequestQueryString = context.Context.LastRequest.RequestUri.Query; + + Assert.True(lastRequestQueryString.Contains($"until={expectedUntilValue}")); + } + + [Test] + [NGitLabRetry] + public async Task GetCommitsDoesntIncludeUntilWhenNotSpecified() + { + // Arrange + using var context = await RepositoryClientTestsContext.CreateAsync(commitCount: 5); + + var defaultBranch = context.Project.DefaultBranch; + var commitRequest = new GetCommitsRequest + { + RefName = defaultBranch, + Until = null, + }; + + // Act + var commits = context.RepositoryClient.GetCommits(commitRequest).ToArray(); + + // Assert + var lastRequestQueryString = context.Context.LastRequest.RequestUri.Query; + + Assert.False(lastRequestQueryString.Contains("until=")); + } + [Test] [NGitLabRetry] public async Task GetCommitDiff() diff --git a/NGitLab/GetCommitsRequest.cs b/NGitLab/GetCommitsRequest.cs index 3f3a9a75..ec24a860 100644 --- a/NGitLab/GetCommitsRequest.cs +++ b/NGitLab/GetCommitsRequest.cs @@ -1,4 +1,6 @@ -namespace NGitLab +using System; + +namespace NGitLab { public class GetCommitsRequest { @@ -13,5 +15,9 @@ public class GetCommitsRequest public int MaxResults { get; set; } public uint PerPage { get; set; } = DefaultPerPage; + + public DateTime? Since { get; set; } + + public DateTime? Until { get; set; } } } diff --git a/NGitLab/Impl/RepositoryClient.cs b/NGitLab/Impl/RepositoryClient.cs index e66d513c..56a0c0ea 100644 --- a/NGitLab/Impl/RepositoryClient.cs +++ b/NGitLab/Impl/RepositoryClient.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using NGitLab.Extensions; @@ -85,6 +86,16 @@ public IEnumerable GetCommits(GetCommitsRequest request) lst.Add($"first_parent={Uri.EscapeDataString(request.FirstParent.ToString())}"); } + if (request.Since.HasValue) + { + lst.Add($"since={Uri.EscapeDataString(request.Since.Value.ToString("s", CultureInfo.InvariantCulture))}"); + } + + if (request.Until.HasValue) + { + lst.Add($"until={Uri.EscapeDataString(request.Until.Value.ToString("s", CultureInfo.InvariantCulture))}"); + } + var perPage = request.MaxResults > 0 ? Math.Min(request.MaxResults, request.PerPage) : request.PerPage; lst.Add($"per_page={perPage.ToStringInvariant()}"); diff --git a/NGitLab/PublicAPI.Unshipped.txt b/NGitLab/PublicAPI.Unshipped.txt index d28176f9..75705c78 100644 --- a/NGitLab/PublicAPI.Unshipped.txt +++ b/NGitLab/PublicAPI.Unshipped.txt @@ -35,6 +35,10 @@ NGitLab.GetCommitsRequest.PerPage.get -> uint NGitLab.GetCommitsRequest.PerPage.set -> void NGitLab.GetCommitsRequest.RefName.get -> string NGitLab.GetCommitsRequest.RefName.set -> void +NGitLab.GetCommitsRequest.Since.get -> System.DateTime? +NGitLab.GetCommitsRequest.Since.set -> void +NGitLab.GetCommitsRequest.Until.get -> System.DateTime? +NGitLab.GetCommitsRequest.Until.set -> void NGitLab.GitLabClient NGitLab.GitLabClient.AdvancedSearch.get -> NGitLab.ISearchClient NGitLab.GitLabClient.Deployments.get -> NGitLab.IDeploymentClient From 0bcd2e89956fef3fb26afc9130f1c90920262d10 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 07:42:06 -0400 Subject: [PATCH 49/57] Bump Meziantou.Analyzer from 2.0.83 to 2.0.84 (#521) Bumps [Meziantou.Analyzer](https://github.com/meziantou/Meziantou.Analyzer) from 2.0.83 to 2.0.84. - [Release notes](https://github.com/meziantou/Meziantou.Analyzer/releases) - [Commits](https://github.com/meziantou/Meziantou.Analyzer/compare/2.0.83...2.0.84) --- updated-dependencies: - dependency-name: Meziantou.Analyzer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 961d54e6..14b7f1f8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -42,7 +42,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 7c3a521a4f5b84dc94ebd073047c2bbba98314c4 Mon Sep 17 00:00:00 2001 From: Louis Zanella Date: Mon, 11 Sep 2023 14:19:35 -0400 Subject: [PATCH 50/57] Make sure GitLabTestContextRequestOptions.LogHeaders() masks authent data (#523) --- .../Docker/GitLabTestContextRequestOptions.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/NGitLab.Tests/Docker/GitLabTestContextRequestOptions.cs b/NGitLab.Tests/Docker/GitLabTestContextRequestOptions.cs index 32d10a47..f7d45b65 100644 --- a/NGitLab.Tests/Docker/GitLabTestContextRequestOptions.cs +++ b/NGitLab.Tests/Docker/GitLabTestContextRequestOptions.cs @@ -199,13 +199,21 @@ private static void LogHeaders(StringBuilder sb, WebHeaderCollection headers) foreach (var headerValue in headerValues) { + sb.Append(headerName).Append(": "); if (string.Equals(headerName, "Private-Token", StringComparison.OrdinalIgnoreCase)) { - sb.Append("Private-Token").Append(": ****** ").AppendLine(); + sb.AppendLine("******"); + } + else if (string.Equals(headerName, "Authorization", StringComparison.OrdinalIgnoreCase)) + { + const string BearerTokenPrefix = "Bearer "; + if (headerValue.StartsWith(BearerTokenPrefix, StringComparison.Ordinal)) + sb.Append(BearerTokenPrefix); + sb.AppendLine("******"); } else { - sb.Append(headerName).Append(": ").Append(headerValue).AppendLine(); + sb.AppendLine(headerValue); } } } From f2d7b021d0c99229e567bddddd150f966799f43e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:55:48 -0400 Subject: [PATCH 51/57] Bump actions/checkout from 3 to 4 (#520) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Thomas Cortes <78750681+Toa741@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dbaeed57..621af2e4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: create_nuget: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 # Get all the history so MinGit can compute the version - name: Setup .NET Core (latest) @@ -65,7 +65,7 @@ jobs: GITLAB_OMNIBUS_CONFIG: "external_url 'http://localhost:48624/'" GITLAB_ROOT_PASSWORD: "Pa$$w0rd" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup .NET Core (latest) uses: actions/setup-dotnet@v3 - run: | From 64ffd1d1bd2a8b6952ba11a9fe889591d412bef2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 08:02:03 -0400 Subject: [PATCH 52/57] Bump Meziantou.Analyzer from 2.0.84 to 2.0.85 (#526) Bumps [Meziantou.Analyzer](https://github.com/meziantou/Meziantou.Analyzer) from 2.0.84 to 2.0.85. - [Release notes](https://github.com/meziantou/Meziantou.Analyzer/releases) - [Commits](https://github.com/meziantou/Meziantou.Analyzer/compare/2.0.84...2.0.85) --- updated-dependencies: - dependency-name: Meziantou.Analyzer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 14b7f1f8..1861a9b9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -42,7 +42,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From ab145aba7b512b6a5647e79d00058f82bfea0bf4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 08:28:13 -0400 Subject: [PATCH 53/57] Bump NSubstitute from 5.0.0 to 5.1.0 (#522) Bumps NSubstitute from 5.0.0 to 5.1.0. --- updated-dependencies: - dependency-name: NSubstitute dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Tests/NGitLab.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab.Tests/NGitLab.Tests.csproj index d0cdbcbc..314a6314 100644 --- a/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab.Tests/NGitLab.Tests.csproj @@ -19,7 +19,7 @@ - + From 8817e1114e6fcca6eaca6e0dfb70e9d68982ad0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Mon, 18 Sep 2023 16:19:13 -0400 Subject: [PATCH 54/57] Support pipeline retry endpoint (#527) --- NGitLab.Mock/Clients/PipelineClient.cs | 14 +++++++++++ NGitLab.Tests/JobTests.cs | 3 ++- NGitLab.Tests/PipelineTests.cs | 33 +++++++++++++++++++++++--- NGitLab/IPipelineClient.cs | 2 ++ NGitLab/Impl/PipelineClient.cs | 6 +++++ NGitLab/PublicAPI.Unshipped.txt | 2 ++ 6 files changed, 56 insertions(+), 4 deletions(-) diff --git a/NGitLab.Mock/Clients/PipelineClient.cs b/NGitLab.Mock/Clients/PipelineClient.cs index 065e3e25..97c028c6 100644 --- a/NGitLab.Mock/Clients/PipelineClient.cs +++ b/NGitLab.Mock/Clients/PipelineClient.cs @@ -291,5 +291,19 @@ public GitLabCollectionResponse GetVariablesAsync(int pipeline { return GitLabCollectionResponse.Create(GetVariables(pipelineId)); } + + public Task RetryAsync(int pipelineId, CancellationToken cancellationToken = default) + { + using (Context.BeginOperationScope()) + { + var jobs = _jobClient.GetJobs(JobScopeMask.Failed).Where(j => j.Pipeline.Id == pipelineId); + foreach (var job in jobs) + { + _jobClient.RunAction(job.Id, JobAction.Retry); + } + + return Task.FromResult(this[pipelineId]); + } + } } } diff --git a/NGitLab.Tests/JobTests.cs b/NGitLab.Tests/JobTests.cs index 4911446c..247cdf6b 100644 --- a/NGitLab.Tests/JobTests.cs +++ b/NGitLab.Tests/JobTests.cs @@ -10,7 +10,7 @@ namespace NGitLab.Tests { public class JobTests { - internal static void AddGitLabCiFile(IGitLabClient client, Project project, int jobCount = 1, bool manualAction = false, string branch = null) + internal static void AddGitLabCiFile(IGitLabClient client, Project project, int jobCount = 1, bool manualAction = false, string branch = null, bool pipelineSucceeds = true) { var content = @" variables: @@ -24,6 +24,7 @@ internal static void AddGitLabCiFile(IGitLabClient client, Project project, int script: - echo test - echo test > file{i.ToString(CultureInfo.InvariantCulture)}.txt + - exit {(pipelineSucceeds ? "0" : "1")} artifacts: paths: - '*.txt' diff --git a/NGitLab.Tests/PipelineTests.cs b/NGitLab.Tests/PipelineTests.cs index bdf2ff38..80c004f9 100644 --- a/NGitLab.Tests/PipelineTests.cs +++ b/NGitLab.Tests/PipelineTests.cs @@ -200,13 +200,40 @@ public async Task Test_get_triggered_pipeline_variables() var trigger = triggers.Create("Test Trigger"); var ciJobToken = trigger.Token; - var pipeline = pipelineClient.CreatePipelineWithTrigger(ciJobToken, project.DefaultBranch, new Dictionary(StringComparer.InvariantCulture) { { "Test", "HelloWorld" } }); + var pipeline = pipelineClient.CreatePipelineWithTrigger(ciJobToken, project.DefaultBranch, new Dictionary(StringComparer.Ordinal) { { "Test", "HelloWorld" } }); var variables = pipelineClient.GetVariables(pipeline.Id); Assert.IsTrue(variables.Any(v => - v.Key.Equals("Test", StringComparison.InvariantCulture) && - v.Value.Equals("HelloWorld", StringComparison.InvariantCulture))); + v.Key.Equals("Test", StringComparison.Ordinal) && + v.Value.Equals("HelloWorld", StringComparison.Ordinal))); + } + + [Test] + [NGitLabRetry] + public async Task Test_retry() + { + using var context = await GitLabTestContext.CreateAsync(); + var project = context.CreateProject(); + var pipelineClient = context.Client.GetPipelines(project.Id); + + using (await context.StartRunnerForOneJobAsync(project.Id)) + { + JobTests.AddGitLabCiFile(context.Client, project, pipelineSucceeds: false); + var pipeline = await GitLabTestContext.RetryUntilAsync(() => pipelineClient.All.FirstOrDefault(), pipeline => + { + if (pipeline != null) + { + TestContext.WriteLine("Pipeline status: " + pipeline.Status); + return pipeline.Status is JobStatus.Failed; + } + + return false; + }, TimeSpan.FromMinutes(2)); + + var retriedPipeline = await pipelineClient.RetryAsync(pipeline.Id); + Assert.AreNotEqual(JobStatus.Failed, retriedPipeline.Status); // Should be created or running + } } } } diff --git a/NGitLab/IPipelineClient.cs b/NGitLab/IPipelineClient.cs index 151d0ee5..5240b9d0 100644 --- a/NGitLab/IPipelineClient.cs +++ b/NGitLab/IPipelineClient.cs @@ -115,5 +115,7 @@ public interface IPipelineClient /// /// GitLabCollectionResponse GetBridgesAsync(PipelineBridgeQuery query); + + Task RetryAsync(int pipelineId, CancellationToken cancellationToken = default); } } diff --git a/NGitLab/Impl/PipelineClient.cs b/NGitLab/Impl/PipelineClient.cs index aa9743dc..6f9305ac 100644 --- a/NGitLab/Impl/PipelineClient.cs +++ b/NGitLab/Impl/PipelineClient.cs @@ -209,5 +209,11 @@ private string CreateGetBridgesUrl(PipelineBridgeQuery query) url = Utils.AddParameter(url, "scope", query.Scope); return url; } + + public Task RetryAsync(int pipelineId, CancellationToken cancellationToken = default) + { + var url = $"{_pipelinesPath}/{pipelineId.ToStringInvariant()}/retry"; + return _api.Post().ToAsync(url, cancellationToken); + } } } diff --git a/NGitLab/PublicAPI.Unshipped.txt b/NGitLab/PublicAPI.Unshipped.txt index 75705c78..4f9b3455 100644 --- a/NGitLab/PublicAPI.Unshipped.txt +++ b/NGitLab/PublicAPI.Unshipped.txt @@ -685,6 +685,7 @@ NGitLab.Impl.PipelineClient.GetTestReportsSummary(int pipelineId) -> NGitLab.Mod NGitLab.Impl.PipelineClient.GetVariables(int pipelineId) -> System.Collections.Generic.IEnumerable NGitLab.Impl.PipelineClient.GetVariablesAsync(int pipelineId) -> NGitLab.GitLabCollectionResponse NGitLab.Impl.PipelineClient.PipelineClient(NGitLab.Impl.API api, int projectId) -> void +NGitLab.Impl.PipelineClient.RetryAsync(int pipelineId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task NGitLab.Impl.PipelineClient.Search(NGitLab.Models.PipelineQuery query) -> System.Collections.Generic.IEnumerable NGitLab.Impl.PipelineClient.SearchAsync(NGitLab.Models.PipelineQuery query) -> NGitLab.GitLabCollectionResponse NGitLab.Impl.PipelineClient.this[int id].get -> NGitLab.Models.Pipeline @@ -857,6 +858,7 @@ NGitLab.IPipelineClient.GetTestReports(int pipelineId) -> NGitLab.Models.TestRep NGitLab.IPipelineClient.GetTestReportsSummary(int pipelineId) -> NGitLab.Models.TestReportSummary NGitLab.IPipelineClient.GetVariables(int pipelineId) -> System.Collections.Generic.IEnumerable NGitLab.IPipelineClient.GetVariablesAsync(int pipelineId) -> NGitLab.GitLabCollectionResponse +NGitLab.IPipelineClient.RetryAsync(int pipelineId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task NGitLab.IPipelineClient.Search(NGitLab.Models.PipelineQuery query) -> System.Collections.Generic.IEnumerable NGitLab.IPipelineClient.SearchAsync(NGitLab.Models.PipelineQuery query) -> NGitLab.GitLabCollectionResponse NGitLab.IPipelineClient.this[int id].get -> NGitLab.Models.Pipeline From eb4f2619b17eca1af0fe234d43cec4f1da314cf2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 07:45:00 -0400 Subject: [PATCH 55/57] Bump Microsoft.Playwright from 1.37.1 to 1.38.0 (#529) Bumps [Microsoft.Playwright](https://github.com/microsoft/playwright-dotnet) from 1.37.1 to 1.38.0. - [Release notes](https://github.com/microsoft/playwright-dotnet/releases) - [Commits](https://github.com/microsoft/playwright-dotnet/compare/v1.37.1...v1.38.0) --- updated-dependencies: - dependency-name: Microsoft.Playwright dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Tests/NGitLab.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab.Tests/NGitLab.Tests.csproj index 314a6314..9652372f 100644 --- a/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab.Tests/NGitLab.Tests.csproj @@ -15,7 +15,7 @@ - + From 1598f2e6e6fbd57bf8ceba8ae124efdaee92dd08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 08:23:27 -0400 Subject: [PATCH 56/57] Bump YamlDotNet from 13.3.1 to 13.4.0 (#530) Bumps [YamlDotNet](https://github.com/aaubry/YamlDotNet) from 13.3.1 to 13.4.0. - [Release notes](https://github.com/aaubry/YamlDotNet/releases) - [Commits](https://github.com/aaubry/YamlDotNet/compare/v13.3.1...v13.4.0) --- updated-dependencies: - dependency-name: YamlDotNet dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Mock/NGitLab.Mock.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab.Mock/NGitLab.Mock.csproj b/NGitLab.Mock/NGitLab.Mock.csproj index a47732f3..d44f59d7 100644 --- a/NGitLab.Mock/NGitLab.Mock.csproj +++ b/NGitLab.Mock/NGitLab.Mock.csproj @@ -13,7 +13,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From adea3646cc736913f5142936d8b116a09241f0b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 10:23:49 -0400 Subject: [PATCH 57/57] Bump Microsoft.NET.Test.Sdk from 17.7.1 to 17.7.2 (#519) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.7.1 to 17.7.2. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.7.1...v17.7.2) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj | 2 +- NGitLab.Tests/NGitLab.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj b/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj index d7e11b03..cb56d584 100644 --- a/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj +++ b/NGitLab.Mock.Tests/NGitLab.Mock.Tests.csproj @@ -7,7 +7,7 @@ - + diff --git a/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab.Tests/NGitLab.Tests.csproj index 9652372f..bab4a6bb 100644 --- a/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab.Tests/NGitLab.Tests.csproj @@ -14,7 +14,7 @@ - +