Skip to content

Commit

Permalink
https://github.com/JetBrains/YouTrackSharp/issues/40
Browse files Browse the repository at this point in the history
  • Loading branch information
aschoelzhorn committed Nov 5, 2017
1 parent 711e181 commit c113e41
Show file tree
Hide file tree
Showing 9 changed files with 442 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);
}
}
}
56 changes: 56 additions & 0 deletions src/YouTrackSharp/Projects/CustomField.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System;
using System.Net.Http;
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; }

// param Additional parameter of custom field. For example, a bundle that is attached with custom field.
}
}
150 changes: 150 additions & 0 deletions src/YouTrackSharp/Projects/ProjectCustomFieldsService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
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));
}

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 a 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)
{
var queryString = new Dictionary<string, string>();
if (string.IsNullOrEmpty(customField?.Name))
{
throw new ArgumentNullException(nameof(customField));
}

if (!string.IsNullOrEmpty(customField.EmptyText))
{
queryString.Add("emptyFieldText", WebUtility.UrlEncode(customField.EmptyText));
}

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

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)
{
var queryString = new Dictionary<string, string>();
if (string.IsNullOrEmpty(customField?.Name))
{
throw new ArgumentNullException(nameof(customField));
}

if (!string.IsNullOrEmpty(customField.EmptyText))
{
queryString.Add("emptyFieldText", WebUtility.UrlEncode(customField.EmptyText));
}

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

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,47 @@
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.Demo1Token;
var service = connection.ProjectCustomFieldsService();

var customField = new CustomField() {Name = "TestField", EmptyText = "empty"};

// Act
await service.CreateProjectCustomField("DP1", customField);

//var created = await service.GetProjectCustomField("DP1", customField.Name);

//// Assert
//Assert.NotNull(created);

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

[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"));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
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 GetProjectCustomField
{
[Fact]
public async Task Valid_Connection_Gets_CustomFields_For_Project()
{
// Arrange
var connection = Connections.Demo1Token;
var service = connection.ProjectCustomFieldsService();

// Act
var result = await service.GetProjectCustomField("DP1", "Assignee");

// Assert
Assert.NotNull(result);

Assert.Equal("Assignee", result.Name);
Assert.Equal(string.Empty, result.Url);
Assert.Equal("user[1]", result.Type);
Assert.True(result.CanBeEmpty);
Assert.Equal("Unassigned", result.EmptyText);
}

[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", "Assignee"));
}
}
}
}
Loading

0 comments on commit c113e41

Please sign in to comment.