Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b53c94f
Preliminary impl of FCM batch support
hiranya911 Jan 29, 2019
6882354
Added BatchMessage class and tests
hiranya911 Jan 29, 2019
e75bcff
Refactored the FCM send operation
hiranya911 Jan 29, 2019
726ee09
Added unit tests for sendBatch() API
hiranya911 Jan 29, 2019
e3ad436
Refactored error handling
hiranya911 Jan 29, 2019
504e9ef
Refactored FCM logic into a new FirebaseMessagingClient class
hiranya911 Jan 30, 2019
80ed4b1
Using a separate request factory for child requests
hiranya911 Jan 30, 2019
057fcb5
Added the InstanceIdClient class for handling topic mgt ops
hiranya911 Jan 30, 2019
2c36d4c
Added license headers
hiranya911 Jan 30, 2019
c437042
Renamed BatchResponse as SendResponse
hiranya911 Jan 30, 2019
a3223fd
Added BatchResponse class; Renamed BatchMessage to MulticastMessage
hiranya911 Feb 6, 2019
5668421
Updated tests and docs
hiranya911 Feb 6, 2019
693711f
Updated documentation
hiranya911 Feb 6, 2019
46507d8
Added documentation and tests
hiranya911 Feb 6, 2019
b4d80e0
Merge branch 'master' into hkj-fcm-batch
hiranya911 Feb 6, 2019
0371159
Updated docs, changelog and annotations
hiranya911 Feb 6, 2019
5d57e07
Updated integration test to use multiple topics; Updated docs
hiranya911 Feb 7, 2019
f8f8c1e
Removing a redundant whitespace
hiranya911 Feb 11, 2019
7ecabe0
Reduced FCM batch size to 100 (#250)
hiranya911 Feb 19, 2019
3c0ae40
Setting the X-Client-Version header for FCM (#252)
hiranya911 Feb 22, 2019
c6367c0
Snippets for FCM sendAll() and sendMulticast() (#249)
hiranya911 Feb 25, 2019
1fbddcb
Addressing some documentation nits
hiranya911 Mar 12, 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
Prev Previous commit
Next Next commit
Added BatchResponse class; Renamed BatchMessage to MulticastMessage
  • Loading branch information
hiranya911 committed Feb 6, 2019
commit a3223fdeb8ef499b6e1781dad3c8d333c4ae4edc
49 changes: 49 additions & 0 deletions src/main/java/com/google/firebase/messaging/BatchResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2019 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.firebase.messaging;

import com.google.common.collect.ImmutableList;
import java.util.List;

public final class BatchResponse {

private final List<SendResponse> responses;
private final int successCount;

BatchResponse(List<SendResponse> responses) {
this.responses = ImmutableList.copyOf(responses);
int successCount = 0;
for (SendResponse response : this.responses) {
if (response.isSuccessful()) {
successCount++;
}
}
this.successCount = successCount;
}

public List<SendResponse> getResponses() {
return responses;
}

public int getSuccessCount() {
return successCount;
}

public int getFailureCount() {
return responses.size() - successCount;
}
}
49 changes: 26 additions & 23 deletions src/main/java/com/google/firebase/messaging/FirebaseMessaging.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import com.google.api.client.http.HttpResponseInterceptor;
import com.google.api.core.ApiFuture;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.firebase.FirebaseApp;
Expand Down Expand Up @@ -48,6 +49,7 @@ private FirebaseMessaging(FirebaseApp app) {
this(app, null);
}

@VisibleForTesting
FirebaseMessaging(FirebaseApp app, @Nullable HttpResponseInterceptor responseInterceptor) {
this.app = checkNotNull(app, "app must not be null");
this.messagingClient = new FirebaseMessagingClient(app, responseInterceptor);
Expand Down Expand Up @@ -141,54 +143,55 @@ protected String execute() throws FirebaseMessagingException {
* @param messages A non-null, non-empty list of messages
* @return A list of {@link SendResponse} instances corresponding to the list of messages sent.
*/
public List<SendResponse> sendBatch(
public BatchResponse sendAll(
@NonNull List<Message> messages) throws FirebaseMessagingException {
return sendBatch(messages, false);
return sendAll(messages, false);
}

public List<SendResponse> sendBatch(
BatchMessage batch, boolean dryRun) throws FirebaseMessagingException {
checkNotNull(batch, "batch message must not be null");
return sendBatch(batch.getMessageList(), dryRun);
public BatchResponse sendAll(
List<Message> messages, boolean dryRun) throws FirebaseMessagingException {
return sendAllOp(messages, dryRun).call();
}

public List<SendResponse> sendBatch(BatchMessage batch) throws FirebaseMessagingException {
return sendBatch(batch, false);
public ApiFuture<BatchResponse> sendAllAsync(List<Message> messages) {
return sendAllAsync(messages, false);
}

public List<SendResponse> sendBatch(
List<Message> messages, boolean dryRun) throws FirebaseMessagingException {
return sendBatchOp(messages, dryRun).call();
public ApiFuture<BatchResponse> sendAllAsync(List<Message> messages, boolean dryRun) {
return sendAllOp(messages, dryRun).callAsync(app);
}

public ApiFuture<List<SendResponse>> sendBatchAsync(BatchMessage batch) {
return sendBatchAsync(batch, false);
public BatchResponse sendMulticast(MulticastMessage batch) throws FirebaseMessagingException {
return sendMulticast(batch, false);
}

public ApiFuture<List<SendResponse>> sendBatchAsync(BatchMessage batch, boolean dryRun) {
public BatchResponse sendMulticast(
MulticastMessage batch, boolean dryRun) throws FirebaseMessagingException {
checkNotNull(batch, "batch message must not be null");
return sendBatchAsync(batch.getMessageList(), dryRun);
return sendAll(batch.getMessageList(), dryRun);
}

public ApiFuture<List<SendResponse>> sendBatchAsync(List<Message> messages) {
return sendBatchAsync(messages, false);

public ApiFuture<BatchResponse> sendMulticastAsync(MulticastMessage batch) {
return sendMulticastAsync(batch, false);
}

public ApiFuture<List<SendResponse>> sendBatchAsync(List<Message> messages, boolean dryRun) {
return sendBatchOp(messages, dryRun).callAsync(app);
public ApiFuture<BatchResponse> sendMulticastAsync(MulticastMessage batch, boolean dryRun) {
checkNotNull(batch, "batch message must not be null");
return sendAllAsync(batch.getMessageList(), dryRun);
}

private CallableOperation<List<SendResponse>, FirebaseMessagingException> sendBatchOp(
private CallableOperation<BatchResponse, FirebaseMessagingException> sendAllOp(
final List<Message> messages, final boolean dryRun) {

final List<Message> immutableMessages = ImmutableList.copyOf(messages);
checkArgument(!immutableMessages.isEmpty(), "messages list must not be empty");
checkArgument(immutableMessages.size() <= 1000,
"messages list must not contain more than 1000 elements");
return new CallableOperation<List<SendResponse>,FirebaseMessagingException>() {
return new CallableOperation<BatchResponse,FirebaseMessagingException>() {
@Override
protected List<SendResponse> execute() throws FirebaseMessagingException {
return messagingClient.sendBatch(messages, dryRun);
protected BatchResponse execute() throws FirebaseMessagingException {
return messagingClient.sendAll(messages, dryRun);
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ String send(Message message, boolean dryRun) throws FirebaseMessagingException {
}
}

List<SendResponse> sendBatch(
BatchResponse sendAll(
List<Message> messages, boolean dryRun) throws FirebaseMessagingException {
try {
return sendBatchRequest(messages, dryRun);
Expand Down Expand Up @@ -135,13 +135,13 @@ private String sendSingleRequest(Message message, boolean dryRun) throws IOExcep
}
}

private List<SendResponse> sendBatchRequest(
private BatchResponse sendBatchRequest(
List<Message> messages, boolean dryRun) throws IOException {

MessagingBatchCallback callback = new MessagingBatchCallback();
BatchRequest batch = newBatchRequest(messages, dryRun, callback);
batch.execute();
return callback.getResponses();
return new BatchResponse(callback.getResponses());
}

private BatchRequest newBatchRequest(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
import java.util.Map;

/**
* BatchMessage
* MulticastMessage
*/
public class BatchMessage {
public class MulticastMessage {

private final List<String> tokens;
private final Map<String, String> data;
Expand All @@ -38,7 +38,7 @@ public class BatchMessage {
private final WebpushConfig webpushConfig;
private final ApnsConfig apnsConfig;

private BatchMessage(Builder builder) {
private MulticastMessage(Builder builder) {
this.tokens = builder.tokens.build();
checkArgument(!this.tokens.isEmpty(), "at least one token must be specified");
checkArgument(this.tokens.size() <= 1000, "no more than 1000 tokens can be specified");
Expand Down Expand Up @@ -168,13 +168,13 @@ public Builder putAllData(@NonNull Map<String, String> map) {
}

/**
* Creates a new {@link Message} instance from the parameters set on this builder.
* Creates a new {@link MulticastMessage} instance from the parameters set on this builder.
*
* @return A new {@link Message} instance.
* @return A new {@link MulticastMessage} instance.
* @throws IllegalArgumentException If any of the parameters set on the builder are invalid.
*/
public BatchMessage build() {
return new BatchMessage(this);
public MulticastMessage build() {
return new MulticastMessage(this);
}
}
}
79 changes: 79 additions & 0 deletions src/test/java/com/google/firebase/messaging/BatchResponseTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2019 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.firebase.messaging;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;

import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;

public class BatchResponseTest {

@Test
public void testEmptyResponses() {
List<SendResponse> responses = new ArrayList<>();

BatchResponse batchResponse = new BatchResponse(responses);

assertEquals(0, batchResponse.getSuccessCount());
assertEquals(0, batchResponse.getFailureCount());
assertEquals(0, batchResponse.getResponses().size());
}

@Test
public void testSomeResponse() {
ImmutableList<SendResponse> responses = ImmutableList.of(
SendResponse.fromMessageId("message1"),
SendResponse.fromMessageId("message2"),
SendResponse.fromException(new FirebaseMessagingException("error-code",
"error-message", null))
);

BatchResponse batchResponse = new BatchResponse(responses);

assertEquals(2, batchResponse.getSuccessCount());
assertEquals(1, batchResponse.getFailureCount());
assertEquals(3, batchResponse.getResponses().size());
for (int i = 0; i < 3; i ++) {
assertSame(responses.get(i), batchResponse.getResponses().get(i));
}
}

@Test
public void testResponsesImmutable() {
List<SendResponse> responses = new ArrayList<>();
responses.add(SendResponse.fromMessageId("message1"));
BatchResponse batchResponse = new BatchResponse(responses);
SendResponse sendResponse = SendResponse.fromMessageId("message2");

try {
batchResponse.getResponses().add(sendResponse);
fail("No error thrown when modifying responses list");
} catch (UnsupportedOperationException expected) {
// expected
}
}

@Test(expected = NullPointerException.class)
public void testResponsesCannotBeNull() {
new BatchResponse(null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void testSend() throws Exception {
}

@Test
public void testSendBatch() throws Exception {
public void testSendAll() throws Exception {
List<Message> messages = new ArrayList<>();
messages.add(
Message.builder()
Expand All @@ -85,32 +85,35 @@ public void testSendBatch() throws Exception {
.setToken("not-a-token")
.build());

List<SendResponse> response = FirebaseMessaging.getInstance().sendBatch(messages, true);
BatchResponse response = FirebaseMessaging.getInstance().sendAll(messages, true);
assertEquals(3, response.getSuccessCount());
assertEquals(0, response.getFailureCount());

assertEquals(3, response.size());
assertTrue(response.get(0).isSuccessful());
String id = response.get(0).getMessageId();
List<SendResponse> responses = response.getResponses();
assertEquals(3, responses.size());
assertTrue(responses.get(0).isSuccessful());
String id = responses.get(0).getMessageId();
assertTrue(id != null && id.matches("^projects/.*/messages/.*$"));

assertTrue(response.get(1).isSuccessful());
id = response.get(1).getMessageId();
assertTrue(responses.get(1).isSuccessful());
id = responses.get(1).getMessageId();
assertTrue(id != null && id.matches("^projects/.*/messages/.*$"));

assertFalse(response.get(2).isSuccessful());
assertNull(response.get(2).getMessageId());
FirebaseMessagingException exception = response.get(2).getException();
assertFalse(responses.get(2).isSuccessful());
assertNull(responses.get(2).getMessageId());
FirebaseMessagingException exception = responses.get(2).getException();
assertNotNull(exception);
assertEquals("invalid-argument", exception.getErrorCode());
}

@Test
public void testLargeSendBatch() throws Exception {
public void testSendThousand() throws Exception {
List<Message> messages = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
messages.add(Message.builder().setTopic("foo-bar").build());
}
List<SendResponse> response = FirebaseMessaging.getInstance().sendBatch(messages, true);
assertEquals(1000, response.size());
BatchResponse response = FirebaseMessaging.getInstance().sendAll(messages, true);
assertEquals(1000, response.getSuccessCount());
}

@Test
Expand Down
Loading