Skip to content

getId() implementation with instrumented tests. #703

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 17 commits into from
Aug 30, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
30cf6ed
getId() implementation with instrumented tests.
ankitaj224 Aug 13, 2019
d034bbb
Addressing rayo's comments.
ankitaj224 Aug 14, 2019
f8b468c
Addressing rayo's comments.
ankitaj224 Aug 14, 2019
8db7967
Merge branch 'ankita_fis' of github.com:firebase/firebase-android-sdk…
ankitaj224 Aug 14, 2019
b507a3c
Merge branch 'create_fis' of github.com:firebase/firebase-android-sdk…
ankitaj224 Aug 14, 2019
f5a7c05
Merge branch 'ankita_fis' of github.com:firebase/firebase-android-sdk…
ankitaj224 Aug 14, 2019
90cdbaa
Addresing comments to resoleve the following:
ankitaj224 Aug 16, 2019
0dfa987
Addressing Ciaran and Rayo's comments.
ankitaj224 Aug 26, 2019
a71a1f3
Addressing Ciaran's comments
ankitaj224 Aug 27, 2019
0a92e2d
Addressing Ciaran's comments
ankitaj224 Aug 27, 2019
4cc0cee
Merge branch 'create_fis' of github.com:firebase/firebase-android-sdk…
ankitaj224 Aug 28, 2019
f16db3f
Merge branch 'ankita_fis' of github.com:firebase/firebase-android-sdk…
ankitaj224 Aug 29, 2019
f8e75fd
Adding param comments and checking if registration status is valid.
ankitaj224 Aug 30, 2019
227bf3b
Correcting lint warning: uses-permission should be declared before
ankitaj224 Aug 30, 2019
4be3847
Adding custom assertThat with more readable assertions
ankitaj224 Aug 30, 2019
68acde5
Correcting instrumented tests to be reliable.
ankitaj224 Aug 30, 2019
16544a6
Merge branch 'ankita_fis' of github.com:firebase/firebase-android-sdk…
ankitaj224 Aug 30, 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
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.installations.local.PersistedFid;
import com.google.firebase.installations.local.PersistedFidEntryValue;
import com.google.firebase.installations.local.PersistedFidEntry;
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the difference between PersistedFid and PersistedFidEntry?

import com.google.firebase.installations.remote.FirebaseInstallationServiceClient;
import com.google.firebase.installations.remote.FirebaseInstallationServiceException;
import com.google.firebase.installations.remote.InstallationResponse;
Expand Down Expand Up @@ -106,7 +106,7 @@ public void testCreateFirebaseInstallation_PersistedFidOk_BackendOk() throws Exc

// No exception, means success.
assertThat(Tasks.await(firebaseInstallations.getId())).isNotEmpty();
PersistedFidEntryValue entryValue = persistedFid.readPersistedFidEntryValue();
PersistedFidEntry entryValue = persistedFid.readPersistedFidEntryValue();
assertThat(entryValue.getFirebaseInstallationId()).isNotEmpty();
assertThat(entryValue.getPersistedStatus()).isEqualTo(PersistedFid.PersistedStatus.REGISTERED);
}
Expand All @@ -127,7 +127,7 @@ public void testCreateFirebaseInstallation_PersistedFidOk_BackendError() throws
.isEqualTo(FirebaseInstallationsException.Status.SDK_INTERNAL_ERROR);
}

PersistedFidEntryValue entryValue = persistedFid.readPersistedFidEntryValue();
PersistedFidEntry entryValue = persistedFid.readPersistedFidEntryValue();
assertThat(entryValue.getFirebaseInstallationId()).isNotEmpty();
assertThat(entryValue.getPersistedStatus())
.isEqualTo(PersistedFid.PersistedStatus.REGISTER_ERROR);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ public void testReadCacheEntry_Null() {
public void testUpdateAndReadCacheEntry() throws Exception {
Copy link
Contributor

Choose a reason for hiding this comment

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

can you add some comments explaining what this is testing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. Thanks. PTAL.

assertTrue(
cache0.insertOrUpdatePersistedFidEntry(
PersistedFidEntryValue.builder()
PersistedFidEntry.builder()
.setFirebaseInstallationId(TEST_FID_1)
.setAuthToken(TEST_AUTH_TOKEN)
.setRefreshToken(TEST_REFRESH_TOKEN)
.setPersistedStatus(PersistedFid.PersistedStatus.UNREGISTERED)
.setTokenCreationEpochInSecs(TEST_CREATION_TIMESTAMP_1)
.setExpiresInSecs(TEST_TOKEN_EXPIRATION_TIMESTAMP)
.build()));
PersistedFidEntryValue entryValue = cache0.readPersistedFidEntryValue();
PersistedFidEntry entryValue = cache0.readPersistedFidEntryValue();
assertThat(entryValue.getFirebaseInstallationId()).isEqualTo(TEST_FID_1);
assertThat(entryValue.getAuthToken()).isEqualTo(TEST_AUTH_TOKEN);
assertThat(entryValue.getRefreshToken()).isEqualTo(TEST_REFRESH_TOKEN);
Expand All @@ -96,7 +96,7 @@ public void testUpdateAndReadCacheEntry() throws Exception {

assertTrue(
cache0.insertOrUpdatePersistedFidEntry(
PersistedFidEntryValue.builder()
PersistedFidEntry.builder()
.setFirebaseInstallationId(TEST_FID_1)
.setAuthToken(TEST_AUTH_TOKEN)
.setRefreshToken(TEST_REFRESH_TOKEN)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.FirebaseApp;
import com.google.firebase.installations.local.PersistedFid;
import com.google.firebase.installations.local.PersistedFidEntryValue;
import com.google.firebase.installations.local.PersistedFidEntry;
import com.google.firebase.installations.remote.FirebaseInstallationServiceClient;
import com.google.firebase.installations.remote.FirebaseInstallationServiceException;
import com.google.firebase.installations.remote.InstallationResponse;
Expand Down Expand Up @@ -147,7 +147,7 @@ String getName() {
*
* Persisted Fid exists and is in Unregistered state
* |
* Register the Fid on FIS backened and update the PersistedFidEntry
* Register the Fid on FIS Server and update the PersistedFidEntry
* |
* return the Persisted Fid
*
Expand All @@ -156,53 +156,47 @@ String getName() {
@WorkerThread
private String getFirebaseInstallationId() throws FirebaseInstallationsException {

PersistedFidEntryValue persistedFidEntryValue = persistedFid.readPersistedFidEntryValue();
PersistedFidEntry persistedFidEntry = persistedFid.readPersistedFidEntryValue();

if (persistedFidDoesNotExistsOrInErrorState(persistedFidEntryValue)) {
return createFidAndPersistFidEntry();
if (persistedFidExistsAndRegistered(persistedFidEntry)) {
return persistedFidEntry.getFirebaseInstallationId();
}

if (persistedFidExistsAndRegistered(persistedFidEntryValue)) {
return persistedFidEntryValue.getFirebaseInstallationId();
if (persistedFidMissingOrInErrorState(persistedFidEntry)) {
return createFidAndPersistFidEntry();
}

if (persistedFidExistsAndUnregistered(persistedFidEntryValue)) {
registerAndSaveFid(persistedFidEntryValue.getFirebaseInstallationId());
return persistedFidEntryValue.getFirebaseInstallationId();
if (persistedFidExistsAndUnregistered(persistedFidEntry)) {
Tasks.call(executor, () -> registerAndSaveFid(persistedFidEntry.getFirebaseInstallationId()));
return persistedFidEntry.getFirebaseInstallationId();
}

return null;
Copy link
Contributor

Choose a reason for hiding this comment

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

How can this method ever return NULL?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If delete() is called and there is no network availability, we mark registration status as deleted. In that case, it returns NULL.

Is that not right?

Copy link
Contributor Author

@ankitaj224 ankitaj224 Aug 21, 2019

Choose a reason for hiding this comment

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

@andirayo Can you please check this? I think the issue is, we have introduced a state DELETED as per fis-android-sdk-design doc. But from your comments on my doc, seems like you don't want the DELETED state.

We can change the above function as :

@workerthread

private String getFirebaseInstallationId() throws FirebaseInstallationsException {

PersistedFidEntry persistedFidEntry = persistedFid.readPersistedFidEntryValue();

if (persistedFidExistsAndRegistered(persistedFidEntry)) {
  return persistedFidEntry.getFirebaseInstallationId();
} else  if (persistedFidExistsAndUnregistered(persistedFidEntry)) {
  Tasks.call(executor, () -> registerAndSaveFid(persistedFidEntry.getFirebaseInstallationId()));
  return persistedFidEntry.getFirebaseInstallationId();
}
  return createFidAndPersistFidEntry();
}

Wdyt?

Copy link
Contributor

Choose a reason for hiding this comment

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

Except for the "else if" in there (an else is not necessary after you "return" in the if-statement, I like it better yes.

How about
// not registered with FIS yet
if (fidExistsAndUnregistered(persistedFidEntry)) {
Tasks.call(executor, () -> registerAndSaveFid(persistedFidEntry.getFirebaseInstallationId()));
}
// return FID from in local storage
if (fidExists(persistedFidEntry)) {
return persistedFidEntry.getFirebaseInstallationId();
}

return createFidAndPersistFidEntry();

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

what do you all think about something like this? I haven't checked that it'll compile, but it seems to clearly express what we're trying to do:

  1. return the existing fid if it's there
  2. create one if it didn't exist
  3. trigger a background registration if it wasn't previously registered
public Task<FID> getFid() {
  return getExistingFid()
    .continueWith(orElse(this::createAndPersistNewFid))
    .onSuccessTask(this::registerFidIfNecessary);
}

public static <F, T> Continuation<F, T> orElse(Supplier<T> supplier) {
  return t -> {
    if (t.isSuccessful()) { return t.getResult(); }
    return supplier.get();
  };
}

private FID createAndPersistNewFid() {
  FID fid = Utils.createRandomFid();
  persist(fid);
  return fid;
}

private void registerFidIfNecessary(FID fid) {
  // do network stuff
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ciarand I tried incorporating the changes you mentioned below:

  @RequiresApi(api = Build.VERSION_CODES.N)
  private Task<String> getFid() {
    return Tasks.call(executor, this::getExistingFid)
        .continueWith(orElse(new NewFidSupplier()))
        .onSuccessTask(this::registerFidIfNecessary);
  }

  private PersistedFidEntry getExistingFid() throws FirebaseInstallationsException {
    PersistedFidEntry persistedFidEntry = persistedFid.readPersistedFidEntryValue();
    if (persistedFidMissingOrInErrorState(persistedFidEntry)) {
      throw new FirebaseInstallationsException(
          "Failed to get existing fid.", FirebaseInstallationsException.Status.CLIENT_ERROR);
    }
    return persistedFidEntry;
  }

  @RequiresApi(api = Build.VERSION_CODES.N)
  @NonNull
  public static <F, T> Continuation<F, T> orElse(@NonNull Supplier<T> supplier) {
    return t -> {
      if (t.isSuccessful()) {
        return (T) t.getResult();
      }
      return supplier.get();
    };
  }

  @RequiresApi(api = Build.VERSION_CODES.N)
  class NewFidSupplier implements Supplier<PersistedFidEntry> {

    @Override
    public PersistedFidEntry get() {
      return createAndPersistNewFid();
    }
  }

  private PersistedFidEntry createAndPersistNewFid() {
    String fid = Utils.createRandomFid();
    try {
      persistFid(fid);
      PersistedFidEntry persistedFidEntry = persistedFid.readPersistedFidEntryValue();
      return persistedFidEntry;
    } catch (FirebaseInstallationsException e) {
      // Exception handling
    }
    return PersistedFidEntry.builder().build();
  }

  private void persistFid(String fid) throws FirebaseInstallationsException {
    boolean firstUpdateCacheResult =
        persistedFid.insertOrUpdatePersistedFidEntry(
            PersistedFidEntry.builder()
                .setFirebaseInstallationId(fid)
                .setRegistrationStatus(PersistedFid.RegistrationStatus.UNREGISTERED)
                .build());

    if (!firstUpdateCacheResult) {
      throw new FirebaseInstallationsException(
          "Failed to update client side cache.",
          FirebaseInstallationsException.Status.CLIENT_ERROR);
    }
  }

  private Task<String> registerFidIfNecessary(PersistedFidEntry persistedFidEntry) {
    if (persistedFidEntry.getRegistrationStatus() != PersistedFid.RegistrationStatus.REGISTERED) {
      Tasks.call(executor, () -> registerAndSaveFid(persistedFidEntry));
    }
    return Tasks.forResult(persistedFidEntry.getFirebaseInstallationId());
  }

  /** Registers the created Fid with FIS Servers and update the shared prefs. */
  private Void registerAndSaveFid(PersistedFidEntry persistedFidEntry)
      throws FirebaseInstallationsException {
    // network call
  }

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 have 2 concerns:

  • Supplier get call requires API level 24, Is this right way of handling it?
  • Exception handling by the Supplier while persisting the newly created fid

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah we can't use the standard java.util.functional.Supplier interface. We can just define one in the parent class:

interface Supplier<T> {
  T get() throws Exception;
}

When you say "Exception handling by the Supplier while persisting the newly created fid" do you mean you're worried that you can't propagate exceptions or you're worried that they will be propagated?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated. Thanks for the comments.

Copy link
Contributor

Choose a reason for hiding this comment

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

mark it @Nullable if it's nullable

}

private static boolean persistedFidDoesNotExistsOrInErrorState(
PersistedFidEntryValue persistedFidEntryValue) {
return persistedFidEntryValue == null
|| persistedFidEntryValue.getPersistedStatus()
== PersistedFid.PersistedStatus.REGISTER_ERROR;
private static boolean persistedFidMissingOrInErrorState(PersistedFidEntry persistedFidEntry) {
return persistedFidEntry == null
|| persistedFidEntry.getPersistedStatus() == PersistedFid.PersistedStatus.REGISTER_ERROR;
}

private static boolean persistedFidExistsAndRegistered(
PersistedFidEntryValue persistedFidEntryValue) {
return persistedFidEntryValue != null
&& persistedFidEntryValue.getPersistedStatus() == PersistedFid.PersistedStatus.REGISTERED;
private static boolean persistedFidExistsAndRegistered(PersistedFidEntry persistedFidEntry) {
return persistedFidEntry != null
&& persistedFidEntry.getPersistedStatus() == PersistedFid.PersistedStatus.REGISTERED;
}

private static boolean persistedFidExistsAndUnregistered(
PersistedFidEntryValue persistedFidEntryValue) {
return persistedFidEntryValue != null
&& persistedFidEntryValue.getPersistedStatus() == PersistedFid.PersistedStatus.UNREGISTERED;
private static boolean persistedFidExistsAndUnregistered(PersistedFidEntry persistedFidEntry) {
return persistedFidEntry != null
&& persistedFidEntry.getPersistedStatus() == PersistedFid.PersistedStatus.UNREGISTERED;
}

private String createFidAndPersistFidEntry() throws FirebaseInstallationsException {
String fid = Utils.createRandomFid();

boolean firstUpdateCacheResult =
persistedFid.insertOrUpdatePersistedFidEntry(
PersistedFidEntryValue.builder()
PersistedFidEntry.builder()
.setFirebaseInstallationId(fid)
.setPersistedStatus(PersistedFid.PersistedStatus.UNREGISTERED)
.setTokenCreationEpochInSecs(0)
.setExpiresInSecs(0)
.build());

if (!firstUpdateCacheResult) {
Expand All @@ -211,18 +205,17 @@ private String createFidAndPersistFidEntry() throws FirebaseInstallationsExcepti
FirebaseInstallationsException.Status.CLIENT_ERROR);
}

registerAndSaveFid(fid);

Tasks.call(executor, () -> registerAndSaveFid(fid));
return fid;
}

/**
* Registers the created Fid with FIS Servers if the Network is available and update the shared
* prefs.
*/
private void registerAndSaveFid(String fid) throws FirebaseInstallationsException {
private Void registerAndSaveFid(String fid) throws FirebaseInstallationsException {
if (!Utils.isNetworkAvailable(firebaseApp.getApplicationContext())) {
return;
return null;
}

try {
Expand All @@ -236,7 +229,7 @@ private void registerAndSaveFid(String fid) throws FirebaseInstallationsExceptio
getApplicationId());

persistedFid.insertOrUpdatePersistedFidEntry(
PersistedFidEntryValue.builder()
PersistedFidEntry.builder()
.setFirebaseInstallationId(fid)
.setPersistedStatus(PersistedFid.PersistedStatus.REGISTERED)
.setAuthToken(installationResponse.getAuthToken().getToken())
Expand All @@ -248,14 +241,13 @@ private void registerAndSaveFid(String fid) throws FirebaseInstallationsExceptio

} catch (FirebaseInstallationServiceException exception) {
persistedFid.insertOrUpdatePersistedFidEntry(
PersistedFidEntryValue.builder()
PersistedFidEntry.builder()
.setFirebaseInstallationId(fid)
.setPersistedStatus(PersistedFid.PersistedStatus.REGISTER_ERROR)
.setTokenCreationEpochInSecs(0)
.setExpiresInSecs(0)
.build());
throw new FirebaseInstallationsException(
exception.getMessage(), FirebaseInstallationsException.Status.SDK_INTERNAL_ERROR);
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

/**
* A layer that locally persists a few Firebase Installation attributes on top the Firebase
* Installation backend API.
* Installation API.
*/
public class PersistedFid {
Copy link
Contributor

Choose a reason for hiding this comment

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

How about calling this LocalStorage or FisStorage or FirebaseInstallationsStorage?

// Status of each presisted fid entry
Expand Down Expand Up @@ -63,7 +63,7 @@ public PersistedFid(@NonNull FirebaseApp firebaseApp) {
}

@Nullable
public synchronized PersistedFidEntryValue readPersistedFidEntryValue() {
public synchronized PersistedFidEntry readPersistedFidEntryValue() {
String fid = prefs.getString(getSharedPreferencesKey(FIREBASE_INSTALLATION_ID_KEY), null);
int status = prefs.getInt(getSharedPreferencesKey(PERSISTED_STATUS_KEY), -1);
String authToken = prefs.getString(getSharedPreferencesKey(AUTH_TOKEN_KEY), null);
Expand All @@ -76,7 +76,7 @@ public synchronized PersistedFidEntryValue readPersistedFidEntryValue() {
return null;
}

return PersistedFidEntryValue.builder()
return PersistedFidEntry.builder()
.setFirebaseInstallationId(fid)
.setPersistedStatus(PersistedStatus.values()[status])
.setAuthToken(authToken)
Expand All @@ -88,7 +88,7 @@ public synchronized PersistedFidEntryValue readPersistedFidEntryValue() {

@NonNull
public synchronized boolean insertOrUpdatePersistedFidEntry(
@NonNull PersistedFidEntryValue entryValue) {
@NonNull PersistedFidEntry entryValue) {
SharedPreferences.Editor editor = prefs.edit();
editor.putString(
getSharedPreferencesKey(FIREBASE_INSTALLATION_ID_KEY),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
import com.google.auto.value.AutoValue;

/**
* This class represents a cache entry value in {@link PersistedFid}, which contains a Firebase
* instance id and the cache status of this entry.
* This class represents a persisted fid entry in {@link PersistedFid}, which contains a few
* Firebase Installation attributes and the persisted status of this entry.
*/
@AutoValue
public abstract class PersistedFidEntryValue {
public abstract class PersistedFidEntry {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please feel free to ignore this comment.
Maybe, it's easier to discuss in person and just submit this PR with this name.
I'm just wondering:
Why can't we just call this "FirebaseInstallation"?
(or "PersistedFirebaseInstallation" in case you want to make it obvious that it's received from local storage)
Let's discuss in person after this is submitted.


@NonNull
public abstract String getFirebaseInstallationId();
Expand All @@ -44,10 +44,12 @@ public abstract class PersistedFidEntryValue {
@NonNull
public abstract Builder toBuilder();

/** Returns a default Builder object to create an InstallationResponse object */
/** Returns a default Builder object to create an PersistedFidEntry object */
@NonNull
public static PersistedFidEntryValue.Builder builder() {
return new AutoValue_PersistedFidEntryValue.Builder();
public static PersistedFidEntry.Builder builder() {
return new AutoValue_PersistedFidEntry.Builder()
.setTokenCreationEpochInSecs(0)
.setExpiresInSecs(0);
}

@AutoValue.Builder
Expand All @@ -71,6 +73,6 @@ public abstract static class Builder {
public abstract Builder setTokenCreationEpochInSecs(long value);

@NonNull
public abstract PersistedFidEntryValue build();
public abstract PersistedFidEntry build();
}
}