Skip to content

FIS error handling #911

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 30 commits into from
Oct 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
49708d2
Simplifying FirebaseInstallations class by adding listeners.
ankitaj224 Sep 25, 2019
a987647
Splitting multiple classes/interfaces into separate files.
ankitaj224 Sep 25, 2019
1d9b1ca
Addressing ciaran's comments to return same token if multiple getAuth…
ankitaj224 Sep 26, 2019
28d0d85
Addressing ciaran's comments to use a boolean to refresh auth token
ankitaj224 Sep 27, 2019
126e372
Cleaning up instrumented test file.
ankitaj224 Sep 27, 2019
c39cdb8
Ciaran's comments on synchronization and runnable.
ankitaj224 Sep 29, 2019
c21a59e
Ciaran's comments on synchronization and runnable.
ankitaj224 Sep 29, 2019
7a46ab0
Merge branch 'listeners' of github.com:firebase/firebase-android-sdk …
ankitaj224 Sep 29, 2019
314b3bb
Fixing precheck failures.
ankitaj224 Sep 30, 2019
715d427
Addressing Ciaran's comment on mock utils.
ankitaj224 Sep 30, 2019
bb322d0
Cleaning doRegistration method.
ankitaj224 Oct 1, 2019
1fd193b
Fixing nit comments.
ankitaj224 Oct 8, 2019
e2d3631
Addressing ciaran's comment on satisfying immediate getId response.
ankitaj224 Oct 8, 2019
e68e599
Fixing FISClient to correctly parse expiration timestamp. (#848)
ankitaj224 Oct 9, 2019
58c8efd
Updating getAuthToken to return creation timestamp (#884)
ankitaj224 Oct 9, 2019
56d1eab
Propagating the exceptions to the clients. (#856)
ankitaj224 Oct 10, 2019
c8367dc
Implemneting retry for FIS Client.
ankitaj224 Oct 10, 2019
72e335d
Merge branch 'listeners' of github.com:firebase/firebase-android-sdk …
ankitaj224 Oct 10, 2019
560fba1
Addressing ciaran's comments.
ankitaj224 Oct 10, 2019
3ac3da2
Merge branch 'fis_sdk' of github.com:firebase/firebase-android-sdk in…
ankitaj224 Oct 10, 2019
d9e5c22
Merge conflicts.
ankitaj224 Oct 10, 2019
f57137c
Nit fixes
ankitaj224 Oct 10, 2019
1d51edf
Extract FID from FIS createInstallation response (#888)
ankitaj224 Oct 14, 2019
0c0b9c2
Merge branch 'listeners' of github.com:firebase/firebase-android-sdk …
ankitaj224 Oct 14, 2019
0407614
Fixing verify format issue.
ankitaj224 Oct 14, 2019
2e7b881
1. Mark PersistedInstallationEntry status UNREGISTERED incase of 500 …
ankitaj224 Oct 14, 2019
615bdf4
Merge branch 'fis_sdk' of github.com:firebase/firebase-android-sdk in…
ankitaj224 Oct 14, 2019
dfa85e7
Addressing Ciaran & Rayo's comments.
ankitaj224 Oct 14, 2019
97b616c
Acceptiong Rayo's suggestion on code improvement.
ankitaj224 Oct 15, 2019
31d441a
Introducing responseCode to validate if the request was successful.
ankitaj224 Oct 15, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions firebase-installations/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ package com.google.firebase.installations.local {
method @Nullable public abstract String getAuthToken();
method public abstract long getExpiresInSecs();
method @Nullable public abstract String getFirebaseInstallationId();
method @Nullable public abstract String getFisError();
method @Nullable public abstract String getRefreshToken();
method @NonNull public abstract com.google.firebase.installations.local.PersistedInstallation.RegistrationStatus getRegistrationStatus();
method public abstract long getTokenCreationEpochInSecs();
Expand All @@ -61,6 +62,7 @@ package com.google.firebase.installations.local {
method @NonNull public abstract com.google.firebase.installations.local.PersistedInstallationEntry.Builder setAuthToken(@Nullable String);
method @NonNull public abstract com.google.firebase.installations.local.PersistedInstallationEntry.Builder setExpiresInSecs(long);
method @NonNull public abstract com.google.firebase.installations.local.PersistedInstallationEntry.Builder setFirebaseInstallationId(@NonNull String);
method @NonNull public abstract com.google.firebase.installations.local.PersistedInstallationEntry.Builder setFisError(@Nullable String);
method @NonNull public abstract com.google.firebase.installations.local.PersistedInstallationEntry.Builder setRefreshToken(@Nullable String);
method @NonNull public abstract com.google.firebase.installations.local.PersistedInstallationEntry.Builder setRegistrationStatus(@NonNull com.google.firebase.installations.local.PersistedInstallation.RegistrationStatus);
method @NonNull public abstract com.google.firebase.installations.local.PersistedInstallationEntry.Builder setTokenCreationEpochInSecs(long);
Expand All @@ -80,10 +82,11 @@ package com.google.firebase.installations.remote {
public abstract class InstallationResponse {
ctor public InstallationResponse();
method @NonNull public static com.google.firebase.installations.remote.InstallationResponse.Builder builder();
method @NonNull public abstract InstallationTokenResult getAuthToken();
method @NonNull public abstract String getFid();
method @NonNull public abstract String getRefreshToken();
method @NonNull public abstract String getUri();
method @Nullable public abstract InstallationTokenResult getAuthToken();
method @Nullable public abstract String getFid();
method @Nullable public abstract String getRefreshToken();
method @Nullable public abstract com.google.firebase.installations.remote.InstallationResponse.ResponseCode getResponseCode();
method @Nullable public abstract String getUri();
method @NonNull public abstract com.google.firebase.installations.remote.InstallationResponse.Builder toBuilder();
}

Expand All @@ -93,8 +96,14 @@ package com.google.firebase.installations.remote {
method @NonNull public abstract com.google.firebase.installations.remote.InstallationResponse.Builder setAuthToken(@NonNull InstallationTokenResult);
method @NonNull public abstract com.google.firebase.installations.remote.InstallationResponse.Builder setFid(@NonNull String);
method @NonNull public abstract com.google.firebase.installations.remote.InstallationResponse.Builder setRefreshToken(@NonNull String);
method @NonNull public abstract com.google.firebase.installations.remote.InstallationResponse.Builder setResponseCode(@NonNull com.google.firebase.installations.remote.InstallationResponse.ResponseCode);
method @NonNull public abstract com.google.firebase.installations.remote.InstallationResponse.Builder setUri(@NonNull String);
}

public enum InstallationResponse.ResponseCode {
enum_constant public static final com.google.firebase.installations.remote.InstallationResponse.ResponseCode OK;
enum_constant public static final com.google.firebase.installations.remote.InstallationResponse.ResponseCode SERVER_ERROR;
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.firebase.installations.FisAndroidTestConstants.DEFAULT_PERSISTED_INSTALLATION_ENTRY;
import static com.google.firebase.installations.FisAndroidTestConstants.INVALID_TEST_FID;
import static com.google.firebase.installations.FisAndroidTestConstants.SERVER_ERROR_INSTALLATION_RESPONSE;
import static com.google.firebase.installations.FisAndroidTestConstants.TEST_API_KEY;
import static com.google.firebase.installations.FisAndroidTestConstants.TEST_APP_ID_1;
import static com.google.firebase.installations.FisAndroidTestConstants.TEST_AUTH_TOKEN;
Expand Down Expand Up @@ -291,6 +292,32 @@ public void testGetId_PersistedInstallationOk_BackendError() throws Exception {
assertThat(updatedInstallationEntry).hasRegistrationStatus(RegistrationStatus.REGISTER_ERROR);
}

@Test
public void testGetId_ServerError_UnregisteredFID() throws Exception {
// Mocking server error on FIS createFirebaseInstallation, returns empty InstallationResponse
when(backendClientReturnsOk.createFirebaseInstallation(
Copy link
Contributor

Choose a reason for hiding this comment

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

where does this test stub a 500 error?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry for the confusion. Added a comment & changed the test name. Does this makes sense?

anyString(), anyString(), anyString(), anyString()))
.thenReturn(SERVER_ERROR_INSTALLATION_RESPONSE);

FirebaseInstallations firebaseInstallations =
new FirebaseInstallations(
executor, firebaseApp, backendClientReturnsOk, persistedInstallation, mockUtils);

Tasks.await(firebaseInstallations.getId());

PersistedInstallationEntry entryValue =
persistedInstallation.readPersistedInstallationEntryValue();
assertThat(entryValue).hasFid(TEST_FID_1);

// Waiting for Task that registers FID on the FIS Servers
executor.awaitTermination(500, TimeUnit.MILLISECONDS);

PersistedInstallationEntry updatedInstallationEntry =
persistedInstallation.readPersistedInstallationEntryValue();
assertThat(updatedInstallationEntry).hasFid(TEST_FID_1);
assertThat(updatedInstallationEntry).hasRegistrationStatus(RegistrationStatus.UNREGISTERED);
}

@Test
public void testGetId_PersistedInstallationError_BackendOk() throws InterruptedException {
FirebaseInstallations firebaseInstallations =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import com.google.firebase.installations.local.PersistedInstallationEntry;
import com.google.firebase.installations.remote.InstallationResponse;
import com.google.firebase.installations.remote.InstallationResponse.ResponseCode;

public final class FisAndroidTestConstants {
public static final String TEST_FID_1 = "cccccccccccccccccccccc";
Expand Down Expand Up @@ -55,6 +56,7 @@ public final class FisAndroidTestConstants {
.setTokenExpirationTimestamp(TEST_TOKEN_EXPIRATION_TIMESTAMP)
.setTokenCreationTimestamp(TEST_CREATION_TIMESTAMP_1)
.build())
.setResponseCode(ResponseCode.OK)
.build();

public static final InstallationTokenResult TEST_INSTALLATION_TOKEN_RESULT =
Expand All @@ -63,4 +65,7 @@ public final class FisAndroidTestConstants {
.setTokenExpirationTimestamp(TEST_TOKEN_EXPIRATION_TIMESTAMP)
.setTokenCreationTimestamp(TEST_CREATION_TIMESTAMP_1)
.build();

public static final InstallationResponse SERVER_ERROR_INSTALLATION_RESPONSE =
InstallationResponse.builder().setResponseCode(ResponseCode.SERVER_ERROR).build();
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.google.firebase.installations.local.PersistedInstallationEntry;
import com.google.firebase.installations.remote.FirebaseInstallationServiceClient;
import com.google.firebase.installations.remote.InstallationResponse;
import com.google.firebase.installations.remote.InstallationResponse.ResponseCode;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
Expand Down Expand Up @@ -218,12 +219,18 @@ private final void doRegistration() {
persistedInstallation.readPersistedInstallationEntryValue();

// New FID needs to be created
if (persistedInstallationEntry.isErrored() || persistedInstallationEntry.isNotGenerated()) {
if (persistedInstallationEntry.isNotGenerated()) {
String fid = utils.createRandomFid();
persistFid(fid);
persistedInstallationEntry = persistedInstallation.readPersistedInstallationEntryValue();
}

if (persistedInstallationEntry.isErrored()) {
throw new FirebaseInstallationsException(
persistedInstallationEntry.getFisError(),
FirebaseInstallationsException.Status.SDK_INTERNAL_ERROR);
}

triggerOnStateReached(persistedInstallationEntry);

// FID needs to be registered
Expand Down Expand Up @@ -262,6 +269,7 @@ private final void doRegistration() {
PersistedInstallationEntry errorInstallationEntry =
persistedInstallationEntry
.toBuilder()
.setFisError(e.getMessage())
.setRegistrationStatus(RegistrationStatus.REGISTER_ERROR)
.build();
persistedInstallation.insertOrUpdatePersistedInstallationEntry(errorInstallationEntry);
Expand Down Expand Up @@ -296,15 +304,17 @@ private Void registerAndSaveFid(PersistedInstallationEntry persistedInstallation
/*fid= */ persistedInstallationEntry.getFirebaseInstallationId(),
/*projectID= */ firebaseApp.getOptions().getProjectId(),
/*appId= */ getApplicationId());
persistedInstallation.insertOrUpdatePersistedInstallationEntry(
PersistedInstallationEntry.builder()
.setFirebaseInstallationId(installationResponse.getFid())
.setRegistrationStatus(RegistrationStatus.REGISTERED)
.setAuthToken(installationResponse.getAuthToken().getToken())
.setRefreshToken(installationResponse.getRefreshToken())
.setExpiresInSecs(installationResponse.getAuthToken().getTokenExpirationTimestamp())
.setTokenCreationEpochInSecs(creationTime)
.build());
if (installationResponse.getResponseCode() == ResponseCode.OK) {
persistedInstallation.insertOrUpdatePersistedInstallationEntry(
PersistedInstallationEntry.builder()
.setFirebaseInstallationId(installationResponse.getFid())
.setRegistrationStatus(RegistrationStatus.REGISTERED)
.setAuthToken(installationResponse.getAuthToken().getToken())
.setRefreshToken(installationResponse.getRefreshToken())
.setExpiresInSecs(installationResponse.getAuthToken().getTokenExpirationTimestamp())
.setTokenCreationEpochInSecs(creationTime)
.build());
}

} catch (FirebaseException exception) {
throw new FirebaseInstallationsException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ class Utils {
* #AUTH_TOKEN_EXPIRATION_BUFFER_IN_SECS}.
*/
public boolean isAuthTokenExpired(PersistedInstallationEntry persistedInstallationEntry) {
return persistedInstallationEntry.getTokenCreationEpochInSecs()
+ persistedInstallationEntry.getExpiresInSecs()
> currentTimeInSecs() + AUTH_TOKEN_EXPIRATION_BUFFER_IN_SECS;
return persistedInstallationEntry.isRegistered()
&& persistedInstallationEntry.getTokenCreationEpochInSecs()
+ persistedInstallationEntry.getExpiresInSecs()
> currentTimeInSecs() + AUTH_TOKEN_EXPIRATION_BUFFER_IN_SECS;
}

/** Returns current time in seconds. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ public abstract class PersistedInstallationEntry {

public abstract long getTokenCreationEpochInSecs();

@Nullable
public abstract String getFisError();

public boolean isRegistered() {
return getRegistrationStatus() == PersistedInstallation.RegistrationStatus.REGISTERED;
}
Expand Down Expand Up @@ -90,6 +93,9 @@ public abstract Builder setRegistrationStatus(
@NonNull
public abstract Builder setTokenCreationEpochInSecs(long value);

@NonNull
public abstract Builder setFisError(@Nullable String value);

@NonNull
public abstract PersistedInstallationEntry build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.google.android.gms.common.util.VisibleForTesting;
import com.google.firebase.FirebaseException;
import com.google.firebase.installations.InstallationTokenResult;
import com.google.firebase.installations.remote.InstallationResponse.ResponseCode;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
Expand Down Expand Up @@ -118,17 +119,21 @@ public InstallationResponse createFirebaseInstallation(
}

int httpResponseCode = httpsURLConnection.getResponseCode();
switch (httpResponseCode) {
case 200:
return readCreateResponse(httpsURLConnection);
case 500:
retryCount++;
break;
default:
throw new FirebaseException(readErrorResponse(httpsURLConnection));

if (httpResponseCode == 200) {
return readCreateResponse(httpsURLConnection);
}
// Usually the FIS server recovers from errors: retry one time before giving up.
if (httpResponseCode >= 500 && httpResponseCode < 600) {
retryCount++;
continue;
}

// Unrecoverable server response or unknown error
throw new FirebaseException(readErrorResponse(httpsURLConnection));
}
throw new FirebaseException(INTERNAL_SERVER_ERROR_MESSAGE);
// Return empty installation response with SERVER_ERROR response code after max retries
return InstallationResponse.builder().setResponseCode(ResponseCode.SERVER_ERROR).build();
} catch (IOException e) {
throw new FirebaseException(String.format(NETWORK_ERROR_MESSAGE, e.getMessage()));
}
Expand Down Expand Up @@ -219,15 +224,18 @@ public InstallationTokenResult generateAuthToken(
httpsURLConnection.addRequestProperty("Authorization", "FIS_v2 " + refreshToken);

int httpResponseCode = httpsURLConnection.getResponseCode();
switch (httpResponseCode) {
case 200:
return readGenerateAuthTokenResponse(httpsURLConnection);
case 500:
retryCount++;
break;
default:
throw new FirebaseException(readErrorResponse(httpsURLConnection));

if (httpResponseCode == 200) {
return readGenerateAuthTokenResponse(httpsURLConnection);
}
// Usually the FIS server recovers from errors: retry one time before giving up.
if (httpResponseCode >= 500 && httpResponseCode < 600) {
retryCount++;
continue;
}

// Unrecoverable server response or unknown error
throw new FirebaseException(readErrorResponse(httpsURLConnection));
}
throw new FirebaseException(INTERNAL_SERVER_ERROR_MESSAGE);
} catch (IOException e) {
Expand Down Expand Up @@ -283,7 +291,7 @@ private InstallationResponse readCreateResponse(HttpsURLConnection conn) throws
}
reader.endObject();

return builder.build();
return builder.setResponseCode(ResponseCode.OK).build();
}

// Read the response from the generateAuthToken FirebaseInstallation API.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,35 @@
package com.google.firebase.installations.remote;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.auto.value.AutoValue;
import com.google.firebase.installations.InstallationTokenResult;

@AutoValue
public abstract class InstallationResponse {

@NonNull
public enum ResponseCode {
// Returned on success
OK,
// An error occurred on the server while processing this request(temporary)
SERVER_ERROR
}

@Nullable
public abstract String getUri();

@NonNull
@Nullable
public abstract String getFid();

@NonNull
@Nullable
public abstract String getRefreshToken();

@NonNull
@Nullable
public abstract InstallationTokenResult getAuthToken();

@Nullable
public abstract ResponseCode getResponseCode();

@NonNull
public abstract Builder toBuilder();

Expand All @@ -56,6 +67,9 @@ public abstract static class Builder {
@NonNull
public abstract Builder setAuthToken(@NonNull InstallationTokenResult value);

@NonNull
public abstract Builder setResponseCode(@NonNull ResponseCode value);

@NonNull
public abstract InstallationResponse build();
}
Expand Down