diff --git a/src/GitLabApiClient/GroupsClient.cs b/src/GitLabApiClient/GroupsClient.cs index 3f157078..c993474d 100644 --- a/src/GitLabApiClient/GroupsClient.cs +++ b/src/GitLabApiClient/GroupsClient.cs @@ -336,5 +336,44 @@ public async Task UpdateLabelAsync(GroupId groupId, UpdateGroupLabel /// Name of the label. public async Task DeleteLabelAsync(GroupId groupId, string name) => await _httpFacade.Delete($"groups/{groupId}/labels?name={name}"); + + /// + /// Retrieves group variables by its id. + /// + /// Id of the group. + public async Task> GetVariablesAsync(GroupId groupId) => + await _httpFacade.GetPagedList($"groups/{groupId}/variables"); + + /// + /// Creates new project variable. + /// + /// The ID, path or of the group. + /// Create variable request. + /// Newly created variable. + public async Task CreateVariableAsync(GroupId groupId, CreateGroupVariableRequest request) + { + Guard.NotNull(request, nameof(request)); + return await _httpFacade.Post($"groups/{groupId}/variables", request); + } + + /// + /// Updates an existing group variable. + /// + /// The ID, path or of the group. + /// Update variable request. + /// Newly modified variable. + public async Task UpdateVariableAsync(GroupId groupId, UpdateGroupVariableRequest request) + { + Guard.NotNull(request, nameof(request)); + return await _httpFacade.Put($"groups/{groupId}/variables/{request.Key}", request); + } + + /// + /// Deletes group variable + /// + /// The ID, path or of the group. + /// The Key ID of the variable. + public async Task DeleteVariableAsync(GroupId groupId, string key) => + await _httpFacade.Delete($"groups/{groupId}/variables/{key}"); } } diff --git a/src/GitLabApiClient/Models/Groups/Requests/CreateGroupVariableRequest.cs b/src/GitLabApiClient/Models/Groups/Requests/CreateGroupVariableRequest.cs new file mode 100644 index 00000000..922bb3b4 --- /dev/null +++ b/src/GitLabApiClient/Models/Groups/Requests/CreateGroupVariableRequest.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json; + +namespace GitLabApiClient.Models.Groups.Requests +{ + public class CreateGroupVariableRequest + { + /// + /// The type of a variable. + /// Available types are: env_var (default) and file + /// + [JsonProperty("variable_type")] + public string VariableType { get; set; } + + /// + /// The key of a variable + /// + [JsonProperty("key")] + public string Key { get; set; } + + /// + /// The value of a variable + /// + [JsonProperty("value")] + public string Value { get; set; } + + /// + /// Whether the variable is protected + /// + [JsonProperty("protected")] + public bool? Protected { get; set; } + + /// + /// Whether the variable is masked + /// + [JsonProperty("masked")] + public bool? Masked { get; set; } + } +} diff --git a/src/GitLabApiClient/Models/Groups/Requests/UpdateGroupVariableRequest.cs b/src/GitLabApiClient/Models/Groups/Requests/UpdateGroupVariableRequest.cs new file mode 100644 index 00000000..3f7d25c6 --- /dev/null +++ b/src/GitLabApiClient/Models/Groups/Requests/UpdateGroupVariableRequest.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json; + +namespace GitLabApiClient.Models.Groups.Requests +{ + public class UpdateGroupVariableRequest + { + /// + /// The type of a variable. + /// Available types are: env_var (default) and file + /// + [JsonProperty("variable_type")] + public string VariableType { get; set; } + + /// + /// The key of a variable + /// + [JsonProperty("key")] + public string Key { get; set; } + + /// + /// The value of a variable + /// + [JsonProperty("value")] + public string Value { get; set; } + + /// + /// Whether the variable is protected + /// + [JsonProperty("protected")] + public bool? Protected { get; set; } + + /// + /// Whether the variable is masked + /// + [JsonProperty("masked")] + public bool? Masked { get; set; } + } +} diff --git a/src/GitLabApiClient/Models/Groups/Responses/Variable.cs b/src/GitLabApiClient/Models/Groups/Responses/Variable.cs new file mode 100644 index 00000000..cb04ceb3 --- /dev/null +++ b/src/GitLabApiClient/Models/Groups/Responses/Variable.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json; + +namespace GitLabApiClient.Models.Groups.Responses +{ + public sealed class Variable + { + /// + /// The type of a variable. + /// Available types are: env_var (default) and file + /// + [JsonProperty("variable_type")] + public string VariableType { get; set; } + + /// + /// The key of a variable + /// + [JsonProperty("key")] + public string Key { get; set; } + + /// + /// The value of a variable + /// + [JsonProperty("value")] + public string Value { get; set; } + + /// + /// Whether the variable is protected + /// + [JsonProperty("protected")] + public bool Protected { get; set; } + + /// + /// Whether the variable is masked + /// + [JsonProperty("masked")] + public bool Masked { get; set; } + } +} diff --git a/src/GitLabApiClient/ProjectsClient.cs b/src/GitLabApiClient/ProjectsClient.cs index 9aec7554..ea0146af 100644 --- a/src/GitLabApiClient/ProjectsClient.cs +++ b/src/GitLabApiClient/ProjectsClient.cs @@ -71,7 +71,7 @@ public async Task> GetUsersAsync(ProjectId projectId) => /// Retrieves project variables by its id. /// /// Id of the project. - public async Task> GetVariablesAsync(int projectId) => + public async Task> GetVariablesAsync(ProjectId projectId) => await _httpFacade.GetPagedList($"projects/{projectId}/variables"); /// diff --git a/test/GitLabApiClient.Test/GitLabClientTest.cs b/test/GitLabApiClient.Test/GitLabClientTest.cs index 928ec8fa..8d7039ad 100644 --- a/test/GitLabApiClient.Test/GitLabClientTest.cs +++ b/test/GitLabApiClient.Test/GitLabClientTest.cs @@ -66,7 +66,7 @@ public async void CanLogin() accessTokenResponse.CreatedAt.Should().NotBeNull(); accessTokenResponse.AccessToken.Should().HaveLength(64); accessTokenResponse.RefreshToken.Should().HaveLength(64); - accessTokenResponse.TokenType.Should().Be("bearer"); + accessTokenResponse.TokenType.Should().BeEquivalentTo("bearer"); var currentSessionAsync = await sut.Users.GetCurrentSessionAsync(); currentSessionAsync.Username.Should().Be(GitLabApiHelper.TestUserName); diff --git a/test/GitLabApiClient.Test/GroupsClientTest.cs b/test/GitLabApiClient.Test/GroupsClientTest.cs index 140a515e..12812e61 100644 --- a/test/GitLabApiClient.Test/GroupsClientTest.cs +++ b/test/GitLabApiClient.Test/GroupsClientTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; using FluentAssertions; using GitLabApiClient.Internal.Queries; @@ -19,6 +20,7 @@ public class GroupsClientTest { private readonly List _groupIdsToClean = new List(); private List MilestoneIdsToClean { get; } = new List(); + private List VariableIdsToClean { get; } = new List(); private readonly GroupsClient _sut = new GroupsClient( GetFacade(), @@ -229,6 +231,92 @@ public async Task CreatedGroupMilestoneCanBeClosed() updatedMilestone.Should().Match(i => i.State == MilestoneState.Closed); } + [Fact] + public async Task GroupVariablesRetrieved() + { + //arrange + var createdVariable = await _sut.CreateVariableAsync(GitLabApiHelper.TestGroupId, new CreateGroupVariableRequest + { + VariableType = "env_var", + Key = "SOME_VAR_KEY_RETRIEVE", + Value = "VALUE_VAR", + Masked = true, + Protected = true + }); + + VariableIdsToClean.Add(createdVariable.Key); + + //act + var variables = await _sut.GetVariablesAsync(GitLabApiHelper.TestGroupId); + var variable = variables.First(v => v.Key == createdVariable.Key); + + //assert + variables.Should().NotBeEmpty(); + variable.Should().Match(v => + v.VariableType == createdVariable.VariableType && + v.Key == createdVariable.Key && + v.Value == createdVariable.Value && + v.Masked == createdVariable.Masked && + v.Protected == createdVariable.Protected); + } + + [Fact] + public async Task GroupVariablesCreated() + { + var request = new CreateGroupVariableRequest + { + VariableType = "env_var", + Key = "SOME_VAR_KEY_CREATED", + Value = "VALUE_VAR", + Masked = true, + Protected = true + }; + + var variable = await _sut.CreateVariableAsync(GitLabApiHelper.TestGroupId, request); + + variable.Should().Match(v => v.VariableType == request.VariableType + && v.Key == request.Key + && v.Value == request.Value + && v.Masked == request.Masked + && v.Protected == request.Protected); + + VariableIdsToClean.Add(request.Key); + } + + [Fact] + public async Task GroupVariableCanBeUpdated() + { + var request = new CreateGroupVariableRequest + { + VariableType = "env_var", + Key = "SOME_VAR_KEY_TO_UPDATE", + Value = "VALUE_VAR", + Masked = true, + Protected = true + }; + + var variable = await _sut.CreateVariableAsync(GitLabApiHelper.TestGroupId, request); + + VariableIdsToClean.Add(request.Key); + + var updateRequest = new UpdateGroupVariableRequest + { + VariableType = "file", + Key = request.Key, + Value = "UpdatedValue", + Masked = request.Masked, + Protected = request.Protected, + }; + + var variableUpdated = await _sut.UpdateVariableAsync(GitLabApiHelper.TestGroupId, updateRequest); + + variableUpdated.Should().Match(v => v.VariableType == updateRequest.VariableType + && v.Key == updateRequest.Key + && v.Value == updateRequest.Value + && v.Masked == updateRequest.Masked + && v.Protected == updateRequest.Protected); + } + [Fact] public Task InitializeAsync() => CleanupGroups(); @@ -244,6 +332,9 @@ private async Task CleanupGroups() foreach (int groupId in _groupIdsToClean) await _sut.DeleteAsync(groupId.ToString()); + + foreach (string variableId in VariableIdsToClean) + await _sut.DeleteVariableAsync(GitLabApiHelper.TestGroupId, variableId); } private static string GetRandomGroupName() diff --git a/test/GitLabApiClient.Test/Utilities/GitLabContainerFixture.cs b/test/GitLabApiClient.Test/Utilities/GitLabContainerFixture.cs index f659f74c..d4d720b5 100755 --- a/test/GitLabApiClient.Test/Utilities/GitLabContainerFixture.cs +++ b/test/GitLabApiClient.Test/Utilities/GitLabContainerFixture.cs @@ -21,7 +21,7 @@ public class GitLabContainerFixture : IAsyncLifetime public static string GitlabHost { get; private set; } private IContainerService _gitlabContainer; - private readonly string _gitlabDockerImage = "gitlab/gitlab-ce:12.5.4-ce.0"; + private readonly string _gitlabDockerImage = "gitlab/gitlab-ce:12.10.2-ce.0"; public async Task InitializeAsync() {