-
Notifications
You must be signed in to change notification settings - Fork 88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add error handling, general refinement to --skip-invitation
flag
#1006
Conversation
src/OctoshiftCLI.Tests/Octoshift/Services/ConfirmationServiceTests.cs
Outdated
Show resolved
Hide resolved
Thanks @hfishback01 for getting this out, can you answer if you handled if org is not emu, not sure if this was addressed or not didn't catch it in the description/screenshots. If not can you add a line item to finish as part of this PR as that's a main error scenario we'll want to handle and make sure we don't keep iterating through csv. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking great! I have a few PR review items to take a look at!
Also, since we're now having interactive prompts, we should probably add a --no-prompt
option, too. We promote this tool to be used in scripts (it even creates said scripts!) so we should be ready to handle non-interactive commands.
We could start by going the easy route and abort with an error stating that --no-prompt
was used, and there are unresolved mannequins that the user needs to address. This should be enough to have the user read the logs and know exactly what to do 👍
If we aren't implementing the mannequin reclaiming command in any scripts, and we're sure that the scripts generated from the CLI tool won't hang, I'm 100% on making this a tech debt item 👍
src/OctoshiftCLI.Tests/Octoshift/Services/ConfirmationServiceTests.cs
Outdated
Show resolved
Hide resolved
src/Octoshift/Commands/ReclaimMannequin/ReclaimMannequinCommandHandler.cs
Outdated
Show resolved
Hide resolved
src/Octoshift/Commands/ReclaimMannequin/ReclaimMannequinCommandArgs.cs
Outdated
Show resolved
Hide resolved
src/Octoshift/Commands/ReclaimMannequin/ReclaimMannequinCommandArgs.cs
Outdated
Show resolved
Hide resolved
catch (Exception ex) | ||
{ | ||
throw new OctoshiftCliException($"Failed to lookup the login for current user", ex); | ||
} |
Check notice
Code scanning / CodeQL
Generic catch clause
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with this CodeQL warning, here. What does a failure look like? Would there be a permission error, for example? Keep in mind that GraphQL will return a 200 on failures related to the response, so we'll want to inspect the GraphQL "errors"
key. The errors that come back from GitHub should already be sanitized and helpful, so most of the hard stuff is probably already done for you: see if there's an "errors"
key, and if so, append the message to your own exception message! 👌
We're using _retryPolicy
here, so it should be robust against network failures, but we should still catch errors related to network issues. If you want to quickly functionally test this, you could redefine url
to something you know will not work, like https://my-local-broken-network
. The _retryPolicy
should kick in and exhaust its retries, then raise an exception related to the network failure.
Additionally, another thing we should be careful of is reporting the correct exceptions to the user. With this block, every exception will turn into a generic OctoshiftCliException
error. This means that if a user creates a friction issue with us and mentions the "Failed to lookup the login for current user" error, we will have no information about it by design, and it'd be difficult to help them.
It's probably better to handle most expected cases, like permissions errors being raised via GraphQL and some network-related errors, then go ahead and let the edge cases slip through as unhandled exceptions (in this scope). Then we'll get all the weird stuff displayed in the console and in the logs, even if they aren't following the happiest path of exception handling 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@synthead The OctoshiftCliException is set up so that we can pass the exception ("ex") as an inner exception
; which the user can view using the --verbose
option. :)
This is the pattern used on a few other functions within this class. If we want to get more specific with errors here, should this and other functions be cleaned up as part of a separate PR? 🤷♀️
As for "what does a failure" look like here; honestly, I don't think there really will be anything outside of a network error; I set up the catch to be cautious. Since we're just getting the user's login and that is associated with the PAT they are using, I don't think we should run into a permissions issue or anything else particularly funky.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking great! I have a few things for you to 👀 at! Gimme a shout if you'd like to pair!
public ConfirmationService() | ||
{ | ||
_writeToConsoleOut = msg => Console.WriteLine(msg); | ||
_writeToConsoleOut = (msg, outputColor) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we made outputColor
default to ConsoleColor.White
? That way, we only need to specify a color if we want to use something other than the default (implied ConsoleColor.White
).
Something like:
_writeToConsoleOut = (msg, outputColor) => | |
_writeToConsoleOut = (msg, outputColor = ConsoleColor.White) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem to be a c# feature (yet): https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/lambda-method-group-defaults#relevant-background
Unless we want to use preview
but I don't think it's worth using an unsupported feature.
src/Octoshift/Commands/ReclaimMannequin/ReclaimMannequinCommandHandler.cs
Outdated
Show resolved
Hide resolved
response = _readConsoleKey(); | ||
if (response != ConsoleKey.Enter) | ||
{ | ||
_writeToConsoleOut(""); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this to write a blank line to the output? If so, perhaps we also want to have msg
have an optional parameter that defaults to ""
?
_writeToConsoleOut = (msg = "", outputColor = ConsoleColor.White) =>
This way, for empty (white) lines, we can simply do:
_writeToConsoleOut();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as the other default value comment... I think it would be a great once this is implemented though!
"This doesn't seem to be a c# feature (yet): https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/lambda-method-group-defaults#relevant-background
Unless we want to use preview but I don't think it's worth using an unsupported feature."
catch (Exception ex) | ||
{ | ||
throw new OctoshiftCliException($"Failed to lookup the login for current user", ex); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with this CodeQL warning, here. What does a failure look like? Would there be a permission error, for example? Keep in mind that GraphQL will return a 200 on failures related to the response, so we'll want to inspect the GraphQL "errors"
key. The errors that come back from GitHub should already be sanitized and helpful, so most of the hard stuff is probably already done for you: see if there's an "errors"
key, and if so, append the message to your own exception message! 👌
We're using _retryPolicy
here, so it should be robust against network failures, but we should still catch errors related to network issues. If you want to quickly functionally test this, you could redefine url
to something you know will not work, like https://my-local-broken-network
. The _retryPolicy
should kick in and exhaust its retries, then raise an exception related to the network failure.
Additionally, another thing we should be careful of is reporting the correct exceptions to the user. With this block, every exception will turn into a generic OctoshiftCliException
error. This means that if a user creates a friction issue with us and mentions the "Failed to lookup the login for current user" error, we will have no information about it by design, and it'd be difficult to help them.
It's probably better to handle most expected cases, like permissions errors being raised via GraphQL and some network-related errors, then go ahead and let the edge cases slip through as unhandled exceptions (in this scope). Then we'll get all the weird stuff displayed in the console and in the logs, even if they aren't following the happiest path of exception handling 👍
switch (result.Errors[0].Message) | ||
{ | ||
case string a when a.Contains("is not an Enterprise Managed Users (EMU) organization"): | ||
_log.LogError("Failed to reclaim mannequins. The --skip-invitation flag is only available to EMU organizations."); | ||
return false; // Indicates we should stop parsing through the CSV | ||
default: | ||
_log.LogWarning($"Failed to reclaim {mannequinUser} ({mannequin.Id}) to {targetUser} ({targetUserId}): {result.Errors[0].Message}"); | ||
return true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way that we can handle this error without parsing human-readable error messages? Does the GraphQL response happen to include a key or something? If so, we should watch for that 👌
If not (and this is difficult to consume as an API user), we should probably file an issue on our end to make this easier to handle 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@synthead At this point, I don't think so.
This is where the error gets raised: https://github.com/github/github/blob/4ad31aba4642a3776068727dd578ca76e61911df/lib/platform/mutations/reattribute_mannequin_to_user.rb#L33
Is there an example of a key somewhere so I can see what you're envisioning?
…ub/gh-gei into reclaimation-error-handling
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍🏾 I think if there are any more non-blocking comments should create a new follow-up issue(s) to address feedback.
co-authored-by: Hannah Fishback <38845272+hfishback01@users.noreply.github.com>
co-authored-by: Hannah Fishback <38845272+hfishback01@users.noreply.github.com>
co-authored-by: Hannah Fishback <38845272+hfishback01@users.noreply.github.com>
Closes #6913
Closes #6914
Closes #1011
This PR addresses feedback from previous PR, including:
It also adds error handling and unit testing for different scenarios as outlined by screenshots below. I've also created an issue to update error message handling here when GH is updated:
--check for duplicate claimant in same issue errror
Updates console color for confirmation request also exits more gracefully on "n":
Errors handled:
Handles duplicates (both to same and different claimants):
User not admin to EMU org:
Other errors that were already handled but mentioned in the issue:
Mannequins already claimed:
Mannequins not part of target org
Claimant not part of target org:
--no-prompt
Used:Feature Flag not enabled:
ThirdPartyNotices.txt
(if applicable)