Skip to content
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

Refactored WwwAuthenticateParameters #2907

Merged
merged 18 commits into from
Oct 4, 2021
Merged

Refactored WwwAuthenticateParameters #2907

merged 18 commits into from
Oct 4, 2021

Conversation

abatishchev
Copy link
Contributor

@abatishchev abatishchev commented Sep 28, 2021

Changes proposed in this request

  • Added optional cancellationToken to CreateFromResourceResponseAsync(string resourceUri, CancellationToken cancellationToken = default)
  • Used is null, is object for null checks
  • Used string.Equals() with StringComparison.OrdinalIgnoreCase for string comparison
  • Removed no-op try/catch that does nothing but erases the exception stack trace
  • Added overloads:
    • CreateFromResourceResponseAsync(HttpClient httpClient, string resourceUri, CancellationToken cancellationToken = default)
    • CreateFromResourceResponseAsync(IMsalHttpClientFactory httpClientFactory, string resourceUri, CancellationToken cancellationToken = default)

Testing

  • Refactored unit tests
  • Added integration test to call ARM

Performance impact

Will slightly improve the performance of the helper methods.

abatishchev and others added 3 commits September 28, 2021 10:05
- Used `is null`, `is object` for null checks
- Used `string.Equals()` with `StringComparison.OrdinalIgnoreCase` for string comparison
- Removed dummy `try/catch` that does nothing but erases the exception stack trace
@jmprieur
Copy link
Contributor

@abatishchev will you update unit tests?

@@ -32,7 +33,7 @@ public IEnumerable<string> Scopes
{
get
{
if (_scopes != null)
if (_scopes is object)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not seen this type of check before. I am curious - how does it differ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a new feature in C# 7.0, part of added pattern matching. It bypasses any potential operator overloads (for either == and/or !=) and directly emits the intended IL for null/not null comparison.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: While this is definitely interesting, these checks obfuscate the fact that we are checking for null here. The word "is" usually implies that you are checking if an object is of a certain type, not whether or not it is null so this may be confusing to someone who is not aware of this feature and they may end up adding the null check in addition to this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it's a (relatively) new feature and might be confusing. As any other new feature :)

There are some potential benefits in using it, plus many most popular OSS projects have switched to using it too.

So here's our options:

  1. Continue to use != null and == null
  2. Use is object and is null (my preference)
  3. Use is not null and is null (I personally find the former the least readable)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like option 1 or 3.
Thoughts @bgavrilMS?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very interesting problem. It looks like "obj is null" is something we can do today. But "object is not null" is a C# 9.0 feature, which we don't currently use (but we probably should).

I agree that "a is object" is a bit counter-intuitive.

According to https://stackoverflow.com/questions/40676426/what-is-the-difference-between-x-is-null-and-x-null it looks like the compiler (Roslyn) was actually modified so that it emits the same kind of logic as long as == and != are not overloaded (I believe we haven't overloaded them for any object in MSAL). '

So maybe let's keep on using option 1 for consistency with MSAL, and separately we can look at moving to C# 9 and adding some analyzers for this kind of problem. Any changes should be consistent across the lib.

Copy link
Member

@bgavrilMS bgavrilMS Oct 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue tracking the analyzer work #2921

@abatishchev
Copy link
Contributor Author

@jmprieur sorry, rushed to open a pull request. Working on adding/updating tests.

@abatishchev
Copy link
Contributor Author

@bgavrilMS, @jmprieur all tests have passed. I think it's ready for a re-review. thanks!

{
if (httpClientFactory is null)
{
throw new ArgumentException($"'{nameof(httpClientFactory)}' cannot be null.", nameof(httpClientFactory));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: We typically use ArgumentNullException for these types of checks on the api surface.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I noticed that too. Let me explain.

The only already existing check is for resourceId for not null or empty or white space. And it throws just ArgumentException. Some people may argue that if the input is an empty string the code can't throw ArgumentNullException because it's not null. But I personally think that's nitpicking and matters less than readable and consistent code.

In this PR I'm adding two more checks, both are for reference types so I'm just checking for not null.
And the caveat is in the order of parameters: ArgumentException(message, paramName) vs ArgumentNullException(paramName, message). Why the inconsistency is a question to BCL. So in our case the code looks little bit more messy and less readable.

So what I would do is to throw ArgumentNullException in all checks. What you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we do that as well in most places, i.e. throw ArgumentNullException for empty string.

Strings should really not be nullable :), but unfortunately we are not a library that uses the nullable references feature, so we have to live with these small inconsistencies.

Copy link
Member

@trwalke trwalke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit pick and a comment about the is object check

#2907 (comment)

@pmaytak pmaytak added this to In Progress in MSAL.NET (legacy) via automation Oct 2, 2021
@bgavrilMS
Copy link
Member

@abatishchev - I made 2 small changes as per @trwalke 's comments and I'm merging this, so as to be able to review your other PR with the tenant id.

@bgavrilMS bgavrilMS merged commit 76d0c0d into master Oct 4, 2021
@bgavrilMS bgavrilMS deleted the alexbat/www-auth-1 branch October 4, 2021 10:45
@bgavrilMS bgavrilMS moved this from In Progress to Fixed in MSAL.NET (legacy) Oct 4, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

None yet

4 participants