Skip to content

Firebase Storage Component Registration#277

Merged
schmidt-sebastian merged 6 commits intomasterfrom
mrschmidt-storagecomponents
Mar 16, 2019
Merged

Firebase Storage Component Registration#277
schmidt-sebastian merged 6 commits intomasterfrom
mrschmidt-storagecomponents

Conversation

@schmidt-sebastian
Copy link
Contributor

@schmidt-sebastian schmidt-sebastian commented Mar 13, 2019

This definitely falls under the umbrella "I don't know what I am doing". But here we go...

This follows the pattern in Firestore to register the component for Firebase Storage. It changes FirebaseStorage to only ever work with a single app, but keeps the option to get different instances for different storage buckets.

I also added getInstanceImpl as a valid name for the FirebaseApp.get() call, as I didn't want to add yet another getInstance() overload (which would result in an ambiguous null cast).

So what does everyone think? :)

@googlebot googlebot added the cla: yes Override cla label Mar 13, 2019
Copy link
Member

@vkryachko vkryachko left a comment

Choose a reason for hiding this comment

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

@schmidt-sebastian Awesome! Thanks for picking it up.

One important thing is that we also need storage to stop integrating with Auth via FirebaseApp(I think it uses the deprecated #getToken(), but maybe something else as well) and start using firebase-auth-interop's InternalAuthProvider like e.g. firestore does:

Feel free to punt it to a separate PR.

}
checkNotNull(app, "Provided FirebaseApp must not be null.");
FirebaseStorageComponent component = app.get(FirebaseStorageComponent.class);
checkNotNull(component, "Firestore component is not present.");
Copy link
Member

Choose a reason for hiding this comment

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

s/Firestore/Storage/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

}

@NonNull
static FirebaseStorage newInstance(@Nullable String bucketName, @NonNull FirebaseApp app) {
Copy link
Member

Choose a reason for hiding this comment

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

Do you actually need this method? Would it make sense to make the constructor package-private and call it directly in the Component? As a side-effect I think it will also make it possible to mock FirebaseStorage.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, we don't need it. I was trying to follow the precedent set by Firestore. Inlined.


class FirebaseStorageComponent {
/** A static map from storage buckets to FirebaseStorage instances. */
private static final Map<String, FirebaseStorage> instances = new HashMap<>();
Copy link
Member

Choose a reason for hiding this comment

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

Note this should not be static, otherwise the instances will be shared between all FirebaseApps.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh yes, that makes sense. Thanks for catching this.

return storage;
}

/** @hide */
Copy link
Member

Choose a reason for hiding this comment

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

I think @hide is not needed for non-public classes/symbols

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The original method was public. I have removed it now that I reduced the visibility.

this.app = app;
}

/** Provides instances of Firestore for given database names. */
Copy link
Member

Choose a reason for hiding this comment

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

s/Firestore/Storage/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@schmidt-sebastian schmidt-sebastian force-pushed the mrschmidt-storagecomponents branch 2 times, most recently from ed00904 to 339880f Compare March 14, 2019 03:06
Copy link
Contributor Author

@schmidt-sebastian schmidt-sebastian left a comment

Choose a reason for hiding this comment

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

Updated the PR. Most of the changes now are related to the switch to InternalAuthTokenProvider,

}

@NonNull
static FirebaseStorage newInstance(@Nullable String bucketName, @NonNull FirebaseApp app) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, we don't need it. I was trying to follow the precedent set by Firestore. Inlined.

}
checkNotNull(app, "Provided FirebaseApp must not be null.");
FirebaseStorageComponent component = app.get(FirebaseStorageComponent.class);
checkNotNull(component, "Firestore component is not present.");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed


class FirebaseStorageComponent {
/** A static map from storage buckets to FirebaseStorage instances. */
private static final Map<String, FirebaseStorage> instances = new HashMap<>();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh yes, that makes sense. Thanks for catching this.

this.app = app;
}

/** Provides instances of Firestore for given database names. */
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

return storage;
}

/** @hide */
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The original method was public. I have removed it now that I reduced the visibility.

@schmidt-sebastian schmidt-sebastian force-pushed the mrschmidt-storagecomponents branch from 339880f to 4039ddd Compare March 14, 2019 03:07
@schmidt-sebastian
Copy link
Contributor Author

/test connected-check-changed

public static FirebaseStorage getInstance(@NonNull FirebaseApp app, @NonNull String url) {
// noinspection ConstantConditions
Preconditions.checkArgument(app != null, "Null is not a valid value for the FirebaseApp.");
Preconditions.checkArgument(
Copy link
Member

Choose a reason for hiding this comment

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

optional nit: elsewhere you switched to staticly imported checks, might make sense to do the same throughout for consistency.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The rest of the SDK doesn't use static imports, so I changed this file to use the qualified import checkNotNull.

return Arrays.asList(
Component.builder(FirebaseStorageComponent.class)
.add(Dependency.required(FirebaseApp.class))
.add(Dependency.optional(InternalAuthProvider.class))
Copy link
Member

Choose a reason for hiding this comment

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

With my limited understanding of storage it looks like InternalAuthProvider is not needed right away(when storage initializes) unlike in Firestore, but only used in response to user calls to downloads and what not. If that is the case consider switching to Dependency.optionalProvider(InternalAuthProvider.class), what it will do is defer initialization of InternalAuthProvider until it is needed as well as prevent potential dependency cycles in the future.

That said, currently the only implementor of InternalAuthProvider is FirebaseAuth which is eager, so deferred initialization will not apply, but it seems a relatively easy change to do(especially if the Provider<T> is hidden behind storage.getAuthProvider()) to bulletproof storage for the future. wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Your understanding is correct. We only fetch a token once a user requests a file upload or download, so we don't need the AuthProvider until later. The change for this was relatively minor, so I updated this PR.

BTW, if wasn't immediately obvious to me whether optionalProvider makes Provider nullable or whether it makes Provider.get() nullable. I think it's the former (from looking through the code and playing with it). Maybe we can document this?

private final AdaptiveStreamBuffer mStreamBuffer;
// Active, current mutable state.
private final AtomicLong mBytesUploaded = new AtomicLong(0);
@Nullable final InternalAuthProvider mAuthProvider;
Copy link
Member

Choose a reason for hiding this comment

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

could it be private?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. FIxed

Copy link
Contributor Author

@schmidt-sebastian schmidt-sebastian left a comment

Choose a reason for hiding this comment

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

Updated the PR. My test app is able to authenticate with these changes.

public static FirebaseStorage getInstance(@NonNull FirebaseApp app, @NonNull String url) {
// noinspection ConstantConditions
Preconditions.checkArgument(app != null, "Null is not a valid value for the FirebaseApp.");
Preconditions.checkArgument(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The rest of the SDK doesn't use static imports, so I changed this file to use the qualified import checkNotNull.

return Arrays.asList(
Component.builder(FirebaseStorageComponent.class)
.add(Dependency.required(FirebaseApp.class))
.add(Dependency.optional(InternalAuthProvider.class))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Your understanding is correct. We only fetch a token once a user requests a file upload or download, so we don't need the AuthProvider until later. The change for this was relatively minor, so I updated this PR.

BTW, if wasn't immediately obvious to me whether optionalProvider makes Provider nullable or whether it makes Provider.get() nullable. I think it's the former (from looking through the code and playing with it). Maybe we can document this?

private final AdaptiveStreamBuffer mStreamBuffer;
// Active, current mutable state.
private final AtomicLong mBytesUploaded = new AtomicLong(0);
@Nullable final InternalAuthProvider mAuthProvider;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. FIxed

@schmidt-sebastian
Copy link
Contributor Author

/test connected-check-changed

1 similar comment
@schmidt-sebastian
Copy link
Contributor Author

/test connected-check-changed

Copy link
Member

@vkryachko vkryachko left a comment

Choose a reason for hiding this comment

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

LGTM

Looks like we have a legit failure in firebase-database-collection whose minSdkVersion is less than min version of playservices-base, I feel like this has alway been the case but now it suddently started failing(I'll take a look at and fix it)

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class StorageRegistrar implements ComponentRegistrar {
@Override
@Keep
Copy link
Member

Choose a reason for hiding this comment

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

nit: no need to @Keep this method, just the class is enough

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

@vkryachko
Copy link
Member

/retest

@schmidt-sebastian schmidt-sebastian merged commit 17e086f into master Mar 16, 2019
@vkryachko vkryachko deleted the mrschmidt-storagecomponents branch March 19, 2019 14:09
@firebase firebase locked and limited conversation to collaborators Oct 12, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

cla: yes Override cla size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants