This repository has been archived by the owner on Oct 12, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 22
Friendly Provider determination #21
Merged
Merged
Changes from 17 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
ccc3e54
ProviderScanner interface & UserAgentImpl stubs
dc6bd62
Extract scanProviders & describeUnmetRequirements
ff1b161
Move join() to new StringHelper class
1f05e3e
Add null-aware StringHelper.equal() method
19719d8
Introduce constant for future re-use
f5d72d4
Collect candidate Provider instances in ctor
1c8516b
Barely implement getUnmetProviderRequirements()
7cf7b06
Initial implementation of findCompatibleProvider()
c03e4b8
Add caching to avoid unnecessary scans
0d09fc0
Verify number of checks in existing tests
deef682
Verify 2 identical calls to find mean only 1 check
2d58dc5
getUnmetProviderRequirements should have >= 1 scan
a88c564
Implement rest of ProviderScanner interface
462c4cc
Extracted throwUnsupported() method
d731ba0
Update test that was just exercising scanProviders
1bcfcdf
Update test to be more precise
329cb53
Switch to new implementation & delete dead code
b7db17f
Fixed missing compatibility check in new methods
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
62 changes: 62 additions & 0 deletions
62
oauth2-useragent-core/src/main/java/com/microsoft/alm/oauth2/useragent/ProviderScanner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// Copyright (c) Microsoft. All rights reserved. | ||
// Licensed under the MIT license. See License.txt in the project root. | ||
|
||
package com.microsoft.alm.oauth2.useragent; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
|
||
public interface ProviderScanner { | ||
|
||
/** | ||
* Scans the {@link Provider} implementations, checks their requirements | ||
* and tries to find one that will work, preferring the one named by the | ||
* {@code userAgentProvider} property, if it was set. | ||
* | ||
* @return a {@link Provider} that was deemed compatible; | ||
* {@code null} if no compatible providers were found. | ||
*/ | ||
Provider findCompatibleProvider(); | ||
|
||
/** | ||
* Scans the {@link Provider} implementations, checks their requirements | ||
* and tries to find one that will work, preferring the one whose | ||
* name was provided. | ||
* | ||
* @param userAgentProvider the name of the preferred provider or | ||
* {@code null} if there's no preference | ||
* @return a {@link Provider} that was deemed compatible; | ||
* {@code null} if no compatible providers were found. | ||
*/ | ||
Provider findCompatibleProvider(final String userAgentProvider); | ||
|
||
/** | ||
* Determines unmet requirements (if any) for each available {@link Provider} implementation. | ||
* | ||
* @return a map of {@link Provider} instances to their list of unmet requirements. | ||
* If a {@link Provider} had all its requirements met, it will not be part of the map. | ||
* An empty map means all providers are compatible. | ||
*/ | ||
Map<Provider, List<String>> getUnmetProviderRequirements(); | ||
|
||
/** | ||
* Calls {@link #findCompatibleProvider()} and | ||
* returns whether a compatible {@link Provider} was found. | ||
* | ||
* @return {@code true} if a compatible provider was found; | ||
* {@code false} otherwise. | ||
*/ | ||
boolean hasCompatibleProvider(); | ||
|
||
/** | ||
* Calls {@link #findCompatibleProvider(String)} and | ||
* returns whether a compatible {@link Provider} was found. | ||
* | ||
* @param userAgentProvider the name of the preferred provider or | ||
* {@code null} if there's no preference | ||
* @return {@code true} if a compatible provider was found; | ||
* {@code false} otherwise. | ||
*/ | ||
boolean hasCompatibleProvider(final String userAgentProvider); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
...2-useragent-core/src/main/java/com/microsoft/alm/oauth2/useragent/utils/StringHelper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// Copyright (c) Microsoft. All rights reserved. | ||
// Licensed under the MIT license. See License.txt in the project root. | ||
|
||
package com.microsoft.alm.oauth2.useragent.utils; | ||
|
||
public class StringHelper { | ||
|
||
public static boolean equal(final String s1, final String s2) { | ||
if (s1 == null) { | ||
return s2 == null; | ||
} | ||
return s1.equals(s2); | ||
} | ||
|
||
public static String join(final String separator, final String[] value) { | ||
if (value == null) | ||
throw new IllegalArgumentException("value is null"); | ||
|
||
// "If separator is null, an empty string (String.Empty) is used instead." | ||
final String sep = separator == null ? "" : separator; | ||
|
||
final StringBuilder result = new StringBuilder(); | ||
|
||
if (value.length > 0) { | ||
result.append(value[0] == null ? "" : value[0]); | ||
for (int i = 1; i < value.length; i++) { | ||
result.append(sep); | ||
result.append(value[i] == null ? "" : value[i]); | ||
} | ||
} | ||
|
||
return result.toString(); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Let me know if my understanding isn't right.
According to the Javadoc
findCompatibleProvider(final String userAgentProvider)
will find a provider that works, only preferring the provider specified asuserAgentProvider
. However in this case, wouldn't this always returnuserAgentProvider
regardless? For example, if I prefer JavaFX provider, I could always make a callfindCompatibleProvider(javaFxProvider)
and I will always get that provider, even though on some platform it doesn't work? From reading the Javadoc, I was under the impression that it will check if JavaFXProvider meets all the requirement, and only return it when it works.Also another general comment, how expensive are those checks? It seems we are adding many states to UserAgentImpl because of this change, I am wondering if the checks are cheap, can we make it stateless? We could just call
scanProvider()
every time and check thedestinationUnmetRequirements
? It probably will make the code easier to read.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.
You're correct: I preserved the override behaviour but the documentation to
findCompatibleProvider()
makes it sound like a requirements check will always be performed.It seems to me that I can preserve the existing behaviour (while also making my documentation accurate) by adding a
checkOverrideIsCompatible
parameter to thedetermineProvider()
method, such thatencode()
will supplyfalse
andfindCompatibleProvider()
will supplytrue
.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.
Given the recently-added issue #22, it appears the
checkRequirements()
for JavaFX will most likely need to try to instantiate aWebView
, making the check expensive. I suspect an SWT provider might also need to do the same, given that some third-party dependencies sometimes need to be installed separately.When I first wrote
checkRequirements()
anddetermineProvider()
, there didn't seem to be any fixes you could make without restarting the process.For example:
Otherwise, once you're deemed compatible (or incompatible), it's unlikely to stop (or start) working, so there's no need to keep checking.
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.
(Using my personal account since I don't want to do 2FA)
I personally prefer not to complicate this further. What I had in mind was a simple check that the consumer calls and can get a true / false decision. Everything else is icing on the cake - I don't think they are essential to my use case.
I will leave it up to you to decide what kind of checks you provide, but I don't oppose just change the Javadoc to make the description accurate. :)
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.
It's understood that there is no need to keep checking, the comment is more about code structure -- remove states IMHO will make the code easier to read.
"Expensive" is relative, the process we are about to invoke is a user interaction, which could run in minutes. As long as the check is tolerable (sub second?), it may not matter that much.
Another option is instead of "true/false", how about determineProvider returns the compatible provider (null to signify no provider is compatible), and then consumer can pass this
Provider
torequestAuthorizationCode
, or create a new function namedrequestAuthorizationCodeWithProvider
and just use it. This way you might be able to avoid storing state?