Skip to content

Commit

Permalink
Add support for Lint endpoints (#460)
Browse files Browse the repository at this point in the history
* Add Lint Client

* Update PublicAPI.Unshipped.txt

* Update 7 files

* Update PublicAPI.Unshipped.txt

* Put the tests yamls in constants
  • Loading branch information
mchanyeechoy authored Jun 7, 2023
1 parent 3fc47f9 commit 5ac8552
Show file tree
Hide file tree
Showing 10 changed files with 271 additions and 0 deletions.
2 changes: 2 additions & 0 deletions NGitLab.Mock/Clients/GitLabClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ public IGraphQLClient GraphQL

public ISearchClient AdvancedSearch => new AdvancedSearchClient(Context);

public ILintClient Lint => new LintClient(Context);

public IEventClient GetEvents() => new EventClient(Context);

public IEventClient GetUserEvents(int userId) => new EventClient(Context, userId: userId);
Expand Down
27 changes: 27 additions & 0 deletions NGitLab.Mock/Clients/LintClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using NGitLab.Models;

namespace NGitLab.Mock.Clients
{
internal class LintClient : ILintClient
{
private readonly ClientContext _context;

public LintClient(ClientContext context)
{
_context = context;
}

public Task<LintCI> ValidateCIYamlContentAsync(string projectId, string yamlContent, LintCIOptions options, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}

public Task<LintCI> ValidateProjectCIConfigurationAsync(string projectId, LintCIOptions options, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
}
}
106 changes: 106 additions & 0 deletions NGitLab.Tests/LintClientTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NGitLab.Models;
using NGitLab.Tests.Docker;
using NUnit.Framework;

namespace NGitLab.Tests
{
public class LintClientTests
{
private const string ValidCIYaml = @"
variables:
CI_DEBUG_TRACE: ""true""
build:
script:
- echo test
";

private const string InvalidCIYaml = @"
variables:
CI_DEBUG_TRACE: ""true""
build:
script:
- echo test
this_key_should_not_exist:
- this should fail the linting
";

[Test]
[NGitLabRetry]
public async Task LintValidCIYaml()
{
using var context = await GitLabTestContext.CreateAsync();
var project = context.CreateProject();
var lintClient = context.Client.Lint;

var result = await context.Client.Lint.ValidateCIYamlContentAsync(project.Id.ToString(), ValidCIYaml, new(), CancellationToken.None);

Assert.True(result.Valid);
Assert.False(result.Errors.Any());
Assert.False(result.Warnings.Any());
}

[Test]
[NGitLabRetry]
public async Task LintInvalidCIYaml()
{
using var context = await GitLabTestContext.CreateAsync();
var project = context.CreateProject();
var lintClient = context.Client.Lint;

var result = await context.Client.Lint.ValidateCIYamlContentAsync(project.Id.ToString(), InvalidCIYaml, new(), CancellationToken.None);

Assert.False(result.Valid);
Assert.True(result.Errors.Any());
Assert.False(result.Warnings.Any());
}

[Test]
[NGitLabRetry]
public async Task LintValidCIProjectYaml()
{
using var context = await GitLabTestContext.CreateAsync();
var project = context.CreateProject();
var lintClient = context.Client.Lint;

context.Client.GetRepository(project.Id).Files.Create(new FileUpsert
{
Branch = project.DefaultBranch,
CommitMessage = "test",
Path = ".gitlab-ci.yml",
Content = ValidCIYaml,
});

var result = await context.Client.Lint.ValidateProjectCIConfigurationAsync(project.Id.ToString(), new(), CancellationToken.None);

Assert.True(result.Valid);
Assert.False(result.Errors.Any());
Assert.False(result.Warnings.Any());
}

[Test]
[NGitLabRetry]
public async Task LintInvalidProjectCIYaml()
{
using var context = await GitLabTestContext.CreateAsync();
var project = context.CreateProject();
var lintClient = context.Client.Lint;

context.Client.GetRepository(project.Id).Files.Create(new FileUpsert
{
Branch = project.DefaultBranch,
CommitMessage = "test",
Path = ".gitlab-ci.yml",
Content = InvalidCIYaml,
});

var result = await context.Client.Lint.ValidateProjectCIConfigurationAsync(project.Id.ToString(), new(), CancellationToken.None);

Assert.False(result.Valid);
Assert.True(result.Errors.Any());
Assert.False(result.Warnings.Any());
}
}
}
3 changes: 3 additions & 0 deletions NGitLab/GitLabClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public class GitLabClient : IGitLabClient

public IGlobalJobClient Jobs { get; }

public ILintClient Lint { get; }

public RequestOptions Options
{
get => _api.RequestOptions;
Expand Down Expand Up @@ -88,6 +90,7 @@ private GitLabClient(GitLabCredentials credentials, RequestOptions options)
GraphQL = new GraphQLClient(_api);
AdvancedSearch = new SearchClient(_api, "/search");
Jobs = new GlobalJobsClient(_api);
Lint = new LintClient(_api);
}

[Obsolete("Use GitLabClient constructor instead")]
Expand Down
2 changes: 2 additions & 0 deletions NGitLab/IGitLabClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public interface IGitLabClient

IMergeRequestClient MergeRequests { get; }

ILintClient Lint { get; }

/// <summary>
/// All the user events of GitLab (can be scoped for the current user).
/// </summary>
Expand Down
16 changes: 16 additions & 0 deletions NGitLab/ILintClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Threading;
using System.Threading.Tasks;
using NGitLab.Models;

namespace NGitLab
{
/// <summary>
/// https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/lint.md
/// </summary>
public interface ILintClient
{
Task<LintCI> ValidateCIYamlContentAsync(string projectId, string yamlContent, LintCIOptions options, CancellationToken cancellationToken = default);

Task<LintCI> ValidateProjectCIConfigurationAsync(string projectId, LintCIOptions options, CancellationToken cancellationToken = default);
}
}
46 changes: 46 additions & 0 deletions NGitLab/Impl/LintClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using NGitLab.Models;

namespace NGitLab.Impl
{
public class LintClient : ILintClient
{
private readonly API _api;

public LintClient(API api)
{
_api = api;
}

public Task<LintCI> ValidateCIYamlContentAsync(string projectId, string yamlContent, LintCIOptions options, CancellationToken cancellationToken = default)
{
var url = BuildLintCIUrl(projectId, options);
var data = new
{
content = yamlContent,
};

return _api.Post().With(data).ToAsync<LintCI>(url, cancellationToken);
}

public Task<LintCI> ValidateProjectCIConfigurationAsync(string projectId, LintCIOptions options, CancellationToken cancellationToken = default)
{
var url = BuildLintCIUrl(projectId, options);

return _api.Get().ToAsync<LintCI>(url, cancellationToken);
}

private static string BuildLintCIUrl(string projectId, LintCIOptions options)
{
var url = Project.Url + "/" + WebUtility.UrlEncode(projectId) + LintCI.Url;

url = Utils.AddParameter(url, "dry_run", options.DryRun);
url = Utils.AddParameter(url, "ref", options.Ref);
url = Utils.AddParameter(url, "include_jobs", options.IncludeJobs);

return url;
}
}
}
21 changes: 21 additions & 0 deletions NGitLab/Models/LintCI.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Text.Json.Serialization;

namespace NGitLab.Models
{
public class LintCI
{
public const string Url = "/ci/lint";

[JsonPropertyName("valid")]
public bool Valid { get; set; }

[JsonPropertyName("merged_yaml")]
public string MergedYaml { get; set; }

[JsonPropertyName("errors")]
public string[] Errors { get; set; }

[JsonPropertyName("warnings")]
public string[] Warnings { get; set; }
}
}
20 changes: 20 additions & 0 deletions NGitLab/Models/LintCIOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace NGitLab.Models
{
public class LintCIOptions
{
/// <summary>
/// Run pipeline creation simulation, or only do static check.
/// </summary>
public bool? DryRun { get; set; }

/// <summary>
/// If the list of jobs that would exist in a static check or pipeline simulation should be included in the response.
/// </summary>
public bool? IncludeJobs { get; set; }

/// <summary>
/// When dry_run is true, sets the branch or tag to use.
/// </summary>
public string Ref { get; set; }
}
}
28 changes: 28 additions & 0 deletions NGitLab/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const NGitLab.Impl.NamespacesClient.Url = "/namespaces" -> string
const NGitLab.Models.Commit.Url = "/commits" -> string
const NGitLab.Models.Contributor.Url = "/contributors" -> string
const NGitLab.Models.Group.Url = "/groups" -> string
const NGitLab.Models.LintCI.Url = "/ci/lint" -> string
const NGitLab.Models.MergeRequest.Url = "/merge_requests" -> string
const NGitLab.Models.Pipeline.Url = "/pipelines" -> string
const NGitLab.Models.PipelineBasic.Url = "/pipelines" -> string
Expand Down Expand Up @@ -72,6 +73,7 @@ NGitLab.GitLabClient.Groups.get -> NGitLab.IGroupsClient
NGitLab.GitLabClient.Issues.get -> NGitLab.IIssueClient
NGitLab.GitLabClient.Jobs.get -> NGitLab.IGlobalJobClient
NGitLab.GitLabClient.Labels.get -> NGitLab.ILabelClient
NGitLab.GitLabClient.Lint.get -> NGitLab.ILintClient
NGitLab.GitLabClient.Members.get -> NGitLab.IMembersClient
NGitLab.GitLabClient.MergeRequests.get -> NGitLab.IMergeRequestClient
NGitLab.GitLabClient.Namespaces.get -> NGitLab.INamespacesClient
Expand Down Expand Up @@ -184,6 +186,7 @@ NGitLab.IGitLabClient.Groups.get -> NGitLab.IGroupsClient
NGitLab.IGitLabClient.Issues.get -> NGitLab.IIssueClient
NGitLab.IGitLabClient.Jobs.get -> NGitLab.IGlobalJobClient
NGitLab.IGitLabClient.Labels.get -> NGitLab.ILabelClient
NGitLab.IGitLabClient.Lint.get -> NGitLab.ILintClient
NGitLab.IGitLabClient.Members.get -> NGitLab.IMembersClient
NGitLab.IGitLabClient.MergeRequests.get -> NGitLab.IMergeRequestClient
NGitLab.IGitLabClient.Namespaces.get -> NGitLab.INamespacesClient
Expand Down Expand Up @@ -297,6 +300,9 @@ NGitLab.ILabelClient.ForProject(int projectId) -> System.Collections.Generic.IEn
NGitLab.ILabelClient.GetGroupLabel(int groupId, string name) -> NGitLab.Models.Label
NGitLab.ILabelClient.GetLabel(int projectId, string name) -> NGitLab.Models.Label
NGitLab.ILabelClient.GetProjectLabel(int projectId, string name) -> NGitLab.Models.Label
NGitLab.ILintClient
NGitLab.ILintClient.ValidateCIYamlContentAsync(string projectId, string yamlContent, NGitLab.Models.LintCIOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<NGitLab.Models.LintCI>
NGitLab.ILintClient.ValidateProjectCIConfigurationAsync(string projectId, NGitLab.Models.LintCIOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<NGitLab.Models.LintCI>
NGitLab.IMembersClient
NGitLab.IMembersClient.AddMemberToGroup(string groupId, NGitLab.Models.GroupMemberCreate user) -> NGitLab.Models.Membership
NGitLab.IMembersClient.AddMemberToProject(string projectId, NGitLab.Models.ProjectMemberCreate user) -> NGitLab.Models.Membership
Expand Down Expand Up @@ -546,6 +552,10 @@ NGitLab.Impl.LabelClient.GetGroupLabel(int groupId, string name) -> NGitLab.Mode
NGitLab.Impl.LabelClient.GetLabel(int projectId, string name) -> NGitLab.Models.Label
NGitLab.Impl.LabelClient.GetProjectLabel(int projectId, string name) -> NGitLab.Models.Label
NGitLab.Impl.LabelClient.LabelClient(NGitLab.Impl.API api) -> void
NGitLab.Impl.LintClient
NGitLab.Impl.LintClient.LintClient(NGitLab.Impl.API api) -> void
NGitLab.Impl.LintClient.ValidateCIYamlContentAsync(string projectId, string yamlContent, NGitLab.Models.LintCIOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<NGitLab.Models.LintCI>
NGitLab.Impl.LintClient.ValidateProjectCIConfigurationAsync(string projectId, NGitLab.Models.LintCIOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<NGitLab.Models.LintCI>
NGitLab.Impl.MembersClient
NGitLab.Impl.MembersClient.AddMemberToGroup(string groupId, NGitLab.Models.GroupMemberCreate user) -> NGitLab.Models.Membership
NGitLab.Impl.MembersClient.AddMemberToProject(string projectId, NGitLab.Models.ProjectMemberCreate user) -> NGitLab.Models.Membership
Expand Down Expand Up @@ -1999,6 +2009,24 @@ NGitLab.Models.LastActivityDate.LastActivityOn.get -> System.DateTimeOffset
NGitLab.Models.LastActivityDate.LastActivityOn.set -> void
NGitLab.Models.LastActivityDate.Username.get -> string
NGitLab.Models.LastActivityDate.Username.set -> void
NGitLab.Models.LintCI
NGitLab.Models.LintCI.Errors.get -> string[]
NGitLab.Models.LintCI.Errors.set -> void
NGitLab.Models.LintCI.LintCI() -> void
NGitLab.Models.LintCI.MergedYaml.get -> string
NGitLab.Models.LintCI.MergedYaml.set -> void
NGitLab.Models.LintCI.Valid.get -> bool
NGitLab.Models.LintCI.Valid.set -> void
NGitLab.Models.LintCI.Warnings.get -> string[]
NGitLab.Models.LintCI.Warnings.set -> void
NGitLab.Models.LintCIOptions
NGitLab.Models.LintCIOptions.DryRun.get -> bool?
NGitLab.Models.LintCIOptions.DryRun.set -> void
NGitLab.Models.LintCIOptions.IncludeJobs.get -> bool?
NGitLab.Models.LintCIOptions.IncludeJobs.set -> void
NGitLab.Models.LintCIOptions.LintCIOptions() -> void
NGitLab.Models.LintCIOptions.Ref.get -> string
NGitLab.Models.LintCIOptions.Ref.set -> void
NGitLab.Models.Membership
NGitLab.Models.Membership.AccessLevel -> int
NGitLab.Models.Membership.AvatarURL -> string
Expand Down

0 comments on commit 5ac8552

Please sign in to comment.