Begin implementation of Enterprise ManagementConsole API, redux (#2010)

* Initial implementation of ManagementConsole - maintenance mode

* Add environment var support for management console password for integration tests

* Add reactive client and unit tests

* Update some xmlDoc

* I think this is a better way to setup the underlying baseUri on IConneciton, to achieve managemet console access rather than requiring a specific GitHubClient that cant call normal API's
Instead, the management client methods can check the base Url and if it contains /api/v3/ they can set their relative endpoint Uri to include a leading "/" which will cause the /api/v3/ to be removed.

* Update EnterpriseClient.cs

Fix xml comments

* Update IEnterpriseClient.cs

Fix xml comments

* Still trying to get the xmDoc perfect, thanks app veyor :)

* XmlDoc'ing my way to success

* Add specific test attribute for management console tests

* check chronic string empty/null

* Use helper's password field in test

* Tidy up maintenance mode tests by using a context/destructor to manage the initial/end state of maintenance mode

* make internal and tidy up URL concatenation

* move GHE endpoint fixup inside ApiUrls methods

* Rework request object to be the correct structure so SimpleJsonSerializer can be used to serialize it.  Remove MaintenanceDate class and just pass in the Date/string for when
Still need to use UrlFormEncoding rather than json in the POST body though...

* Create abstract base class for FormUrlEncoded parameters (similar to existing RequetParameters) and inherit from it in UpdateMaintenanceRequest

* Fix maintenance context logic - destructor should always turn maintenance OFF regardless of initial requested state

* Fix xml comment

* Fix Xml comment

* Those pesky xml comments!

* Fine, I give up!

* Fix string.Format

* fix bad rebase

* fix failing convention tests

* restore missing whitespace

* writing some docs

* some edits

* edit
This commit is contained in:
Brendan Forster
2019-09-22 15:09:56 -03:00
committed by GitHub
parent fafbf33b78
commit 8cd0b341dd
37 changed files with 965 additions and 40 deletions
@@ -0,0 +1,107 @@
using System;
using System.Diagnostics;
using System.Globalization;
namespace Octokit
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class UpdateMaintenanceRequest : FormUrlEncodedParameters
{
/// <summary>
/// Maintenance request with default details (results in Maintenance mode being turned off immediately)
/// </summary>
public UpdateMaintenanceRequest()
{
Maintenance = new UpdateMaintenanceRequestDetails();
}
/// <summary>
/// Maintenance request with specific details
/// </summary>
public UpdateMaintenanceRequest(UpdateMaintenanceRequestDetails maintenance)
{
Ensure.ArgumentNotNull(maintenance, "maintenance");
Maintenance = maintenance;
}
/// <summary>
/// Details for the maintenance request
/// </summary>
public UpdateMaintenanceRequestDetails Maintenance { get; protected set; }
internal string DebuggerDisplay
{
get
{
return string.Format(CultureInfo.InvariantCulture, "Maintenance: {0}", this.Maintenance.DebuggerDisplay);
}
}
}
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class UpdateMaintenanceRequestDetails
{
/// <summary>
/// Maintenance request details with default values (results in Maintenance mode being turned off immediately)
/// </summary>
public UpdateMaintenanceRequestDetails()
{ }
/// <summary>
/// Maintenance request details to enable/disable maintenance mode immediately
/// </summary>
/// <param name="enabled">true to enable, false to disable</param>
public UpdateMaintenanceRequestDetails(bool enabled)
{
Enabled = enabled;
When = "now";
}
/// <summary>
/// Maintenance request details to enable/disable maintenance at a specified time (only applicable when enabling maintenance)
/// </summary>
/// <param name="enabled">true to enable, false to disable</param>
/// <param name="when">A phrase specifying when maintenance mode will be enabled. Phrase uses humanised forms supported by the <a href="https://github.com/mojombo/chronic">mojombo/chronic library</a> used by the GitHub API</param>
/// such as "this friday at 5pm" or "5 minutes before midday tomorrow"
/// <remarks>If enabled is false, the when parameter is ignored and maintenance is turned off immediately</remarks>
public UpdateMaintenanceRequestDetails(bool enabled, string when)
{
Ensure.ArgumentNotNullOrEmptyString(when, "when");
Enabled = enabled;
When = when;
}
/// <summary>
/// Maintenance request details to enable/disable maintenance at a specified time (only applicable when enabling maintenance)
/// </summary>
/// <param name="enabled">true to enable, false to disable</param>
/// <param name="when">A <see cref="DateTimeOffset"/> specifying when maintenance mode will be enabled.</param>
/// <remarks>If enabled is false, the when parameter is ignored and maintenance is turned off immediately</remarks>
public UpdateMaintenanceRequestDetails(bool enabled, DateTimeOffset when)
{
Enabled = enabled;
When = when.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ",
CultureInfo.InvariantCulture);
}
/// <summary>
/// Whether maintenance mode is enabled or disabled
/// </summary>
public bool Enabled { get; protected set; }
/// <summary>
/// When maintenance mode will take effect (only applicable when enabling maintenance)
/// </summary>
public string When { get; protected set; }
internal string DebuggerDisplay
{
get
{
return string.Format(CultureInfo.InvariantCulture, "Enabled: {0} When: {1}", this.Enabled, this.When);
}
}
}
}
@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Octokit.Internal;
namespace Octokit
{
/// <summary>
/// Base class for classes which represent UrlFormEncoded parameters to certain API endpoints.
/// </summary>
public abstract class FormUrlEncodedParameters
{
/// <summary>
/// Converts the derived object into a UrlFormEncoded parameter string containing named parameters and their json serialized values
/// This format is required for particular API calls (eg the GitHub Enterprise Management Console API) that take a parameter formatted as json but not in the request body
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings")]
public string ToFormUrlEncodedParameterString()
{
var parameters = new List<string>();
foreach (var prop in GetPropertyParametersForType(this.GetType()))
{
parameters.Add(string.Format(CultureInfo.InvariantCulture, "{0}={1}", prop.Key, prop.GetValue(this)));
}
return string.Join("&", parameters);
}
static List<JsonParameter> GetPropertyParametersForType(Type type)
{
return type.GetAllProperties()
.Where(p => p.Name != "DebuggerDisplay")
.Select(p => new JsonParameter(p))
.ToList();
}
class JsonParameter
{
readonly PropertyInfo _property;
public JsonParameter(PropertyInfo property)
{
_property = property;
Key = GetParameterKeyFromProperty(property);
}
public string Key { get; private set; }
public string GetValue(object instance)
{
var value = _property.GetValue(instance, null);
return value != null ? new SimpleJsonSerializer().Serialize(value) : null;
}
[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase",
Justification = "GitHub API depends on lower case strings")]
static string GetParameterKeyFromProperty(PropertyInfo property)
{
var attribute = property.GetCustomAttributes(typeof(ParameterAttribute), false)
.Cast<ParameterAttribute>()
.FirstOrDefault(attr => attr.Key != null);
return attribute == null
? property.Name.ToLowerInvariant()
: attribute.Key;
}
}
}
}
@@ -100,7 +100,7 @@ namespace Octokit
Comments != null ? "Comments," : ""
).Trim(',');
return String.Format(CultureInfo.InvariantCulture, "Statistics: {0}", fieldsPresent);
return string.Format(CultureInfo.InvariantCulture, "Statistics: {0}", fieldsPresent);
}
}
}
@@ -45,7 +45,7 @@ namespace Octokit
{
get
{
return String.Format(CultureInfo.InvariantCulture, "TotalCommitComments: {0} TotalGistComments: {1} TotalIssueComments: {2} TotalPullRequestComments: {3}", TotalCommitComments, TotalGistComments, TotalIssueComments, TotalPullRequestComments);
return string.Format(CultureInfo.InvariantCulture, "TotalCommitComments: {0} TotalGistComments: {1} TotalIssueComments: {2} TotalPullRequestComments: {3}", TotalCommitComments, TotalGistComments, TotalIssueComments, TotalPullRequestComments);
}
}
}
@@ -38,7 +38,7 @@ namespace Octokit
{
get
{
return String.Format(CultureInfo.InvariantCulture, "TotalGists: {0} PrivateGists: {1} PublicGists: {2}", TotalGists, PrivateGists, PublicGists);
return string.Format(CultureInfo.InvariantCulture, "TotalGists: {0} PrivateGists: {1} PublicGists: {2}", TotalGists, PrivateGists, PublicGists);
}
}
}
@@ -38,7 +38,7 @@ namespace Octokit
{
get
{
return String.Format(CultureInfo.InvariantCulture, "TotalHooks: {0} ActiveHooks: {1} InactiveHooks: {2}", TotalHooks, ActiveHooks, InactiveHooks);
return string.Format(CultureInfo.InvariantCulture, "TotalHooks: {0} ActiveHooks: {1} InactiveHooks: {2}", TotalHooks, ActiveHooks, InactiveHooks);
}
}
}
@@ -38,7 +38,7 @@ namespace Octokit
{
get
{
return String.Format(CultureInfo.InvariantCulture, "TotalIssues: {0} OpenIssues: {1} ClosedIssues: {2}", TotalIssues, OpenIssues, ClosedIssues);
return string.Format(CultureInfo.InvariantCulture, "TotalIssues: {0} OpenIssues: {1} ClosedIssues: {2}", TotalIssues, OpenIssues, ClosedIssues);
}
}
}
@@ -38,7 +38,7 @@ namespace Octokit
{
get
{
return String.Format(CultureInfo.InvariantCulture, "TotalMilestones: {0} OpenMilestones: {1} ClosedMilestones: {2}", TotalMilestones, OpenMilestones, ClosedMilestones);
return string.Format(CultureInfo.InvariantCulture, "TotalMilestones: {0} OpenMilestones: {1} ClosedMilestones: {2}", TotalMilestones, OpenMilestones, ClosedMilestones);
}
}
}
@@ -45,7 +45,7 @@ namespace Octokit
{
get
{
return String.Format(CultureInfo.InvariantCulture, "TotalOrgs: {0} DisabledOrgs: {1} TotalTeams: {2} TotalTeamMembers: {3}", TotalOrgs, DisabledOrgs, TotalTeams, TotalTeamMembers);
return string.Format(CultureInfo.InvariantCulture, "TotalOrgs: {0} DisabledOrgs: {1} TotalTeams: {2} TotalTeamMembers: {3}", TotalOrgs, DisabledOrgs, TotalTeams, TotalTeamMembers);
}
}
}
@@ -24,7 +24,7 @@ namespace Octokit
{
get
{
return String.Format(CultureInfo.InvariantCulture, "TotalPages: {0}", TotalPages);
return string.Format(CultureInfo.InvariantCulture, "TotalPages: {0}", TotalPages);
}
}
}
@@ -48,7 +48,7 @@ namespace Octokit
{
get
{
return String.Format(CultureInfo.InvariantCulture, "TotalPulls: {0} MergedPulls: {1} MergeablePulls: {2} UnmergeablePulls: {3}", TotalPulls, MergedPulls, MergeablePulls, UnmergeablePulls);
return string.Format(CultureInfo.InvariantCulture, "TotalPulls: {0} MergedPulls: {1} MergeablePulls: {2} UnmergeablePulls: {3}", TotalPulls, MergedPulls, MergeablePulls, UnmergeablePulls);
}
}
}
@@ -59,7 +59,7 @@ namespace Octokit
{
get
{
return String.Format(CultureInfo.InvariantCulture, "TotalRepos: {0} RootRepos: {1} ForkRepos: {2} OrgRepos: {3} TotalPushes: {4} TotalWikis: {5}", TotalRepos, RootRepos, ForkRepos, OrgRepos, TotalPushes, TotalWikis);
return string.Format(CultureInfo.InvariantCulture, "TotalRepos: {0} RootRepos: {1} ForkRepos: {2} OrgRepos: {3} TotalPushes: {4} TotalWikis: {5}", TotalRepos, RootRepos, ForkRepos, OrgRepos, TotalPushes, TotalWikis);
}
}
}
@@ -38,7 +38,7 @@ namespace Octokit
{
get
{
return String.Format(CultureInfo.InvariantCulture, "TotalUsers: {0} AdminUsers: {1} SuspendedUsers: {2}", TotalUsers, AdminUsers, SuspendedUsers);
return string.Format(CultureInfo.InvariantCulture, "TotalUsers: {0} AdminUsers: {1} SuspendedUsers: {2}", TotalUsers, AdminUsers, SuspendedUsers);
}
}
}
@@ -24,7 +24,7 @@ namespace Octokit
{
get
{
return String.Format(CultureInfo.InvariantCulture, "Status: {0}", Status);
return string.Format(CultureInfo.InvariantCulture, "Status: {0}", Status);
}
}
}
@@ -59,7 +59,7 @@ namespace Octokit
{
get
{
return String.Format(CultureInfo.InvariantCulture, "Seats: {0} SeatsUsed: {1} DaysUntilExpiration: {2}", Seats, SeatsUsed, DaysUntilExpiration);
return string.Format(CultureInfo.InvariantCulture, "Seats: {0} SeatsUsed: {1} DaysUntilExpiration: {2}", Seats, SeatsUsed, DaysUntilExpiration);
}
}
}
@@ -0,0 +1,34 @@
using System.Diagnostics;
using System.Globalization;
namespace Octokit
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class MaintenanceModeActiveProcesses
{
public MaintenanceModeActiveProcesses() { }
public MaintenanceModeActiveProcesses(string name, int number)
{
Name = name;
Number = number;
}
public string Name { get; protected set; }
public int Number { get; protected set; }
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "{0} ({1})", Name, Number);
}
internal string DebuggerDisplay
{
get
{
return this.ToString();
}
}
}
}
@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Octokit.Internal;
namespace Octokit
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class MaintenanceModeResponse
{
public MaintenanceModeResponse() { }
public MaintenanceModeResponse(MaintenanceModeStatus status, string scheduledTime, IReadOnlyList<MaintenanceModeActiveProcesses> activeProcesses)
{
Status = status;
ScheduledTime = scheduledTime;
ActiveProcesses = activeProcesses;
}
public StringEnum<MaintenanceModeStatus> Status
{
get;
private set;
}
public string ScheduledTime
{
get;
private set;
}
[Parameter(Key = "connection_services")]
public IReadOnlyList<MaintenanceModeActiveProcesses> ActiveProcesses
{
get;
private set;
}
internal string DebuggerDisplay
{
get
{
return string.Format(CultureInfo.InvariantCulture,
"Status: {0} ScheduledTime: {1} Connections: {2}",
Status,
ScheduledTime ?? "",
string.Join(", ", ActiveProcesses));
}
}
}
public enum MaintenanceModeStatus
{
[Parameter(Value = "off")]
Off,
[Parameter(Value = "on")]
On,
[Parameter(Value = "scheduled")]
Scheduled
}
}
@@ -25,7 +25,7 @@ namespace Octokit
{
get
{
return String.Format(CultureInfo.InvariantCulture, "Message: {0}", string.Join("\r\n", Message));
return string.Format(CultureInfo.InvariantCulture, "Message: {0}", string.Join("\r\n", Message));
}
}
}