-
Notifications
You must be signed in to change notification settings - Fork 298
Firebase Cloud Messaging API #127
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
Conversation
|
@hiranya911 I am wondering if there is a ticket some where of features desired in this PR. I have been working on an implementation myself in a personal project and would love to contribute if there is an actual list of requirements for the API |
|
@jcvillalta03 The initial offering is going to include the methods described above. If you have ideas for more features or further expansions, please feel free to create a new issue in this repo. |
|
Thanks @hiranya911. So at this point, this is initial offering is considered “code complete”? |
|
@jcvillalta03 for the moment yes. Equivalent set of operations are being implemented in other Admin SDK variants (Node, Python and Go) as well. But you should feel encouraged to propose changes/additions on top of what I have done here. If a certain use case cannot be handled properly with the initial set of APIs, we'd like to know, and see if we can do something about it. |
|
Ok, thanks for the explanation. Sorry to keeping conversation going in comments, I just wanted to make sure that I understand the Contribution guidelines. I look forward to this release! |
schmidt-sebastian
left a comment
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.
Overall, this is very good. I left some nits from an initial review that apply to a few places in this PR. It might make sense for you to do a first pass on these before I look at this in more depth.
| @@ -0,0 +1,201 @@ | |||
| /* | |||
| * Copyright 2017 Google Inc. | |||
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.
The line should now be "Copyright 2018 Google LLC"
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.
Done. Also added the header to a set of files that were missing it.
|
|
||
| /** | ||
| * Represents the Android-specific options that can be included in a {@link Message}. | ||
| * Instances of this class are thread-safe and immutable. |
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.
How does this show up in the JavaDoc output? I wonder if we should add a newline (or <p>).
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.
They show up as a single paragraph. We already have a quite a bit of similarly documented classes (See UserRecord class from auth for example)
| this.ttl = String.format("%ds", seconds); | ||
| } | ||
| } else { | ||
| this.ttl = null; |
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.
Nit: This is not needed since it is the default (here and above)
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 is needed since it's final
| * @return This builder. | ||
| */ | ||
| public Builder putData(@NonNull String key, @NonNull String value) { | ||
| this.data.put(key, value); |
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.
This doesn't override the existing value. Do you instead mean that the final AndroidConfig's data map will be overridden?
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 overrides the data set using the Message.Builder class. It's an FCM mechanic executed at the backend. In other words AndroidConfig.data map gets priority over Message.data map when FCM sends notifications. I've slightly modified the text to make this clear.
| this.body = builder.body; | ||
| this.icon = builder.icon; | ||
| if (builder.color != null) { | ||
| checkArgument(builder.color.matches("^#[0-9a-fA-F]{6}$"), |
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.
Suggestion (here and below):
this.color = checkArgument(...)
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.
Actually checkArgument() does not return anything. Only checkNotNull() returns the input argument.
| private final String threadId; | ||
|
|
||
| private Aps(Builder builder) { | ||
| int alerts = Booleans.countTrue( |
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.
How about:
checkArgument(Strings.isNullOrEmpty(builder.alertString) || builder.alert == null, ...)
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.
Done
|
|
||
| /** | ||
| * Sets the badge to be displayed with the message. Set to 0 to remove the badge. Do not | ||
| * specify any value to keep the badge unchanged. |
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.
This reads a little weird since it is not possible to not specify this value using this API. Can you make it more clear that you mean "do not call this method"?
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.
Done
| } | ||
|
|
||
| /** | ||
| * Sends the given {@link Message} via Firebase Cloud Messaging. If the {@code dryRun} option |
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.
Insert new paragraph after the first sentence.
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.
Done
|
|
||
| // Server error codes as defined in https://developers.google.com/instance-id/reference/server | ||
| // TODO: Should we handle other error codes here (e.g. PERMISSION_DENIED)? | ||
| private static final Map<String, String> ERROR_CODES = ImmutableMap.<String, String>builder() |
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.
Can we move these to a single place so the Admin SDK can share these errors?
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.
The backend error codes (keys of the map) are specific to the services we invoke at various places. I'm not sure there's a lot to be gained by sharing them across the package/SDK. What sort of sharing were you thinking of? Put them all in a shared FirebaseMessagingConstants class?
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.
Yeah, that's what I was leaning towards. It looks like https://github.com/firebase/firebase-admin-java/blob/master/src/main/java/com/google/firebase/iid/FirebaseInstanceId.java uses HTTP Response Codes though and not these same errors.
| } | ||
| } | ||
| this.successCount = successCount; | ||
| this.failureCount = failureCount; |
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.
This is just errors.size().
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.
Done
|
Thanks @schmidt-sebastian and @sampson-chen for the initial round of feedback. I've addressed almost all the comments. Over to you for another look. |
avishalom
left a comment
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.
LGTM(nit and question)
| Message message = Message.builder() | ||
| .setNotification(new Notification("Title", "Body")) | ||
| .setAndroidConfig(AndroidConfig.builder() | ||
| .setRestrictedPackageName("com.demoapps.hkj") |
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.
name?
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.
Changed
|
|
||
| private String makeSendRequest(Message message, | ||
| boolean dryRun) throws FirebaseMessagingException { | ||
| ImmutableMap.Builder<String, Object> payload = ImmutableMap.<String, Object>builder() |
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.
consider ImmutableMap.of(...)
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.
Have to use Builder here since we are conditionally putting some values.
| FirebaseMessaging messaging = FirebaseMessaging.getInstance(); | ||
| TopicManagementResponse results = messaging.subscribeToTopicAsync( | ||
| ImmutableList.of(TEST_REGISTRATION_TOKEN), "mock-topic").get(); | ||
| assertEquals(1, results.getSuccessCount() + results.getFailureCount()); |
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.
Is there no point in testing success and failure separately like in the next FirebaseMessagingTest.java?
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.
There's no meaningful way to test success case since the IID tokens keep changing.
| * information as well as the recipient information. In particular, the message must contain | ||
| * exactly one of token, topic or condition parameters. Instances of this class are thread-safe | ||
| * and immutable. Use {@link Message.Builder} co crete new instances. | ||
| * and immutable. Use {@link Message.Builder} to crete new instances. |
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.
s/crete/create/
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.
Done
| if (builder.ttl != null) { | ||
| checkArgument(builder.ttl >= 0, "ttl must not be negative"); | ||
| long seconds = TimeUnit.MILLISECONDS.toSeconds(builder.ttl); | ||
| long subsecondNanos = TimeUnit.MILLISECONDS.toNanos(builder.ttl - seconds * 1000L); |
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.
I think this should be 1000 * 1000.
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.
builder.ttl is in millis. So I believe this is correct. We have a test case for this where 10ms is correctly transformed to 0.010000000s (where the decimal part is subsecond nanos).
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.
Sorry, I think I had nanoseconds in my head.
|
|
||
| @Override | ||
| public void destroy() { | ||
| // NOTE: We don't explicitly tear down anything here, but public methods of StorageClient |
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.
StorageClient -> FirebaseMessaging
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.
Done
| /** | ||
| * Represents a message that can be sent via Firebase Cloud Messaging (FCM). Contains payload | ||
| * information as well as the recipient information. In particular, the message must contain | ||
| * exactly one of token, topic or condition parameters. Instances of this class are thread-safe |
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.
s/one of token, topic or condition parameters/one token, topic or condition parameter/
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.
Done
|
|
||
| // Server error codes as defined in https://developers.google.com/instance-id/reference/server | ||
| // TODO: Should we handle other error codes here (e.g. PERMISSION_DENIED)? | ||
| private static final Map<String, String> ERROR_CODES = ImmutableMap.<String, String>builder() |
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.
Yeah, that's what I was leaning towards. It looks like https://github.com/firebase/firebase-admin-java/blob/master/src/main/java/com/google/firebase/iid/FirebaseInstanceId.java uses HTTP Response Codes though and not these same errors.
| Map<Message, Map<String, Object>> testMessages = buildTestMessages(); | ||
|
|
||
| for (Map.Entry<Message, Map<String, Object>> entry : testMessages.entrySet()) { | ||
| response.setContent("{\"name\": \"mock-name\"}"); |
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.
Can you remove the code duplication in this test its counterpart?
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.
Done
| MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); | ||
| FirebaseMessaging messaging = initMessaging(response); | ||
| for (int code : HTTP_ERRORS) { | ||
| response.setStatusCode(code).setContent( |
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.
Same for these tests. The code here could use some de-duping.
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.
Done
|
|
||
| ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||
| request.getContent().writeTo(out); | ||
| assertEquals("{\"to\":\"/topics/test-topic\",\"registration_tokens\":[\"id1\",\"id2\"]}", |
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.
This seems overly harsh as it relies on an unspecified order during the serialization.
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.
Fixed by parsing into a map, and then comparing entries.
| assertEquals(TEST_IID_UNSUBSCRIBE_URL, request.getUrl().toString()); | ||
| assertEquals("Bearer test-token", request.getHeaders().getAuthorization()); | ||
| assertEquals("true", request.getHeaders().get("access_token_auth")); | ||
|
|
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.
The lines from 343-356 also seem duplicated to me.
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.
Done
| .build(); | ||
| FirebaseApp.initializeApp(options); | ||
| FirebaseMessaging messaging = FirebaseMessaging.getInstance(); | ||
| TestResponseInterceptor interceptor = new TestResponseInterceptor(); |
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.
Can 314-319 be done in the @before method?
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.
Moved to a helper method.
|
Made the suggested changes. |
* Accept prefixed topic names * Parsing topic name in a helper method * Updated javadoc for setTopic()
Implements the messaging related types and the following entry points:
go/firebase-admin-fcm-api