Skip to content

Differentiate between user cancellation and actual errors #426

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

Merged
merged 22 commits into from
Dec 6, 2016
Merged

Differentiate between user cancellation and actual errors #426

merged 22 commits into from
Dec 6, 2016

Conversation

SUPERCILEX
Copy link
Collaborator

@SUPERCILEX SUPERCILEX commented Nov 28, 2016

@@ -34,7 +34,7 @@
import com.firebase.ui.auth.AuthUI;
import com.firebase.ui.auth.AuthUI.IdpConfig;
import com.firebase.ui.auth.IdpResponse;
import com.firebase.ui.auth.ui.ResultCodes;
import com.firebase.ui.auth.ResultCodes;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The library version should probably be bumped to 1.1.0 since this is slight breakage.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this would technically require a major version (if you're going by semver) since old code would break when updated.

Could we keep the .ui.ResultCodes class but have it be all @Deprecated constants that are just symlinks to the new class? Then it's fully backwards compatible.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Awesome idea!

PS: how does deprecation work? When the library hits 2.0, will we remove the deprecated stuff or are we stuck with it for life?

Copy link
Contributor

Choose a reason for hiding this comment

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

We can remove deprecated things after a major version change (if we want to). Or we can leave it for life. This will be the first example of deprecation in this library so there's no precedent for us, and every library does it a bit differently.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ok, thanks for the info! (I'm pretty sure we also deprecated setProviders(String... providers))

/**
* Sign in succeeded
**/
public static final int OK = Activity.RESULT_OK;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I decided to unify result codes and make ResultCodes a central place for those.

I decided to use the existing activity result codes to not break anything.

/**
* Sign in failed due to lack of network connection
**/
public static final int NO_NETWORK = 10;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I also removed the RESULT_ bit since the class is named ResultCodes and it looks nicer without the extra RESULT_.

/**
* An unknown error has occurred
**/
public static final int UNKNOWN_ERROR = 20;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Should we be more specific here? The errors finished with this code vary widely and we log each of them so I didn't think it was necessary. The only thing people want to know is if the user cancelled the sign in or an error occurred.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is fine for now, it would be nice to be more expressive in the long term but I don't want to overwhelm people with 50 error codes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's what I was thinking: we can expand later, but for now at least we have something to work with.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should now be moved to another file like ErrorCodes.java or something since it's no longer a "response code" which should only be codes returned to onActivityResult directly and not the codes contained by IdpResponse.

@SUPERCILEX
Copy link
Collaborator Author

I also added an isNewUser method to IdpResponse in this PR because I thought it would have something to do with ResultCodes. Turns out it didn't and I'm not quite sure why I thought that in the first place, but here it is, crammed into this PR.

@SUPERCILEX
Copy link
Collaborator Author

Turns out there's no way to know if an IDP is a new user so I changed the method name to isNewEmailUser. If I'm wrong, I'd love hear a solution!

/**
* @return true if the user was created in the sign in flow, false if it already existed.
*/
public boolean isNewEmailUser() {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should delay this feature because with a little more effort (which would be out of scope here) we could do a more complete job with the "new or existing user" problem.

At the time when we acquire the users email (either directly or through an IDP response) we could do a query for providers from email and use that to determine if it's a new or existing user. It comes at the cost of an extra request but at least it would be correct.

The other option is just to recommend that developers keep an index of known users in the Realtime Database, which is what ~90% of devs are doing anyway (for various reasons). We could even provide a FBUI helper function to manage this.

In any case, we should discuss it thoroughly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I learned something new today! I thought fetchProvidersForEmail only worked for email/password accounts.

I agree, since this is going to be much more complicated, I'll save it for another PR.

@@ -82,7 +83,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (requestCode == RC_CHOOSE_PHOTO) {
if (resultCode == RESULT_OK) {
if (resultCode == ResultCodes.OK) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems like an IntelliJ refactor bug, in this case I am not using AuthUI I want to see if the Camera returned Activity.RESULT_OK

@@ -204,8 +204,8 @@ startActivityForResult(

#####Response codes
The authentication flow only provides three response codes:
Copy link
Contributor

Choose a reason for hiding this comment

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

This sentence is no longer true ("only three response codes..."). Maybe we should just point to the ResponseCodes file here and rely on JavaDoc comments so we don't have to keep updating this.

@@ -69,7 +69,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) {

if (providerConfig == null) {
// we don't have a provider to handle this
finish(Activity.RESULT_CANCELED, new Intent());
finish(ResultCodes.UNKNOWN_ERROR, new Intent());
Copy link
Contributor

Choose a reason for hiding this comment

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

Changing all of the CANCELED to UNKNOWN_ERROR is still a breaking API change, as people checking for CANCELED in code will no longer see the same behavior.

This makes me think maybe we should simplify to three cases:

  • OK
  • CANCELED (with optional extras describing the error)
  • NO_NETWORK (just to keep backwards compat, otherwise I'd say we lump this into CANCELED)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I like that, what about getErrorCode in IdpResponse?

Copy link
Contributor

Choose a reason for hiding this comment

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

That seems like the right answer 👍

@SUPERCILEX SUPERCILEX changed the title Differentiate between user cancellation and actual errors [DONT MERGE] Differentiate between user cancellation and actual errors Nov 29, 2016
// user is signed in!
startActivity(new Intent(this, WelcomeBackActivity.class));
finish();
if (data == null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: let's check if (requestCode != ResultCodes.OK && data == null) here to more clearly indicate what happened.

@SUPERCILEX SUPERCILEX changed the title [DONT MERGE] Differentiate between user cancellation and actual errors Differentiate between user cancellation and actual errors Nov 29, 2016
Copy link
Collaborator Author

@SUPERCILEX SUPERCILEX left a comment

Choose a reason for hiding this comment

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

I extracted ErrorCodes so ResultCodes now only has the activity codes so should we just remove that class altogether or keep it in case we want to add another result code?

`ResultCodes.NO_NETWORK` if sign in failed due to a lack of network connectivity,
and `ResultCodes.UNKNOWN_ERROR` for all other errors.
Typically, the only recourse for most apps if sign in fails is to ask
the user to sign in again later, or proceed with anonymous sign-in if supported.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Here's the updated README

startActivity(SignedInActivity.createIntent(this, IdpResponse.fromResultIntent(data)));
finish();
return;
if (data != null) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What do you think of this way of handling the sign in response?

Copy link
Contributor

Choose a reason for hiding this comment

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

There are a few things here:

  • I'd like checking for RESULT_OK to be a top-level check. That's our contract, not the nullity of data.
  • When the resultCode is CANCELED we will have two quick snackbars right? One because the IdpResponse contains UNKNOWN_ERROR and one because it was canceled.

}
if (requestCode == RC_SIGN_IN) {
if (data != null) {
IdpResponse response = IdpResponse.fromResultIntent(data);
Copy link
Contributor

@samtstern samtstern Nov 29, 2016

Choose a reason for hiding this comment

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

How about:

if (requestCode == RC_SIGN_IN) {
  IdpResponse response = (data == null) ? null : IdpResponse.fromResultIntent(data);
  if (resultCode == ResultCodes.OK) {
    startActivity(SignedInActivity.createIntent(this, response));
    finish();
    return
  } else if (resultCode == ResultCodes.CANCELED) {
    // Canceled...
  } else if (response != null) {
    // All the error handling ...
  }

Copy link
Collaborator Author

@SUPERCILEX SUPERCILEX Nov 29, 2016

Choose a reason for hiding this comment

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

That's cleaner, but it doesn't work. Since we are now returning ResultCodes.CANCELED with an added error code, it's a little different. So how about this?

/**
* Sign in failed due to lack of network connection
**/
public static final int NO_NETWORK = 10;
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this one actually a result code and not an error? We still need to return this via onActivityResult in order to honor our previous API contract.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No, I don't think so. It should be an error code, but to keep backwards compatibility I'm returning it in SignInDelegate which the old ResultCodes points to.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah ok i see what you did here. So you're returning it in both places for SignInDelegate, with the recommendation being that from here on out people use the errorCode to make decisions when the result is not OK. That makes sense!

@samtstern
Copy link
Contributor

Everything LGTM. I need to figure out what the next milestone will look like before I merge this into master, we might want a dev branch instead.

@SUPERCILEX
Copy link
Collaborator Author

@samtstern Sounds good to me!

return resultIntent.getParcelableExtra(ExtraConstants.EXTRA_IDP_RESPONSE);
} else {
return null;
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Since fromResultIntent is annotated as @Nullable, I think this was the original intention.

@SUPERCILEX
Copy link
Collaborator Author

@samtstern I added 1 commit since you last approved my PR.

@samtstern
Copy link
Contributor

Your added commit looks good to me, thanks for flagging it.

See #418 (comment)

@SUPERCILEX SUPERCILEX changed the base branch from master to version-1.1.0-dev December 6, 2016 17:22
…codes

# Conflicts:
#	app/src/main/res/values/strings.xml
#	auth/src/main/java/com/firebase/ui/auth/ui/ResultCodes.java
#	auth/src/main/java/com/firebase/ui/auth/ui/accountlink/WelcomeBackIdpPrompt.java
#	auth/src/main/java/com/firebase/ui/auth/ui/email/EmailHintContainerActivity.java
#	auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailActivity.java
Signed-off-by: Alex Saveau <[email protected]>
@SUPERCILEX
Copy link
Collaborator Author

@samtstern looks like we're good to go!

@samtstern samtstern merged commit 44a4f27 into firebase:version-1.1.0-dev Dec 6, 2016
@SUPERCILEX SUPERCILEX deleted the error-codes branch December 6, 2016 19:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants