blob: ecc544378d5b882f630f3cac8a24df4761c5716b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.usergrid.services.notifications.gcm;
import com.google.android.gcm.server.Constants;
import com.google.android.gcm.server.InvalidRequestException;
import net.jcip.annotations.NotThreadSafe;
import org.apache.usergrid.ServiceApplication;
import org.apache.usergrid.persistence.*;
import org.apache.usergrid.persistence.entities.*;
import org.apache.usergrid.persistence.index.query.CounterResolution;
import org.apache.usergrid.services.ServiceResults;
import org.apache.usergrid.services.notifications.*;
import org.junit.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.*;
import org.apache.usergrid.services.ServiceAction;
import static org.junit.Assert.*;
import static org.apache.usergrid.services.notifications.ApplicationQueueManager.NOTIFIER_ID_POSTFIX;
@NotThreadSafe
public class NotificationsServiceIT extends AbstractServiceNotificationIT {
private static final Logger logger = LoggerFactory
.getLogger(NotificationsServiceIT.class);
/**
* set to true to use actual connections to GCM servers
*/
private static final boolean USE_REAL_CONNECTIONS = true;
private static final String PROVIDER = USE_REAL_CONNECTIONS ? "google" : "noop";
private static final String API_KEY = "AIzaSyCIH_7WC0mOqBGMOXyQnFgrBpOePgHvQJM";
private static final String PUSH_TOKEN = "APA91bGxRGnMK8tKgVPzSlxtCFvwSVqx0xEPjA06sBmiK0k"
+ "QsiwUt6ipSYF0iPRHyUgpXle0P8OlRWJADkQrcN7yxG4pLMg1CVmrqDu8tfSe63mZ-MRU2IW0cOhmo"
+ "sqzC9trl33moS3OvT7qjDjkP4Qq8LYdwwYC5A";
private Notifier notifier;
private Device device1, device2;
private NotificationsService ns;
private QueueListener listener;
@Before
public void before() throws Exception {
// create gcm notifier //
app.clear();
app.put("name", "gcm");
app.put("provider", PROVIDER);
app.put("environment", "development");
app.put("apiKey", API_KEY);
notifier = (Notifier) app
.testRequest(ServiceAction.POST, 1, "notifiers").getEntity()
.toTypedEntity();
String key = notifier.getName() + NOTIFIER_ID_POSTFIX;
// create devices //
app.clear();
app.put(key, PUSH_TOKEN);
Entity e = app.testRequest(ServiceAction.POST, 1, "devices").getEntity();
app.testRequest(ServiceAction.GET, 1, "devices", e.getUuid());
device1 = app.getEntityManager().get(e.getUuid(), Device.class);
assertEquals(device1.getProperty(key), PUSH_TOKEN);
app.put(key, PUSH_TOKEN);
e = app.testRequest(ServiceAction.POST, 1, "devices").getEntity();
device2 = app.getEntityManager().get(e.getUuid(), Device.class);
ns = getNotificationService();
listener = new QueueListener(ns.getServiceManagerFactory(), ns.getEntityManagerFactory(), new Properties());
listener.start();
}
@After
public void after() {
if (listener != null) {
listener.stop();
}
}
@Test
public void emptyPushNotification() throws Exception {
app.clear();
app.put("name", "foo");
app.put("provider", PROVIDER);
app.put("environment", "development");
app.put("apiKey", API_KEY);
Notifier n = (Notifier) app
.testRequest(ServiceAction.POST, 1, "notifiers").getEntity()
.toTypedEntity();
app.clear();
String payload = "Hello, World!";
Map<String, String> payloads = new HashMap<String, String>(1);
payloads.put("foo", payload);
app.put("payloads", payloads);
app.put("debug", true);
app.put("queued", System.currentTimeMillis());
Entity e = app.testRequest(ServiceAction.POST, 1, "devices", device1.getUuid(), "notifications")
.getEntity();
app.testRequest(ServiceAction.GET, 1, "notifications", e.getUuid());
Notification notification = app.getEntityManager().get(e.getUuid(),
Notification.class);
// perform push //
notification = notificationWaitForComplete(notification);
checkReceipts(notification, 0);
}
@Test
public void singlePushNotification() throws Exception {
app.clear();
String payload = "Hello, World!";
Map<String, String> payloads = new HashMap<String, String>(1);
payloads.put(notifier.getUuid().toString(), payload);
app.put("payloads", payloads);
app.put("queued", System.currentTimeMillis());
app.put("debug", true);
app.put("expire", System.currentTimeMillis() + 300000); // add 5 minutes to current time
Entity e = app.testRequest(ServiceAction.POST, 1, "devices", device1.getUuid(), "notifications").getEntity();
app.testRequest(ServiceAction.GET, 1, "notifications", e.getUuid());
Notification notification = app.getEntityManager().get(e.getUuid(), Notification.class);
assertEquals(
notification.getPayloads().get(notifier.getUuid().toString()),
payload);
// perform push //
notification = notificationWaitForComplete(notification);
checkReceipts(notification, 1);
}
@Test
public void singlePushNotificationMapPayload() throws Exception {
app.clear();
Map<String, Object> topLevel = new HashMap<>();
Map<String, String> mapPayload = new HashMap<String, String>(){{
put("key1", "value1");
put("key2", "value2");
}};
topLevel.put("enabler", mapPayload);
Map<String, Object> payloads = new HashMap<>(1);
payloads.put(notifier.getUuid().toString(), topLevel);
app.put("payloads", payloads);
app.put("queued", System.currentTimeMillis());
app.put("debug", true);
app.put("expire", System.currentTimeMillis() + 300000); // add 5 minutes to current time
Entity e = app.testRequest(ServiceAction.POST, 1, "devices", device1.getUuid(), "notifications").getEntity();
app.testRequest(ServiceAction.GET, 1, "notifications", e.getUuid());
Notification notification = app.getEntityManager().get(e.getUuid(), Notification.class);
//assertEquals(
// notification.getPayloads().get(notifier.getUuid().toString()),
// payload);
// perform push //
notification = notificationWaitForComplete(notification);
checkReceipts(notification, 1);
}
@Test
public void singlePushNotificationNoReceipts() throws Exception {
app.clear();
String payload = "Hello, World!";
Map<String, String> payloads = new HashMap<String, String>(1);
payloads.put(notifier.getUuid().toString(), payload);
app.put("payloads", payloads);
app.put("queued", System.currentTimeMillis());
app.put("debug", true);
app.put("saveReceipts",false );
app.put("expire", System.currentTimeMillis() + 300000); // add 5 minutes to current time
Entity e = app.testRequest(ServiceAction.POST, 1, "devices", device1.getUuid(), "notifications").getEntity();
app.testRequest(ServiceAction.GET, 1, "notifications", e.getUuid());
Notification notification = app.getEntityManager().get(e.getUuid(), Notification.class);
assertEquals(
notification.getPayloads().get(notifier.getUuid().toString()),
payload);
// perform push //
notification = notificationWaitForComplete(notification);
checkReceipts(notification, 0);
}
@Test
public void singlePushNotificationHighPriority() throws Exception {
app.clear();
String payload = "Hello, World!";
Map<String, String> payloads = new HashMap<String, String>(1);
payloads.put(notifier.getUuid().toString(), payload);
app.put("payloads", payloads);
app.put("queued", System.currentTimeMillis());
app.put("debug", true);
app.put("expire", System.currentTimeMillis() + 300000); // add 5 minutes to current time
app.put("priority", "high");
Entity e = app.testRequest(ServiceAction.POST, 1, "devices", device1.getUuid(), "notifications").getEntity();
app.testRequest(ServiceAction.GET, 1, "notifications", e.getUuid());
Notification notification = app.getEntityManager().get(e.getUuid(), Notification.class);
assertEquals(
notification.getPayloads().get(notifier.getUuid().toString()),
payload);
// perform push //
notification = notificationWaitForComplete(notification);
assertEquals("high", notification.getPriority());
checkReceipts(notification, 1);
}
@Test
public void singlePushNotificationWithInvalidPriority() throws Exception {
app.clear();
String payload = "Hello, World!";
Map<String, String> payloads = new HashMap<String, String>(1);
payloads.put(notifier.getUuid().toString(), payload);
app.put("payloads", payloads);
app.put("queued", System.currentTimeMillis());
app.put("debug", true);
app.put("expire", System.currentTimeMillis() + 300000); // add 5 minutes to current time
app.put("priority", "not_a_priority");
Entity e = app.testRequest(ServiceAction.POST, 1, "devices", device1.getUuid(), "notifications").getEntity();
app.testRequest(ServiceAction.GET, 1, "notifications", e.getUuid());
Notification notification = app.getEntityManager().get(e.getUuid(), Notification.class);
assertEquals(
notification.getPayloads().get(notifier.getUuid().toString()),
payload);
// perform push //
notification = notificationWaitForComplete(notification);
// if priority is invalid, it should default to normal
assertEquals("normal", notification.getPriority());
checkReceipts(notification, 1);
}
@Test
public void singlePushNotificationWithMapPayload() throws Exception {
app.clear();
String payload = "{\"message\":\"Hello, World!\", \"campaign\":\"Hello Campaign\"}";
Map<String, String> payloads = new HashMap<String, String>(1);
payloads.put(notifier.getUuid().toString(), payload);
app.put("payloads", payloads);
app.put("queued", System.currentTimeMillis());
app.put("debug", true);
app.put("expire", System.currentTimeMillis() + 300000); // add 5 minutes to current time
Entity e = app.testRequest(ServiceAction.POST, 1, "devices", device1.getUuid(), "notifications").getEntity();
app.testRequest(ServiceAction.GET, 1, "notifications", e.getUuid());
Notification notification = app.getEntityManager().get(e.getUuid(), Notification.class);
assertEquals(
notification.getPayloads().get(notifier.getUuid().toString()),
payload);
// perform push //
notification = notificationWaitForComplete(notification);
checkReceipts(notification, 1);
}
@Ignore("turn this on and run individually when you want to verify. there is an issue with the aggregate counter, because of all the other tests"
+ "AND, there is an issue with the batcher where we have to turn it up/down to see the results in time. ")
@Test
public void singlePushNotificationWithCounters() throws Exception {
long ts = System.currentTimeMillis() - ( 24 * 60 * 60 * 1000 );
app.clear();
String payload = "Hello, World!";
Map<String, String> payloads = new HashMap<String, String>(1);
payloads.put(notifier.getUuid().toString(), payload);
app.put("payloads", payloads);
app.put("queued", System.currentTimeMillis());
app.put("debug", false);
app.put("expire", System.currentTimeMillis() + 300000); // add 5 minutes to current time
Entity e = app.testRequest(ServiceAction.POST, 1, "devices", device1.getUuid(), "notifications").getEntity();
app.testRequest(ServiceAction.GET, 1, "notifications", e.getUuid());
Notification notification = app.getEntityManager().get(e.getUuid(), Notification.class);
assertEquals(
notification.getPayloads().get(notifier.getUuid().toString()),
payload);
// perform push //
notification = notificationWaitForComplete(notification);
// checkReceipts(notification, 1);
verifyNotificationCounter( notification,"completed",ts,1 );
verifyNotificationCounter( notification,"failed",ts, 0 );
}
public void verifyNotificationCounter(Notification notification,String status,Long timestamp, int expected){
Results countersResults = app.getEntityManager().getAggregateCounters( null,null,null,"counters.notifications."+notification.getUuid()+"."+status,
CounterResolution.ALL,timestamp,System.currentTimeMillis(),false ) ;
assertEquals( 1, countersResults.getCounters().size() );
if(expected > 0) {
assertEquals( expected, countersResults.getCounters().get( 0 ).getValues().get( 0 ).getValue() );
}else if (expected == 0){
assertEquals( 0,countersResults.getCounters().get( 0 ).getValues().size());
}
LocalDateTime localDateTime = LocalDateTime.now();
StringBuilder currentDate = new StringBuilder( );
currentDate.append( "counters.notifications.aggregate."+status+"." );
currentDate.append( localDateTime.getYear()+"." );
currentDate.append( localDateTime.getMonth()+"." );
currentDate.append( localDateTime.getDayOfMonth()); //+"." );
countersResults = app.getEntityManager().getAggregateCounters( null,null,null,currentDate.toString(),
CounterResolution.ALL,timestamp,System.currentTimeMillis(),false ) ;
//checks to see that it exists
assertEquals( 1, countersResults.getCounters().size() );
if(expected > 0) {
assertEquals( expected, countersResults.getCounters().get( 0 ).getValues().get( 0 ).getValue() );
}
else if (expected == 0){
assertEquals( 0,countersResults.getCounters().get( 0 ).getValues().size());
}
}
@Test
public void singlePushNotificationMultipleDevices() throws Exception {
app.clear();
String payload = "Hello, World!";
Map<String, String> payloads = new HashMap<String, String>(1);
payloads.put(notifier.getUuid().toString(), payload);
app.put("payloads", payloads);
app.put("queued", System.currentTimeMillis());
app.put("debug", true);
app.put("expire", System.currentTimeMillis() + 300000); // add 5 minutes to current time
Entity e = app.testRequest(ServiceAction.POST, 1, "devices", "*", "notifications").getEntity();
app.testRequest(ServiceAction.GET, 1, "notifications", e.getUuid());
Notification notification = app.getEntityManager().get(e.getUuid(), Notification.class);
assertEquals(
notification.getPayloads().get(notifier.getUuid().toString()),
payload);
// perform push //
notification = notificationWaitForComplete(notification);
checkReceipts(notification, 2);
}
@Test
public void singlePushNotificationViaUser() throws Exception {
app.clear();
// create user asdf
app.put("username", "asdf");
app.put("email", "asdf@adsf.com");
User user = (User) app.testRequest(ServiceAction.POST, 1, "users").getEntity();
assertNotNull(user);
// post an existing device to user's devices collection
Entity device = app.testRequest(ServiceAction.POST, 1, "users", user.getUuid(), "devices", device1.getUuid()).getEntity();
assertEquals(device.getUuid(), device1.getUuid());
// create and post notification
String payload = "Hello, World!";
Map<String, String> payloads = new HashMap<String, String>(1);
payloads.put(notifier.getUuid().toString(), payload);
app.put("payloads", payloads);
app.put("queued", System.currentTimeMillis());
app.put("debug", true);
Entity e = app.testRequest(ServiceAction.POST, 1, "users", user.getUuid(), "notifications").getEntity();
app.testRequest(ServiceAction.GET, 1, "notifications", e.getUuid());
// perform push //
Notification notification = app.getEntityManager().get(e.getUuid(), Notification.class);
notification = notificationWaitForComplete(notification);
checkReceipts(notification, 1);
}
@Test
public void twoBatchNotification() throws Exception {
app.clear();
String payload = "Hello, World!";
Map<String, String> payloads = new HashMap<String, String>(1);
payloads.put(notifier.getUuid().toString(), payload);
app.put("payloads", payloads);
app.put("queued", System.currentTimeMillis());
app.put("debug", true);
Entity e = app.testRequest(ServiceAction.POST, 1, "devices", "notifications").getEntity();
app.testRequest(ServiceAction.GET, 1, "notifications", e.getUuid());
Notification notification = app.getEntityManager().get(e.getUuid(), Notification.class);
assertEquals(
notification.getPayloads().get(notifier.getUuid().toString()),
payload);
// reduce Batch size to 1
Field field = GCMAdapter.class.getDeclaredField("BATCH_SIZE");
field.setAccessible(true);
int multicastSize = field.getInt(GCMAdapter.class);
try {
field.setInt(GCMAdapter.class, 1);
// perform push //
notification = notificationWaitForComplete(notification);
checkReceipts(notification, 2);
} finally {
field.setInt(GCMAdapter.class, multicastSize);
}
}
@Test
public void badPayloads() throws Exception {
// bad payloads format
app.clear();
app.put("payloads", "{asdf}");
app.put("debug", true);
try {
app.testRequest(ServiceAction.POST, 1, "devices", device1.getUuid(), "notifications");
fail("invalid payload should have been rejected");
} catch (IllegalArgumentException ex) {
// ok
}
// bad notifier
Map<String, String> payloads = new HashMap<String, String>(2);
app.put("payloads", payloads);
payloads.put("xxx", "");
try {
app.testRequest(ServiceAction.POST, 1, "devices", device1.getUuid(), "notifications");
fail("invalid payload should have been rejected");
} catch (IllegalArgumentException ex) {
// ok
}
// payload too long
// need the real provider for this one...
app.clear();
app.put("name", "gcm2");
app.put("provider", "google");
app.put("environment", "development");
app.put("apiKey", API_KEY);
Entity e = app.testRequest(ServiceAction.POST, 1, "notifiers")
.getEntity();
Notifier notifier2 = app.getEntityManager().get(e.getUuid(), Notifier.class);
payloads.clear();
StringBuilder sb = new StringBuilder();
sb.append("{\"x\":\"");
while (sb.length() < 4080) {
sb.append("x");
}
sb.append("\"}");
payloads.put(notifier2.getUuid().toString(), sb.toString());
app.clear();
app.put("payloads", payloads);
app.put("debug", true);
try {
app.testRequest(ServiceAction.POST, 1, "devices", device1.getUuid(), "notifications");
fail("invalid payload should have been rejected");
} catch (Exception ex) {
assertEquals("java.lang.IllegalArgumentException: GCM payloads must be 4096 characters or less",
ex.getMessage());
// ok
}
}
@Test
public void badToken() throws Exception {
// create device w/ bad token
app.put(notifier.getName() + NOTIFIER_ID_POSTFIX, PUSH_TOKEN + "x");
Entity badDeviceEntity = app.testRequest(ServiceAction.POST, 1, "devices").getEntity();
Device badDevice = app.getEntityManager().get(badDeviceEntity.getUuid(), Device.class);
// create notification payload
app.clear();
String payload = "Hello, World!";
Map<String, String> payloads = new HashMap<String, String>(1);
payloads.put(notifier.getUuid().toString(), payload);
app.put("payloads", payloads);
app.put("queued", System.currentTimeMillis());
app.put("debug", true);
// create push notification
Entity e = app.testRequest(ServiceAction.POST, 1, "devices", badDevice.getUuid(), "notifications")
.getEntity();
// validate notification was created successfully
app.testRequest(ServiceAction.GET, 1, "notifications", e.getUuid());
Notification notification = app.getEntityManager().get(e.getUuid(), Notification.class);
assertEquals(
notification.getPayloads().get(notifier.getUuid().toString()),
payload);
// wait for notification to be marked finished
notification = notificationWaitForComplete(notification);
// receipts are created and queried, wait a bit longer for this to happen as indexing
app.waitForQueueDrainAndRefreshIndex(500);
// get the receipts entity IDs
List<EntityRef> receipts = getNotificationReceipts(notification);
assertEquals(1, receipts.size());
// Validate the error is the correct type InvalidRegistration
Receipt receipt = app.getEntityManager().get(receipts.get(0), Receipt.class);
assertEquals("InvalidRegistration", receipt.getErrorCode());
}
@Test
public void createGoogleNotifierWithBadAPIKey() throws Exception {
final String badKey = API_KEY + "bad";
// create notifier with bad API key
app.clear();
app.put("name", "gcm_bad_key");
app.put("provider", PROVIDER);
app.put("environment", "development");
app.put("apiKey", badKey);
try {
notifier = (Notifier) app
.testRequest(ServiceAction.POST, 1, "notifiers").getEntity()
.toTypedEntity();
} catch (InvalidRequestException e) {
assertEquals(Constants.ERROR_INVALID_REGISTRATION, e.getDescription());
}
}
@Test
public void sendNotificationWithBadAPIKey() throws Exception {
final String badKey = API_KEY + "bad";
// update an existing notifier with a bad API key
app.clear();
app.put("apiKey", badKey);
notifier = (Notifier) app
.testRequest(ServiceAction.PUT, 1, "notifiers", notifier.getUuid()).getEntity()
.toTypedEntity();
// create notification payload
app.clear();
String payload = "Hello, World!";
Map<String, String> payloads = new HashMap<String, String>(1);
payloads.put(notifier.getUuid().toString(), payload);
app.put("payloads", payloads);
app.put("queued", System.currentTimeMillis());
app.put("debug", true);
// create notification
Entity e = app.testRequest(ServiceAction.POST, 1, "devices", device1.getUuid(), "notifications")
.getEntity();
// validate notification was created successfully
Notification notification = app.getEntityManager().get(e.getUuid(), Notification.class);
assertEquals(
notification.getPayloads().get(notifier.getUuid().toString()),
payload);
// wait for notification to be marked finished and retrieve it back
notification = notificationWaitForComplete(notification);
app.testRequest(ServiceAction.GET, 1, "notifications", e.getUuid());
// receipts are created and queried, wait a bit longer for this to happen as indexing
app.waitForQueueDrainAndRefreshIndex(5000);
// get the receipts entity IDs
List<EntityRef> receipts = getNotificationReceipts(notification);
assertEquals(1, receipts.size());
// Validate the error is the correct type InvalidRegistration
Receipt receipt = app.getEntityManager().get(receipts.get(0), Receipt.class);
assertEquals("InvalidRegistration", receipt.getErrorCode());
}
}