diff --git a/README.md b/README.md index 68fb734b..c54f13d0 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,10 @@ The following API's are currently supported: * [Projects-related methods](https://www.jetbrains.com/help/youtrack/standalone/Projects-Related-Methods.html) through `ProjectsService` * [Issues-related methods](https://www.jetbrains.com/help/youtrack/standalone/Issues-Related-Methods.html) through `IssuesService` * [Time-tracking-related methods](https://www.jetbrains.com/help/youtrack/standalone/Time-Tracking-User-Methods.html) through `TimeTrackingService` - +* Administration API's + * [User management](https://www.jetbrains.com/help/youtrack/standalone/Users.html) through `UserManagementService` + * [Time Tracker management](https://www.jetbrains.com/help/youtrack/standalone/Time-Tracking-Settings-Methods.html) through `TimeTrackingManagementService` + Many other API's are not included yet - feel free to [tackle one of the `UpForGrabs` issues](https://github.com/JetBrains/YouTrackSharp/issues?q=is%3Aissue+is%3Aopen+label%3AUpForGrabs) and make YouTrackSharp better! diff --git a/build/YouTrackSharp.Build.csproj.dotsettings b/build/YouTrackSharp.Build.csproj.dotsettings index 2ed93010..d62738ec 100644 --- a/build/YouTrackSharp.Build.csproj.dotsettings +++ b/build/YouTrackSharp.Build.csproj.dotsettings @@ -6,6 +6,7 @@ True <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True True True True \ No newline at end of file diff --git a/global.json b/global.json index f992be3d..21340d61 100644 --- a/global.json +++ b/global.json @@ -1,5 +1 @@ -{ - "sdk": { - "version": "1.0.4" - } -} \ No newline at end of file +{"sdk":{"version":"1.1.0"}} \ No newline at end of file diff --git a/src/YouTrackSharp/ConnectionExtensions.cs b/src/YouTrackSharp/ConnectionExtensions.cs index 8a450c0a..6958afeb 100644 --- a/src/YouTrackSharp/ConnectionExtensions.cs +++ b/src/YouTrackSharp/ConnectionExtensions.cs @@ -1,11 +1,12 @@ using YouTrackSharp.Issues; +using YouTrackSharp.Management; using YouTrackSharp.Projects; using YouTrackSharp.TimeTracking; namespace YouTrackSharp { /// - /// Extension methods for , providing easy access to all services. + /// Extension methods for , providing easy access to all services. /// public static class ConnectionExtensions { @@ -33,10 +34,30 @@ public static IssuesService CreateIssuesService(this Connection connection) /// Creates a . /// /// The to create a service with. - /// for working with YouTrack issues. + /// for working with YouTrack time tracking. public static TimeTrackingService CreateTimeTrackingService(this Connection connection) { return new TimeTrackingService(connection); } + + /// + /// Creates a . + /// + /// The to create a service with. + /// for managing YouTrack users. + public static UserManagementService CreateUserManagementService(this Connection connection) + { + return new UserManagementService(connection); + } + + /// + /// Creates a . + /// + /// The to create a service with. + /// for managing YouTrack time tracking settings. + public static TimeTrackingManagementService CreateTimeTrackingManagementService(this Connection connection) + { + return new TimeTrackingManagementService(connection); + } } } \ No newline at end of file diff --git a/src/YouTrackSharp/Management/Group.cs b/src/YouTrackSharp/Management/Group.cs new file mode 100644 index 00000000..1894ebdb --- /dev/null +++ b/src/YouTrackSharp/Management/Group.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; + +namespace YouTrackSharp.Management +{ + /// + /// A class that represents YouTrack group information. + /// + public class Group + { + /// + /// Id of the group. + /// + [JsonProperty("entityId")] + public string Id { get; set; } + + /// + /// Name of the group. + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// URL of the group. + /// + [JsonProperty("url")] + public string Url { get; set; } + } +} \ No newline at end of file diff --git a/src/YouTrackSharp/Management/SystemWideTimeTrackingSettings.cs b/src/YouTrackSharp/Management/SystemWideTimeTrackingSettings.cs new file mode 100644 index 00000000..c071ed11 --- /dev/null +++ b/src/YouTrackSharp/Management/SystemWideTimeTrackingSettings.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace YouTrackSharp.Management +{ + /// + /// A class that represents YouTrack system wide time settings information. + /// + public class SystemWideTimeTrackingSettings + { + /// + /// Creates an instance of the class. + /// + public SystemWideTimeTrackingSettings() + { + WorkDays = new List>(); + } + + /// + /// Hours A Day. + /// + [JsonProperty("hoursADay")] + public int HoursADay { get; set; } + + /// + /// Days A Week. + /// + [JsonProperty("daysAWeek")] + public int DaysAWeek { get; set; } + + /// + /// WorkDays A Week. + /// + [JsonProperty("workWeek")] + public ICollection> WorkDays { get; set; } + } +} \ No newline at end of file diff --git a/src/YouTrackSharp/Management/TimeTrackingManagementService.cs b/src/YouTrackSharp/Management/TimeTrackingManagementService.cs new file mode 100644 index 00000000..0d364214 --- /dev/null +++ b/src/YouTrackSharp/Management/TimeTrackingManagementService.cs @@ -0,0 +1,153 @@ +using System; +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.TimeTracking; + +namespace YouTrackSharp.Management +{ + /// + /// A class that represents a REST API client for administering Time Tracking Settings in YouTrack. + /// It uses a implementation to connect to the remote YouTrack server instance. + /// + public class TimeTrackingManagementService + { + private readonly Connection _connection; + + /// + /// Creates an instance of the class. + /// + /// A instance that provides a connection to the remote YouTrack server instance. + public TimeTrackingManagementService(Connection connection) + { + _connection = connection ?? throw new ArgumentNullException(nameof(connection)); + } + + /// + /// Get the current system-wide time tracking settings. + /// + /// Uses the REST API Get System-wide Time Tracking Settings. + /// System-wide . + /// When the call to the remote YouTrack server instance failed. + public async Task GetSystemWideTimeTrackingSettings() + { + var client = await _connection.GetAuthenticatedHttpClient(); + var response = await client.GetAsync($"rest/admin/timetracking"); + + response.EnsureSuccessStatusCode(); + + return JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); + } + + + /// + /// Updates the system-wide time tracking settings. + /// + /// Uses the REST API Set system-wide time tracking settings: a list of working days in a week, and a number of hours in a working day. + /// Parameter daysAWeek is ignored since Youtrack 5.1 + /// When the call to the remote YouTrack server instance failed and YouTrack reported an error message. + /// When the call to the remote YouTrack server instance failed. + public async Task UpdateSystemWideTimeTrackingSettings(SystemWideTimeTrackingSettings timeSettings) + { + if (timeSettings == null) + { + throw new ArgumentNullException(nameof(timeSettings)); + } + + var stringContent = new StringContent(JsonConvert.SerializeObject(timeSettings)); + stringContent.Headers.ContentType = new MediaTypeHeaderValue(Constants.HttpContentTypes.ApplicationJson); + + var client = await _connection.GetAuthenticatedHttpClient(); + var response = await client.PutAsync($"rest/admin/timetracking", stringContent); + + if (response.StatusCode == HttpStatusCode.BadRequest) + { + // Try reading the error message + var responseJson = JObject.Parse(await response.Content.ReadAsStringAsync()); + if (responseJson["value"] != null) + { + throw new YouTrackErrorException(responseJson["value"].Value()); + } + else + { + throw new YouTrackErrorException(Strings.Exception_UnknownError); + } + } + + response.EnsureSuccessStatusCode(); + } + + + /// + /// Get the current time tracking settings for a specific project. + /// + /// Uses the REST API Get Time Tracking Settings for a Project. + /// Id of the project to get timetracking settings for. + /// . + /// When the is null or empty. + /// When the call to the remote YouTrack server instance failed. + public async Task GetTimeTrackingSettingsForProject(string projectId) + { + if (string.IsNullOrEmpty(projectId)) + { + throw new ArgumentNullException(nameof(projectId)); + } + + var client = await _connection.GetAuthenticatedHttpClient(); + var response = await client.GetAsync($"rest/admin/project/{projectId}/timetracking"); + + response.EnsureSuccessStatusCode(); + + return JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); + } + + + /// + /// Updates the current time tracking settings for a specific project. + /// + /// Uses the REST API Configure time tracking settings for a specific project. + /// Id of the project to update. + /// Timetracking settings for this project. + /// When the is null or empty. + /// When the is null. + /// When the call to the remote YouTrack server instance failed and YouTrack reported an error message. + /// When the call to the remote YouTrack server instance failed. + public async Task UpdateTimeTrackingSettingsForProject(string projectId, TimeTrackingSettings timeTrackingSettings) + { + if (string.IsNullOrEmpty(projectId)) + { + throw new ArgumentNullException(nameof(projectId)); + } + + if (timeTrackingSettings == null) + { + throw new ArgumentNullException(nameof(timeTrackingSettings)); + } + + var stringContent = new StringContent(JsonConvert.SerializeObject(timeTrackingSettings)); + stringContent.Headers.ContentType = new MediaTypeHeaderValue(Constants.HttpContentTypes.ApplicationJson); + + var client = await _connection.GetAuthenticatedHttpClient(); + var response = await client.PutAsync($"rest/admin/project/{projectId}/timetracking", stringContent); + + if (response.StatusCode == HttpStatusCode.BadRequest) + { + // Try reading the error message + var responseJson = JObject.Parse(await response.Content.ReadAsStringAsync()); + if (responseJson["value"] != null) + { + throw new YouTrackErrorException(responseJson["value"].Value()); + } + else + { + throw new YouTrackErrorException(Strings.Exception_UnknownError); + } + } + + response.EnsureSuccessStatusCode(); + } + } +} \ No newline at end of file diff --git a/src/YouTrackSharp/Management/TimeTrackingSettings.cs b/src/YouTrackSharp/Management/TimeTrackingSettings.cs new file mode 100644 index 00000000..8245b582 --- /dev/null +++ b/src/YouTrackSharp/Management/TimeTrackingSettings.cs @@ -0,0 +1,43 @@ +using Newtonsoft.Json; + +namespace YouTrackSharp.Management +{ + /// + /// A class that represents YouTrack timetracking settings information. + /// + public class TimeTrackingSettings + { + /// + /// Is time tracking enabled? + /// + [JsonProperty("enabled")] + public bool Enabled { get; set; } + + /// + /// Field that contains Estimation data. + /// + [JsonProperty("estimation")] + public TimeField Estimation { get; set; } + + /// + /// Field that contains SpentTime data. + /// + [JsonProperty("spentTime")] + public TimeField SpentTime { get; set; } + + public class TimeField + { + /// + /// Name of the field. + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// Url of the field. + /// + [JsonProperty("url")] + public string Url { get; set; } + } + } +} \ No newline at end of file diff --git a/src/YouTrackSharp/Management/User.cs b/src/YouTrackSharp/Management/User.cs new file mode 100644 index 00000000..d3554ac9 --- /dev/null +++ b/src/YouTrackSharp/Management/User.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json; + +namespace YouTrackSharp.Management +{ + /// + /// A class that represents YouTrack user information. + /// + public class User + { + /// + /// Username of the user. + /// + [JsonProperty("login")] + public string Username { get; set; } + + /// + /// Full name of the user. + /// + [JsonProperty("fullName")] + public string FullName { get; set; } + + /// + /// Email address of the user. + /// + [JsonProperty("email")] + public string Email { get; set; } + + /// + /// Jabber of the user. + /// + [JsonProperty("jabber")] + public string Jabber { get; set; } + } +} \ No newline at end of file diff --git a/src/YouTrackSharp/Management/UserManagementService.cs b/src/YouTrackSharp/Management/UserManagementService.cs new file mode 100644 index 00000000..030a7d80 --- /dev/null +++ b/src/YouTrackSharp/Management/UserManagementService.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace YouTrackSharp.Management +{ + /// + /// A class that represents a REST API client for administering user accounts in YouTrack. + /// It uses a implementation to connect to the remote YouTrack server instance. + /// + public class UserManagementService + { + private readonly Connection _connection; + + /// + /// Creates an instance of the class. + /// + /// A instance that provides a connection to the remote YouTrack server instance. + public UserManagementService(Connection connection) + { + _connection = connection ?? throw new ArgumentNullException(nameof(connection)); + } + + /// + /// Get user by login name. + /// + /// Uses the REST API Get user by login name. + /// A instance that represents the user matching or null if the user does not exist. + /// When the call to the remote YouTrack server instance failed. + /// When the is null or empty. + public async Task GetUser(string username) + { + if (string.IsNullOrEmpty(username)) throw new ArgumentNullException(nameof(username)); + + var client = await _connection.GetAuthenticatedHttpClient(); + var response = await client.GetAsync($"rest/admin/user/{username}"); + + if (response.StatusCode == HttpStatusCode.NotFound) + { + return null; + } + + response.EnsureSuccessStatusCode(); + + return JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); + } + + /// + /// Get a list of all available registered users. + /// + /// Uses the REST API Get a list of all available registered users. + /// Search query (part of user login, name or email). + /// Filter by group (groupID). + /// Filter by role. + /// Filter by project (projectID) + /// Filter by permission. + /// When true, get only users which are currently online. Defaults to false. + /// Paginator mode (takes 10 records). + /// A of instances. + /// When the call to the remote YouTrack server instance failed. + public async Task> GetUsers(string filter = null, string group = null, string role = null, + string project = null, string permission = null, bool onlineOnly = false, int start = 0) + { + var queryString = new List(7); + if (!string.IsNullOrEmpty(filter)) + { + queryString.Add($"q={WebUtility.UrlEncode(filter)}"); + } + if (!string.IsNullOrEmpty(group)) + { + queryString.Add($"group={WebUtility.UrlEncode(group)}"); + } + if (!string.IsNullOrEmpty(role)) + { + queryString.Add($"role={WebUtility.UrlEncode(role)}"); + } + if (!string.IsNullOrEmpty(project)) + { + queryString.Add($"project={WebUtility.UrlEncode(project)}"); + } + if (!string.IsNullOrEmpty(permission)) + { + queryString.Add($"permission={WebUtility.UrlEncode(permission)}"); + } + if (onlineOnly) + { + queryString.Add($"onlineOnly=true"); + } + if (start > 0) + { + queryString.Add($"start={start}"); + } + + var query = string.Join("&", queryString); + + return await GetUsersFromPath($"rest/admin/user?{query}"); + } + + /// + /// Create a new user. + /// + /// Uses the REST API Create a new user. + /// Login name of the user to be created. + /// Full name of a new user. + /// E-mail address of the user. + /// Jabber address for the new user. + /// Password for the new user. + /// When the call to the remote YouTrack server instance failed. + public async Task CreateUser(string username, string fullName, string email, string jabber, string password) + { + var queryString = new Dictionary(4); + if (!string.IsNullOrEmpty(fullName)) + { + queryString.Add("fullName", WebUtility.UrlEncode(fullName)); + } + if (!string.IsNullOrEmpty(email)) + { + queryString.Add("email", WebUtility.UrlEncode(email)); + } + if (!string.IsNullOrEmpty(jabber)) + { + queryString.Add("jabber", WebUtility.UrlEncode(jabber)); + } + if (!string.IsNullOrEmpty(password)) + { + queryString.Add("password", WebUtility.UrlEncode(password)); + } + + var client = await _connection.GetAuthenticatedHttpClient(); + var response = await client.PutAsync($"rest/admin/user/{username}", new FormUrlEncodedContent(queryString)); + + response.EnsureSuccessStatusCode(); + } + + /// + /// Updates a user. + /// + /// Uses the REST API Update a user. + /// Login name of the user to be updated. + /// Full name of a user. + /// E-mail address of the user. + /// Jabber address for the user. + /// Password for the user. + /// When the call to the remote YouTrack server instance failed. + public async Task UpdateUser(string username, string fullName = null, string email = null, string jabber = null, string password = null) + { + var queryString = new Dictionary(4); + if (!string.IsNullOrEmpty(fullName)) + { + queryString.Add("fullName", WebUtility.UrlEncode(fullName)); + } + if (!string.IsNullOrEmpty(email)) + { + queryString.Add("email", WebUtility.UrlEncode(email)); + } + if (!string.IsNullOrEmpty(jabber)) + { + queryString.Add("jabber", WebUtility.UrlEncode(jabber)); + } + if (!string.IsNullOrEmpty(password)) + { + queryString.Add("password", WebUtility.UrlEncode(password)); + } + + var client = await _connection.GetAuthenticatedHttpClient(); + var response = await client.PostAsync($"rest/admin/user/{username}", new FormUrlEncodedContent(queryString)); + + response.EnsureSuccessStatusCode(); + } + + /// + /// Delete specific user account. + /// + /// Uses the REST API Delete a user. + /// Login name of the user to be deleted. + /// When the call to the remote YouTrack server instance failed. + public async Task DeleteUser(string username) + { + var client = await _connection.GetAuthenticatedHttpClient(); + var response = await client.DeleteAsync($"rest/admin/user/{username}"); + + response.EnsureSuccessStatusCode(); + } + + /// + /// Merge users. + /// + /// Uses the REST API Delete a user. + /// Login name of the user to be merged. + /// Login name of the user to merge into. + /// When the call to the remote YouTrack server instance failed. + public async Task MergeUsers(string usernameToMerge, string targetUser) + { + var client = await _connection.GetAuthenticatedHttpClient(); + var response = await client.PostAsync($"rest/admin/user/{targetUser}/merge/{usernameToMerge}", new StringContent(string.Empty)); + + response.EnsureSuccessStatusCode(); + } + + /// + /// Get all groups the specified user participates in. + /// + /// Uses the REST API Get all groups the specified user participates in. + /// Login name of the user to retrieve information for. + /// A of instances. + /// When the call to the remote YouTrack server instance failed. + public async Task> GetGroupsForUser(string username) + { + var client = await _connection.GetAuthenticatedHttpClient(); + var response = await client.GetAsync($"rest/admin/user/{username}/group"); + + response.EnsureSuccessStatusCode(); + + return JsonConvert.DeserializeObject>( + await response.Content.ReadAsStringAsync()); + } + + /// + /// Add user to group. + /// + /// Uses the REST API Add user account to a group. + /// Login name of the user to be updated. + /// Name of the group to add the user to. + /// When the call to the remote YouTrack server instance failed. + public async Task AddUserToGroup(string username, string group) + { + var client = await _connection.GetAuthenticatedHttpClient(); + var response = await client.PostAsync($"rest/admin/user/{username}/group/{WebUtility.UrlEncode(group)}", new StringContent(string.Empty)); + + response.EnsureSuccessStatusCode(); + } + + /// + /// Remove user from group. + /// + /// Uses the REST API Remove user account from a group. + /// Login name of the user to be updated. + /// Name of the group to remove the user from. + /// When the call to the remote YouTrack server instance failed. + public async Task RemoveUserFromGroup(string username, string group) + { + var client = await _connection.GetAuthenticatedHttpClient(); + var response = await client.DeleteAsync($"rest/admin/user/{username}/group/{WebUtility.UrlEncode(group)}"); + + response.EnsureSuccessStatusCode(); + } + + private async Task> GetUsersFromPath(string path) + { + if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path)); + + var client = await _connection.GetAuthenticatedHttpClient(); + var response = await client.GetAsync(path); + + response.EnsureSuccessStatusCode(); + + var users = new List(); + var userRefs = JArray.Parse(await response.Content.ReadAsStringAsync()); + foreach (var userRef in userRefs) + { + users.Add(await GetUser(userRef["login"].Value())); + } + + return users; + } + } +} \ No newline at end of file diff --git a/src/YouTrackSharp/TimeTracking/TimeTrackingService.cs b/src/YouTrackSharp/TimeTracking/TimeTrackingService.cs index 74a84c99..b768cfba 100644 --- a/src/YouTrackSharp/TimeTracking/TimeTrackingService.cs +++ b/src/YouTrackSharp/TimeTracking/TimeTrackingService.cs @@ -170,7 +170,6 @@ public async Task UpdateWorkItemForIssue(string issueId, string workItemId, Work response.EnsureSuccessStatusCode(); } - /// /// Deletes a work item for an issue from the server. /// @@ -196,5 +195,5 @@ public async Task DeleteWorkItemForIssue(string issueId, string workItemId) response.EnsureSuccessStatusCode(); } - } + } } \ No newline at end of file diff --git a/src/YouTrackSharp/TimeTracking/WorkItem.cs b/src/YouTrackSharp/TimeTracking/WorkItem.cs index 9b4b0e3f..0bfeed85 100644 --- a/src/YouTrackSharp/TimeTracking/WorkItem.cs +++ b/src/YouTrackSharp/TimeTracking/WorkItem.cs @@ -42,7 +42,7 @@ public WorkItem(DateTime? date, TimeSpan duration, string description = null, Wo public string Id { get; set; } /// - /// Represents when the work item was created. + /// Represents when the work item was performed. /// [JsonConverter(typeof(UnixDateTimeOffsetConverter))] [JsonProperty("date")] @@ -72,5 +72,19 @@ public WorkItem(DateTime? date, TimeSpan duration, string description = null, Wo /// [JsonProperty("author")] public Author Author { get; set; } + + /// + /// Represents when the work item was created. + /// + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonProperty("created")] + public DateTime? Created { get; set; } + + /// + /// Represents when the work item was updated. + /// + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonProperty("updated")] + public DateTime? Updated { get; set; } } } \ No newline at end of file diff --git a/src/YouTrackSharp/YouTrackSharp.csproj b/src/YouTrackSharp/YouTrackSharp.csproj index dbcf7354..503c8a55 100644 --- a/src/YouTrackSharp/YouTrackSharp.csproj +++ b/src/YouTrackSharp/YouTrackSharp.csproj @@ -5,7 +5,7 @@ True True https://raw.githubusercontent.com/JetBrains/YouTrackSharp/master/logo.png - https://github.com/JetBrains/YouTrackSharp/blob/master/LICENSE.TXT + https://github.com/JetBrains/YouTrackSharp/blob/master/LICENSE.txt https://github.com/JetBrains/YouTrackSharp/ YouTrack JetBrains SDK 3.0.0.0 @@ -17,7 +17,7 @@ JetBrains https://github.com/JetBrains/YouTrackSharp.git Git - 3.0.1 + 3.1.0 diff --git a/tests/YouTrackSharp.Tests/Infrastructure/Connections.cs b/tests/YouTrackSharp.Tests/Infrastructure/Connections.cs index a602da75..613c6fa4 100644 --- a/tests/YouTrackSharp.Tests/Infrastructure/Connections.cs +++ b/tests/YouTrackSharp.Tests/Infrastructure/Connections.cs @@ -19,6 +19,8 @@ public static string ServerUrl public static Connection Demo2Password => new UsernamePasswordConnection(ServerUrl, "demo2", "demo2"); + public static Connection Demo3Token => + new BearerTokenConnection(ServerUrl, "perm:ZGVtbzM=.WW91VHJhY2tTaGFycA==.L04RdcCnjyW2UPCVg1qyb6dQflpzFy"); public static class TestData { diff --git a/tests/YouTrackSharp.Tests/Integration/Management/TimeTracking/GetSystemWideTimeTrackingSettings.cs b/tests/YouTrackSharp.Tests/Integration/Management/TimeTracking/GetSystemWideTimeTrackingSettings.cs new file mode 100644 index 00000000..102a97cc --- /dev/null +++ b/tests/YouTrackSharp.Tests/Integration/Management/TimeTracking/GetSystemWideTimeTrackingSettings.cs @@ -0,0 +1,35 @@ +using System.Linq; +using System.Threading.Tasks; +using Xunit; +using YouTrackSharp.Tests.Infrastructure; + +namespace YouTrackSharp.Tests.Integration.Management.TimeTracking +{ + public partial class TimeTrackingServiceTests + { + public class GetSystemwideTimeTrackingSettings + { + [Fact] + public async Task Valid_Connection_Gets_Systemwide_TimeTracking_Settings() + { + // Arrange + var connection = Connections.Demo3Token; + var service = connection.CreateTimeTrackingManagementService(); + + // Act + var results = await service.GetSystemWideTimeTrackingSettings(); + var workdays = results.WorkDays.ToList(); + + // Assert + Assert.Equal(5, results.DaysAWeek); + Assert.True(results.HoursADay > 0); + + Assert.Equal(1, workdays[0].Value); + Assert.Equal(2, workdays[1].Value); + Assert.Equal(3, workdays[2].Value); + Assert.Equal(4, workdays[3].Value); + Assert.Equal(5, workdays[4].Value); + } + } + } +} \ No newline at end of file diff --git a/tests/YouTrackSharp.Tests/Integration/Management/TimeTracking/GetTimeTrackingSettingsForProject.cs b/tests/YouTrackSharp.Tests/Integration/Management/TimeTracking/GetTimeTrackingSettingsForProject.cs new file mode 100644 index 00000000..7f90870a --- /dev/null +++ b/tests/YouTrackSharp.Tests/Integration/Management/TimeTracking/GetTimeTrackingSettingsForProject.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using Xunit; +using YouTrackSharp.Tests.Infrastructure; + +namespace YouTrackSharp.Tests.Integration.Management.TimeTracking +{ + public partial class TimeTrackingServiceTests + { + public class GetTimeTrackingSettingsForProject + { + [Fact] + public async Task Valid_Connection_Gets_TimeTracking_Settings_For_Project() + { + // Arrange + var connection = Connections.Demo3Token; + var service = connection.CreateTimeTrackingManagementService(); + + // Act + var results = await service.GetTimeTrackingSettingsForProject("DP1"); + + // Assert + Assert.True(results.Enabled); + Assert.Equal("Estimation", results.Estimation.Name); + Assert.Equal("Spent time", results.SpentTime.Name); + } + } + } +} \ No newline at end of file diff --git a/tests/YouTrackSharp.Tests/Integration/Management/TimeTracking/UpdateSystemWideTimeTrackingSettings.cs b/tests/YouTrackSharp.Tests/Integration/Management/TimeTracking/UpdateSystemWideTimeTrackingSettings.cs new file mode 100644 index 00000000..8116a013 --- /dev/null +++ b/tests/YouTrackSharp.Tests/Integration/Management/TimeTracking/UpdateSystemWideTimeTrackingSettings.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Xunit; +using YouTrackSharp.Management; +using YouTrackSharp.Tests.Infrastructure; + +namespace YouTrackSharp.Tests.Integration.Management.TimeTracking +{ + public partial class TimeTrackingManagementServiceTests + { + public class UpdateSystemWideTimeTrackingSettings + { + [Fact] + public async Task Valid_Connection_Updates_Systemwide_TimeTracking_Settings() + { + // Arrange + var connection = Connections.Demo3Token; + var service = connection.CreateTimeTrackingManagementService(); + + var random = new Random(); + var hoursADay = random.Next(1, 20); + + // Act + var timeSettings = new SystemWideTimeTrackingSettings + { + HoursADay = hoursADay, + WorkDays = new List>(5) + }; + timeSettings.WorkDays.Add(new SubValue { Value = 1 }); + timeSettings.WorkDays.Add(new SubValue { Value = 2 }); + timeSettings.WorkDays.Add(new SubValue { Value = 3 }); + timeSettings.WorkDays.Add(new SubValue { Value = 4 }); + timeSettings.WorkDays.Add(new SubValue { Value = 5 }); + + await service.UpdateSystemWideTimeTrackingSettings(timeSettings); + + // Assert + var result = await service.GetSystemWideTimeTrackingSettings(); + Assert.NotNull(result); + Assert.Equal(hoursADay, result.HoursADay); + } + } + } +} \ No newline at end of file diff --git a/tests/YouTrackSharp.Tests/Integration/Management/TimeTracking/UpdateTimeTrackingSettingsForProject.cs b/tests/YouTrackSharp.Tests/Integration/Management/TimeTracking/UpdateTimeTrackingSettingsForProject.cs new file mode 100644 index 00000000..adbff4c4 --- /dev/null +++ b/tests/YouTrackSharp.Tests/Integration/Management/TimeTracking/UpdateTimeTrackingSettingsForProject.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using Xunit; +using YouTrackSharp.Management; +using YouTrackSharp.Tests.Infrastructure; + +namespace YouTrackSharp.Tests.Integration.Management.TimeTracking +{ + public partial class TimeTrackingServiceTests + { + public class TimeTrackingManagementServiceTests + { + [Fact] + public async Task Valid_Connection_Updates_TimeTracking_Settings_For_Project() + { + // Arrange + var connection = Connections.Demo1Token; + var service = connection.CreateTimeTrackingManagementService(); + + // Act + await service.UpdateTimeTrackingSettingsForProject("DP1", new TimeTrackingSettings { Enabled = true }); + + // Assert + var result = await service.GetTimeTrackingSettingsForProject("DP1"); + Assert.True(result.Enabled); + } + } + } +} \ No newline at end of file diff --git a/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/AddAndRemoveUserFromGroup.cs b/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/AddAndRemoveUserFromGroup.cs new file mode 100644 index 00000000..866f7614 --- /dev/null +++ b/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/AddAndRemoveUserFromGroup.cs @@ -0,0 +1,39 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Xunit; +using YouTrackSharp.Tests.Infrastructure; + +namespace YouTrackSharp.Tests.Integration.Management.UserManagement +{ + public partial class UserManagementServiceTests + { + public class AddAndRemoveUserFromGroup + { + [Fact] + public async Task Valid_Connection_Adds_And_Removes_User_From_Group() + { + // Arrange + var connection = Connections.Demo3Token; + var service = connection.CreateUserManagementService(); + + // Act + try + { + await service.AddUserToGroup("demo2", "Reporters"); + + // Assert + var result = await service.GetGroupsForUser("demo2"); + Assert.NotNull(result); + Assert.True(result.Count > 0); + Assert.True(result.Any(group => + string.Equals(group.Name, "Reporters", StringComparison.OrdinalIgnoreCase))); + } + finally + { + await service.RemoveUserFromGroup("demo2", "Reporters"); + } + } + } + } +} \ No newline at end of file diff --git a/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/CreateAndDeleteUser.cs b/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/CreateAndDeleteUser.cs new file mode 100644 index 00000000..edac4518 --- /dev/null +++ b/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/CreateAndDeleteUser.cs @@ -0,0 +1,40 @@ +using System; +using System.Threading.Tasks; +using Xunit; +using YouTrackSharp.Tests.Infrastructure; + +namespace YouTrackSharp.Tests.Integration.Management.UserManagement +{ + public partial class UserManagementServiceTests + { + public class CreateAndDeleteUser + { + [Fact(Skip = "Don't want to pollute our server instance with random users.")] + public async Task Valid_Connection_Creates_User() + { + // Arrange + var connection = Connections.Demo3Token; + var service = connection.CreateUserManagementService(); + + var randomUsername = "test" + Guid.NewGuid().ToString().Replace("-", string.Empty); + var randomPassword = "pwd" + Guid.NewGuid().ToString().Replace("-", string.Empty); + + // Act + await service.CreateUser(randomUsername, "Test User", "test@example.org", null, randomPassword); + + // Assert + try + { + var result = await service.GetUser(randomUsername); + Assert.NotNull(result); + Assert.Equal(randomUsername, result.Username); + } + finally + { + // Delete the user + await service.DeleteUser(randomUsername); + } + } + } + } +} \ No newline at end of file diff --git a/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/CreateAndMergeUser.cs b/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/CreateAndMergeUser.cs new file mode 100644 index 00000000..7b9b5f09 --- /dev/null +++ b/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/CreateAndMergeUser.cs @@ -0,0 +1,47 @@ +using System; +using System.Threading.Tasks; +using Xunit; +using YouTrackSharp.Tests.Infrastructure; + +namespace YouTrackSharp.Tests.Integration.Management.UserManagement +{ + public partial class UserManagementServiceTests + { + public class CreateAndMergeUser + { + [Fact(Skip = "Don't want to pollute our server instance with random users.")] + public async Task Valid_Connection_Merges_User() + { + // Arrange + var connection = Connections.Demo3Token; + var service = connection.CreateUserManagementService(); + + var randomUsername = "test" + Guid.NewGuid().ToString().Replace("-", string.Empty); + var randomPassword = "pwd" + Guid.NewGuid().ToString().Replace("-", string.Empty); + + await service.CreateUser(randomUsername + "1", "Test 1 User", "test1@example.org", null, randomPassword); + await service.CreateUser(randomUsername + "2", "Test 2 User", "test2@example.org", null, randomPassword); + + // Act + await service.MergeUsers(randomUsername + "1", randomUsername + "2"); + + // Assert + try + { + var result1 = await service.GetUser(randomUsername + "1"); + var result2 = await service.GetUser(randomUsername + "2"); + + Assert.Null(result1); + + Assert.NotNull(result2); + Assert.Equal(randomUsername + "2", result2.Username); + } + finally + { + // Delete the user + await service.DeleteUser(randomUsername + "2"); + } + } + } + } +} \ No newline at end of file diff --git a/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/CreateAndUpdateUser.cs b/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/CreateAndUpdateUser.cs new file mode 100644 index 00000000..9054f136 --- /dev/null +++ b/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/CreateAndUpdateUser.cs @@ -0,0 +1,43 @@ +using System; +using System.Threading.Tasks; +using Xunit; +using YouTrackSharp.Tests.Infrastructure; + +namespace YouTrackSharp.Tests.Integration.Management.UserManagement +{ + public partial class UserManagementServiceTests + { + public class CreateAndUpdateUser + { + [Fact(Skip = "Don't want to pollute our server instance with random users.")] + public async Task Valid_Connection_Updates_User() + { + // Arrange + var connection = Connections.Demo3Token; + var service = connection.CreateUserManagementService(); + + var randomUsername = "test" + Guid.NewGuid().ToString().Replace("-", string.Empty); + var randomPassword = "pwd" + Guid.NewGuid().ToString().Replace("-", string.Empty); + + await service.CreateUser(randomUsername, "Test User", "test1@example.org", null, randomPassword); + + // Act + await service.UpdateUser(randomUsername, "Test user (updated)"); + + // Assert + try + { + var result = await service.GetUser(randomUsername); + + Assert.NotNull(result); + Assert.Equal(randomUsername, "Test user (updated)"); + } + finally + { + // Delete the user + await service.DeleteUser(randomUsername); + } + } + } + } +} \ No newline at end of file diff --git a/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/GetGroupsForUser.cs b/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/GetGroupsForUser.cs new file mode 100644 index 00000000..9994b19e --- /dev/null +++ b/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/GetGroupsForUser.cs @@ -0,0 +1,31 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Xunit; +using YouTrackSharp.Tests.Infrastructure; + +namespace YouTrackSharp.Tests.Integration.Management.UserManagement +{ + public partial class UserManagementServiceTests + { + public class GetGroupsForUser + { + [Fact] + public async Task Valid_Connection_Returns_Groups_ForUser() + { + // Arrange + var connection = Connections.Demo3Token; + var service = connection.CreateUserManagementService(); + + // Act + var result = await service.GetGroupsForUser("demo3"); + + // Assert + Assert.NotNull(result); + Assert.True(result.Count > 0); + Assert.True(result.Any(group => + string.Equals(group.Name, "All Users", StringComparison.OrdinalIgnoreCase))); + } + } + } +} \ No newline at end of file diff --git a/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/GetUser.cs b/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/GetUser.cs new file mode 100644 index 00000000..48170873 --- /dev/null +++ b/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/GetUser.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; +using Xunit; +using YouTrackSharp.Tests.Infrastructure; + +namespace YouTrackSharp.Tests.Integration.Management.UserManagement +{ + public partial class UserManagementServiceTests + { + public class GetUser + { + [Fact] + public async Task Valid_Connection_Returns_User() + { + // Arrange + var connection = Connections.Demo3Token; + var service = connection.CreateUserManagementService(); + + // Act + var result = await service.GetUser("demo1"); + + // Assert + Assert.NotNull(result); + Assert.Equal("demo1", result.Username); + } + + [Fact] + public async Task Valid_Connection_Returns_Null_For_NonExistant_User() + { + // Arrange + var connection = Connections.Demo3Token; + var service = connection.CreateUserManagementService(); + + // Act + var result = await service.GetUser("does-not-exist"); + + // Assert + Assert.Null(result); + } + } + } +} \ No newline at end of file diff --git a/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/GetUsers.cs b/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/GetUsers.cs new file mode 100644 index 00000000..1cadbaa1 --- /dev/null +++ b/tests/YouTrackSharp.Tests/Integration/Management/UserManagement/GetUsers.cs @@ -0,0 +1,29 @@ +using System.Linq; +using System.Threading.Tasks; +using Xunit; +using YouTrackSharp.Tests.Infrastructure; + +namespace YouTrackSharp.Tests.Integration.Management.UserManagement +{ + public partial class UserManagementServiceTests + { + public class GetUsers + { + [Fact] + public async Task Valid_Connection_Returns_Users() + { + // Arrange + var connection = Connections.Demo3Token; + var service = connection.CreateUserManagementService(); + + // Act + var result = await service.GetUsers(); + + // Assert + Assert.NotNull(result); + Assert.True(result.Count > 0); + Assert.True(result.Any(user => user.Username == "demo1")); + } + } + } +} \ No newline at end of file diff --git a/tests/YouTrackSharp.Tests/YouTrackSharp.Tests.csproj b/tests/YouTrackSharp.Tests/YouTrackSharp.Tests.csproj index e0ba89cd..98e647e0 100644 --- a/tests/YouTrackSharp.Tests/YouTrackSharp.Tests.csproj +++ b/tests/YouTrackSharp.Tests/YouTrackSharp.Tests.csproj @@ -7,13 +7,13 @@ JetBrains YouTrackSharp Tests for YouTrackSharp. - https://github.com/JetBrains/YouTrackSharp/blob/master/LICENSE.TXT + https://github.com/JetBrains/YouTrackSharp/blob/master/LICENSE.txt JetBrains https://github.com/JetBrains/YouTrackSharp/ https://raw.githubusercontent.com/JetBrains/YouTrackSharp/master/logo.png https://github.com/JetBrains/YouTrackSharp.git Git - 3.0.1 + 3.1.0 False diff --git a/tests/YouTrackSharp.Tests/YouTrackSharp.Tests.csproj.DotSettings b/tests/YouTrackSharp.Tests/YouTrackSharp.Tests.csproj.DotSettings new file mode 100644 index 00000000..e303c506 --- /dev/null +++ b/tests/YouTrackSharp.Tests/YouTrackSharp.Tests.csproj.DotSettings @@ -0,0 +1,2 @@ + + False \ No newline at end of file