* Added StringEnum<TEnum> * Added tests * Make sure the serializer can work with StringEnum * Use StringEnum for EventInfo.Event * Add convention test to assert that all Response models use StringEnum<> to wrap enum properties * Add Stringnum<> to all response types failing convention test * Handle StringEnum to Enum conversion when Issue response model populates IssueUpdate request model * Fix unit test * Refactor SimpleJsonSerializer to expose the DeserializeEnum strategy so it can be used in StringEnum class * Need to expose/use SerializeEnum functionality too, so we use the correct string representation of enum values that have custom properties (eg ReactionType Plus1 to "+1") * fix unit tests, since the string is now the "correct" upstream api value * Add a couple of tests for the Enum serialize/deserialize when underscores, hyphens and custom property attributes are present * Compare parsed values for equality * add convention test to ensure enum members all have Parameter property set * update test to cover implicit conversions too * this test should work but fails at the moment due to magic hyphen removal in deserializer causing a one way trip from utf-8 to EncodingType.Utf8 with no way to get back * (unsuccesfully) expand event info test to try to catch more cases of unknown event types * fix broken integration test while im here * Fixed build errors after .NET Core merge * Value -> StringValue, ParsedValue -> Value * Don't allow StringValue to be null * Ignore enums not used in request/response models * Added ParameterAttribute to almost all enum values * Ignore Language enum * Fix failing tests * Fix milestone sort parameter and tests * whitespace * fix milestone unit tests * Fix StringEnum.Equals ... This could've been embarrassing! * Change SimpleJsonSerializer Enum handling to only use `[Parameter()]` attributes (no more magic removal of hyphen/underscores from strings) * Tidy up this integration test while im here * Only test request/response enums in convention test * Keep skipping Language * Remove unused method * Remove excluded enum types * Removed unnecessary ParameterAttributes * Remove unused enum * Add StringEnum test for string-comparison of two invalid values * Bring back IssueCommentSort and use it in IssueCommentRequest This reverts commit 38a4a291d1476ef8c992fe0f76956974b6f32a49. * Use assembly instead of namespace for Octokit check * Add failing test to reproduce the issue where only the first enum paramter/value was added to the cache * Fix deserializer enum cache to include all enum members rather than only the first member encountered * Use a static SimpleJsonSerializer in StringEnum * Remove serializer instance in StringEnum * Add some documentation on StringEnum<TEnum> * Fix parameter value to resolve failing integration test
4.3 KiB
Working with Enums
In order to provide a consistent and familiar dotnet experience, Octokit maps appropriate GitHub API fields to c# Enum's.
For example an Issue's status API values are "open" and "closed" which in Octokit are represented as ItemState.Open and ItemState.Closed
Introducing the StringEnum<TEnum> Wrapper
Since the upstream GitHub API can move fast, we want to avoid throwing exceptions when deserializing responses that contain field values that are not yet present in our Octokit Enum values.
Therefore Octokit now uses a wrapper class StringEnum<TEnum> to represent these values in all response models.
StringEnum<TEnum> Usage
StringEnum is able to be implicitly converted to/from Enum and string values and provides the convenience of dealing with explicit enumeration members the majority of cases, with the added resillience to fall back to strings when it needs to.
Whilst existing code will continue to function due to the implicit conversions, users should update their code to the "safe" usage patterns shown below in order to guard against future API additions causing deserialization failures.
Implicit conversions:
public enum EncodingType
{
Ascii, // api value "ascii"
Utf8 // api value "utf-8"
}
// Implicit conversion from Enum
StringEnum<EncodingType> encoding = EncodingType.Utf8;
// Implicit conversion from API string
StringEnum<EncodingType> encoding = "utf-8";
When dealing with a known API value
Accessing the string value (safe):
StringEnum<EncodingType> encoding = "utf-8";
Console.WriteLine(encoding.StringValue);
// "utf-8"
Accessing the Enum value (not safe, but works as the value is known):
StringEnum<EncodingType> encoding = "utf-8";
Console.WriteLine(encoding.Value);
// EncodingType.Utf8
When dealing with an unknown API value
Accessing the string value (safe):
StringEnum<EncodingType> encoding = "new_hoopy_format";
Console.WriteLine(encoding.StringValue);
// "new_hoopy_format"
Accessing the Enum value (not safe, throws exceptionn as the value is not known)
StringEnum<EncodingType> encoding = "new_hoopy_format";
encoding.Value;
// ArgumentException: "Value 'new_hoopy_format' is not a valid 'EncodingType' enum value.
Evaluating the value safely, using TryParse()
StringEnum<EncodingType> encoding = "new_hoopy_format";
if (encoding.TryParse(out EncodingType type))
{
Console.WriteLine("{0} was a known enum member!", type);
}
else
{
Console.WriteLine("{0} was not a known enum member!", encoding.StringValue);
}
// "new_hoopy_format was not a known enum member!"
Activity/Timeline APIs and EventInfoState
Of particular importance are the Enum's used in activity/event stream APIs, such as EventInfoState. Since new functionality introduced on GitHub.com frequently leads to additional event types being received, Octokit was constantly playing catchup with these Enum values. Now, with StringEnum<TEnum> we have a solution to keep your code functioning, without needing to wait for a new Octokit release!
Safe Issue Timeline EventInfoState Example
// Get the event timeline for a PullRequest
var timelineEventInfos = await client.Issue.Timeline.GetAllForIssue("octokit", "octokit.net", 1595);
foreach (var issueEvent in timelineEventInfos)
{
// Safely check whether the EventInfoType was a known Enum value
if (issueEvent.Event.TryParse(out EventInfoState eventState))
{
// Enum value known
switch (eventState)
{
case EventInfoState.Commented:
case EventInfoState.CommitCommented:
case EventInfoState.LineCommented:
{
Console.WriteLine("Comment activity found!");
break;
}
case EventInfoState.ReviewDismissed:
case EventInfoState.Reviewed:
case EventInfoState.ReviewRequested:
case EventInfoState.ReviewRequestRemoved:
{
Console.WriteLine("Review activity found!");
break;
}
}
}
else
{
// Enum value not known, use StringValue
Console.WriteLine("Unknown EventInfoState encountered: {0}",
issueEvent.Event.StringValue);
}
}