Skip to content

Commit

Permalink
#40 (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
aschoelzhorn authored and maartenba committed Nov 15, 2017
1 parent 711e181 commit 6a7fc09
Show file tree
Hide file tree
Showing 9 changed files with 475 additions and 1 deletion.
10 changes: 10 additions & 0 deletions src/YouTrackSharp/ConnectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,15 @@ public static TimeTrackingManagementService CreateTimeTrackingManagementService(
{
return new TimeTrackingManagementService(connection);
}

/// <summary>
/// Creates a <see cref="ProjectCustomFieldsService" />.
/// </summary>
/// <param name="connection">The <see cref="Connection" /> to create a service with.</param>
/// <returns><see cref="ProjectCustomFieldsService" /> for accessing custom project fields.</returns>
public static ProjectCustomFieldsService ProjectCustomFieldsService(this Connection connection)
{
return new ProjectCustomFieldsService(connection);
}
}
}
52 changes: 52 additions & 0 deletions src/YouTrackSharp/Projects/CustomField.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Newtonsoft.Json;

namespace YouTrackSharp.Projects
{
/// <summary>
/// Custom field for a project
/// </summary>
public class CustomField
{
/// <summary>
/// Creates an instance of the <see cref="CustomField" /> class.
/// </summary>
public CustomField()
{
Name = string.Empty;
Url = string.Empty;
Type = string.Empty;
CanBeEmpty = false;
EmptyText = string.Empty;
}

/// <summary>
/// Name of project custom field.
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }

/// <summary>
/// The Url of the custom field.
/// </summary>
[JsonProperty("url")]
public string Url { get; set; }

/// <summary>
/// Type of this custom field.
/// </summary>
[JsonProperty("type")]
public string Type { get; set; }

/// <summary>
/// Mandatory binary parameter defining if the field can have empty value or not.
/// </summary>
[JsonProperty("canBeEmpty")]
public bool CanBeEmpty { get; set; }

/// <summary>
/// Text that is shown when the custom field has no value.
/// </summary>
[JsonProperty("emptyText")]
public string EmptyText { get; set; }
}
}
159 changes: 159 additions & 0 deletions src/YouTrackSharp/Projects/ProjectCustomFieldsService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using YouTrackSharp.Management;

namespace YouTrackSharp.Projects
{
/// <summary>
/// A class that represents a REST API client for <a href="https://www.jetbrains.com/help/youtrack/standalone/Project-Custom-Fields.html"> methods related to operations with custom fields of a project</a>.
/// It uses a <see cref="Connection" /> implementation to connect to the remote YouTrack server instance.
/// </summary>
public class ProjectCustomFieldsService
{
private readonly Connection _connection;

/// <summary>
/// Creates an instance of the <see cref="ProjectCustomFieldsService" /> class.
/// </summary>
/// <param name="connection">A <see cref="Connection" /> instance that provides a connection to the remote YouTrack server instance.</param>
public ProjectCustomFieldsService(Connection connection)
{
_connection = connection ?? throw new ArgumentNullException(nameof(connection));
}

/// <summary>
/// Get custom fields used in a project.
/// </summary>
/// <remarks>Uses the REST API <a href="https://www.jetbrains.com/help/youtrack/standalone/GET-Project-Custom-Fields.html">Get Project Custom Fields</a>.</remarks>
/// <param name="projectId">Id of the project to get the custom fields for.</param>
/// <returns>A <see cref="T:System.Collections.Generic.ICollection`1" /> of <see cref="CustomField" /> that are accessible for currently logged in user.</returns>
/// <exception cref="T:System.Net.HttpRequestException">When the call to the remote YouTrack server instance failed.</exception>
public async Task<ICollection<CustomField>> GetProjectCustomFields(string projectId)
{
var client = await _connection.GetAuthenticatedHttpClient();
var response = await client.GetAsync($"rest/admin/project/{projectId}/customfield");

response.EnsureSuccessStatusCode();

return JsonConvert.DeserializeObject<ICollection<CustomField>>(await response.Content.ReadAsStringAsync());
}

/// <summary>
/// Get a project's custom field by its name.
/// </summary>
/// <remarks>Uses the REST API <a href="https://www.jetbrains.com/help/youtrack/standalone/GET-Project-Custom-Field.html">Get Project Custom Field</a>.</remarks>
/// <param name="projectId">Id of the project to get the custom field for.</param>
/// <param name="customFieldName">Name of the custom field to get.</param>
/// <returns><see cref="CustomField" />.</returns>
/// <exception cref="T:System.Net.HttpRequestException">When the call to the remote YouTrack server instance failed.</exception>
public async Task<CustomField> GetProjectCustomField(string projectId, string customFieldName)
{
var client = await _connection.GetAuthenticatedHttpClient();
var response = await client.GetAsync($"rest/admin/project/{projectId}/customfield/{customFieldName}");

response.EnsureSuccessStatusCode();

return JsonConvert.DeserializeObject<CustomField>(await response.Content.ReadAsStringAsync());
}

/// <summary>
/// Remove specified custom field from a project.
/// </summary>
/// <remarks>Uses the REST API <a href="https://www.jetbrains.com/help/youtrack/standalone/DELETE-Project-Custom-Field.html">Delete a project custom field</a>.</remarks>
/// <param name="projectId">Id of the project to delete the custom field for.</param>
/// <param name="customFieldName">Name of the custom field to delete.</param>
/// <exception cref="T:System.ArgumentNullException">When the <paramref name="projectId"/> is null or empty.</exception>
/// <exception cref="T:System.ArgumentNullException">When the <paramref name="customFieldName"/> is null or empty.</exception>
/// <exception cref="T:System.Net.HttpRequestException">When the call to the remote YouTrack server instance failed.</exception>
public async Task DeleteProjectCustomField(string projectId, string customFieldName)
{
if (string.IsNullOrEmpty(projectId))
{
throw new ArgumentNullException(nameof(projectId));
}
if (string.IsNullOrEmpty(customFieldName))
{
throw new ArgumentNullException(nameof(customFieldName));
}

var client = await _connection.GetAuthenticatedHttpClient();
var response = await client.DeleteAsync($"rest/admin/project/{projectId}/customfield/{customFieldName}");

if (response.StatusCode == HttpStatusCode.NotFound)
{
return;
}

response.EnsureSuccessStatusCode();
}

/// <summary>
/// Adds an existing custom field to a specific project.
/// </summary>
/// <remarks>Uses the REST API <a href="https://www.jetbrains.com/help/youtrack/standalone/PUT-Project-Custom-Field.html">Create a project custom field</a>.</remarks>
/// <param name="projectId">Id of the project to add a custom field.</param>
/// <param name="customField"><see cref="CustomField" /> to add to the project.</param>
/// <exception cref="T:System.ArgumentNullException">When the <paramref name="projectId"/> is null or empty.</exception>
/// <exception cref="T:System.ArgumentNullException">When the <paramref name="customField"/> is null or empty.</exception>
/// <exception cref="T:YouTrackErrorException">When the call to the remote YouTrack server instance failed and YouTrack reported an error message.</exception>
/// <exception cref="T:System.Net.HttpRequestException">When the call to the remote YouTrack server instance failed.</exception>
public async Task CreateProjectCustomField(string projectId, CustomField customField)
{
if (string.IsNullOrEmpty(customField?.Name))
{
throw new ArgumentNullException(nameof(customField));
}

string query = string.Empty;
if (!string.IsNullOrEmpty(customField.EmptyText))
{
query = $"?emptyFieldText={WebUtility.UrlEncode(customField.EmptyText)}";
}

var client = await _connection.GetAuthenticatedHttpClient();
var response = await client.PutAsync($"rest/admin/project/{projectId}/customfield/{customField.Name}{query}", new MultipartContent());

response.EnsureSuccessStatusCode();
}

/// <summary>
/// Updates a custom field to a specific project.
/// </summary>
/// <remarks>Uses the REST API <a href="https://www.jetbrains.com/help/youtrack/standalone/POST-Project-Custom-Field.html">Updates a project custom field</a>.</remarks>
/// <param name="projectId">Id of the project.</param>
/// <param name="customField"><see cref="CustomField" /> to update in project.</param>
/// <exception cref="T:System.ArgumentNullException">When the <paramref name="projectId"/> is null or empty.</exception>
/// <exception cref="T:System.ArgumentNullException">When the <paramref name="customField"/> is null or empty.</exception>
/// <exception cref="T:YouTrackErrorException">When the call to the remote YouTrack server instance failed and YouTrack reported an error message.</exception>
/// <exception cref="T:System.Net.HttpRequestException">When the call to the remote YouTrack server instance failed.</exception>
public async Task UpdateProjectCustomField(string projectId, CustomField customField)
{
if (string.IsNullOrEmpty(projectId))
{
throw new ArgumentNullException(nameof(projectId));
}

if (string.IsNullOrEmpty(customField?.Name))
{
throw new ArgumentNullException(nameof(customField));
}

string query = string.Empty;
if (!string.IsNullOrEmpty(customField.EmptyText))
{
query = $"?emptyFieldText={WebUtility.UrlEncode(customField.EmptyText)}";
}

var client = await _connection.GetAuthenticatedHttpClient();
var response = await client.PostAsync($"rest/admin/project/{projectId}/customfield/{customField.Name}{query}", new MultipartContent());

response.EnsureSuccessStatusCode();
}
}
}
2 changes: 1 addition & 1 deletion src/YouTrackSharp/TimeTracking/TimeTrackingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public TimeTrackingService(Connection connection)
/// Get work types for a specific project from the server.
/// </summary>
/// <remarks>Uses the REST API <a href="https://www.jetbrains.com/help/youtrack/standalone/GET-Work-Types-for-a-Project.html">GET Work Types for a Project</a>.</remarks>
/// <param name="projectId">Id of the issue to get work items for.</param>
/// <param name="projectId">Id of the project to get work items for.</param>
/// <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1" /> of <see cref="WorkType" /> for the requested project <paramref name="projectId"/>.</returns>
/// <exception cref="T:System.ArgumentNullException">When the <paramref name="projectId"/> is null or empty.</exception>
/// <exception cref="T:System.Net.HttpRequestException">When the call to the remote YouTrack server instance failed.</exception>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using YouTrackSharp.Projects;
using YouTrackSharp.Tests.Infrastructure;

namespace YouTrackSharp.Tests.Integration.Projects
{
public partial class ProjectCustomFieldsServiceTests
{
public class CreateProjectCustomField
{
[Fact]
public async Task Valid_Connection_Creates_CustomField_For_Project()
{
// Arrange
var connection = Connections.Demo1Password;
var service = connection.ProjectCustomFieldsService();
var customField = new CustomField {Name = "TestField"};
var projectId = "DP1";

// Act
await service.CreateProjectCustomField(projectId, customField);

var created = await service.GetProjectCustomField(projectId, customField.Name);

// Assert
Assert.NotNull(created);

Assert.Equal(customField.Name, created.Name);
Assert.Equal(customField.EmptyText, string.Empty);

// cleanup
await service.DeleteProjectCustomField(projectId, customField.Name);
}

[Fact]
public async Task Valid_Connection_Creates_CustomField_With_EmptyText_For_Project()
{
// Arrange
var connection = Connections.Demo1Password;
var service = connection.ProjectCustomFieldsService();
var customField = new CustomField { Name = "TestField", EmptyText = "empty" };
var projectId = "DP1";

// Act
await service.CreateProjectCustomField(projectId, customField);

var created = await service.GetProjectCustomField(projectId, customField.Name);

// Assert
Assert.NotNull(created);

Assert.Equal(customField.Name, created.Name);
Assert.Equal(customField.EmptyText, created.EmptyText);

// cleanup
await service.DeleteProjectCustomField(projectId, customField.Name);
}

[Fact]
public async Task Invalid_Connection_Throws_UnauthorizedConnectionException()
{
// Arrange
var service = Connections.UnauthorizedConnection.ProjectCustomFieldsService();

// Act & Assert
await Assert.ThrowsAsync<UnauthorizedConnectionException>(
async () => await service.GetProjectCustomField("DP1", "TestField"));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using YouTrackSharp.Tests.Infrastructure;

namespace YouTrackSharp.Tests.Integration.Projects
{
public partial class ProjectCustomFieldsServiceTests
{
public class DeleteProjectCustomField
{
[Fact]
public async Task Valid_Connection_Deletes_CustomField_For_Project()
{
// Arrange
var connection = Connections.Demo1Token;
var service = connection.ProjectCustomFieldsService();

// Act & Assert
var acted = false;
await service.DeleteProjectCustomField("DP1", " TestField");
acted = true;

Assert.True(acted);
}

[Fact]
public async Task Invalid_Connection_Throws_UnauthorizedConnectionException()
{
// Arrange
var service = Connections.UnauthorizedConnection.ProjectCustomFieldsService();

// Act & Assert
await Assert.ThrowsAsync<UnauthorizedConnectionException>(
async () => await service.DeleteProjectCustomField("DP1", "TestField"));
}
}
}
}
Loading

0 comments on commit 6a7fc09

Please sign in to comment.