diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java
index 333cb3e..86ed2cf 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2017 Groupon, Inc
- * Copyright 2014-2017 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
*
* The Billing Project 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
@@ -62,6 +62,7 @@ import org.killbill.billing.util.config.definition.PaymentConfig;
import org.killbill.billing.util.config.definition.SecurityConfig;
import org.killbill.bus.api.PersistentBus;
import org.killbill.commons.jdbi.guice.DaoConfig;
+import org.killbill.notificationq.api.NotificationQueueService;
import org.skife.config.ConfigurationObjectFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -107,6 +108,9 @@ public class TestJaxrsBase extends KillbillClient {
protected TestApiListener busHandler;
@Inject
+ protected NotificationQueueService notificationQueueService;
+
+ @Inject
@Named(KillbillServerModule.SHIRO_DATA_SOURCE_ID)
protected DataSource shiroDataSource;
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java
index 4b32eba..6413b6e 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java
@@ -21,22 +21,38 @@ package org.killbill.billing.jaxrs;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.awaitility.Awaitility;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
+import org.joda.time.DateTime;
+import org.killbill.CreatorName;
import org.killbill.billing.client.KillBillClientException;
import org.killbill.billing.client.model.TenantKey;
import org.killbill.billing.jaxrs.json.NotificationJson;
import org.killbill.billing.notification.plugin.api.ExtBusEventType;
+import org.killbill.billing.server.DefaultServerService;
+import org.killbill.billing.server.notifications.PushNotificationKey;
import org.killbill.billing.server.notifications.PushNotificationListener;
+import org.killbill.billing.server.notifications.PushNotificationRetryService;
import org.killbill.billing.tenant.api.TenantKV;
+import org.killbill.notificationq.DefaultNotificationQueueService;
+import org.killbill.notificationq.NotificationQueueDispatcher;
+import org.killbill.notificationq.api.NotificationEvent;
+import org.killbill.notificationq.api.NotificationQueue;
+import org.killbill.notificationq.api.NotificationQueueService.NotificationQueueHandler;
+import org.killbill.notificationq.dao.NotificationEventModelDao;
+import org.killbill.queue.QueueObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
@@ -44,7 +60,11 @@ import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.MoreObjects;
import com.google.common.io.CharStreams;
public class TestPushNotification extends TestJaxrsBase {
@@ -125,6 +145,72 @@ public class TestPushNotification extends TestJaxrsBase {
unregisterTenantForCallback(callback);
}
+ @Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/726")
+ public void testVerify726Backport() throws Exception {
+ // Record an event without the metadata field
+ final PushNotificationKeyPre726 key = new PushNotificationKeyPre726(UUID.randomUUID(),
+ UUID.randomUUID(),
+ UUID.randomUUID().toString(),
+ UUID.randomUUID().toString(),
+ UUID.randomUUID(),
+ 1,
+ UUID.randomUUID().toString());
+ final String eventJson = QueueObjectMapper.get().writeValueAsString(key);
+ // Need to serialize it manually, to reflect the correct class name
+ final NotificationEventModelDao notificationEventModelDao = new NotificationEventModelDao(CreatorName.get(),
+ clock.getUTCNow(),
+ PushNotificationKey.class.getName(),
+ eventJson,
+ UUID.randomUUID(),
+ internalCallContext.getAccountRecordId(),
+ internalCallContext.getTenantRecordId(),
+ UUID.randomUUID(),
+ clock.getUTCNow(),
+ DefaultServerService.SERVER_SERVICE + ":testVerify726Backport");
+
+ final AtomicReference<PushNotificationKey> notification = new AtomicReference<PushNotificationKey>();
+ // Need to create a custom queue to extract the deserialized event
+ final NotificationQueue notificationQueue = notificationQueueService.createNotificationQueue(DefaultServerService.SERVER_SERVICE,
+ "testVerify726Backport",
+ new NotificationQueueHandler() {
+ @Override
+ public void handleReadyNotification(final NotificationEvent notificationKey, final DateTime eventDateTime, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
+ if (!(notificationKey instanceof PushNotificationKey)) {
+ Assert.fail();
+ return;
+ }
+ final PushNotificationKey key = (PushNotificationKey) notificationKey;
+ notification.set(key);
+ }
+ }
+ );
+ try {
+ notificationQueue.startQueue();
+ ((NotificationQueueDispatcher) notificationQueueService).getDao().insertEntry(notificationEventModelDao);
+ Awaitility.await()
+ .atMost(10, TimeUnit.SECONDS)
+ .until(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return notification.get() != null;
+ }
+ });
+ } finally {
+ notificationQueue.stopQueue();
+ }
+
+ final PushNotificationKey retrievedKey = notification.get();
+ Assert.assertEquals(retrievedKey.getTenantId(), key.tenantId);
+ Assert.assertEquals(retrievedKey.getAccountId(), key.accountId);
+ Assert.assertEquals(retrievedKey.getEventType(), key.eventType);
+ Assert.assertEquals(retrievedKey.getObjectType(), key.objectType);
+ Assert.assertEquals(retrievedKey.getObjectId(), key.objectId);
+ Assert.assertEquals(retrievedKey.getAttemptNumber(), (Integer) key.attemptNumber);
+ Assert.assertEquals(retrievedKey.getUrl(), key.url);
+ // New NULL field
+ Assert.assertNull(retrievedKey.getMetaData());
+ }
+
private void unregisterTenantForCallback(final String callback) throws KillBillClientException {
final TenantKey result = killBillClient.getCallbackNotificationForTenant(requestOptions);
Assert.assertEquals(result.getKey(), TenantKV.TenantKey.PUSH_NOTIFICATION_CB.toString());
@@ -374,6 +460,33 @@ public class TestPushNotification extends TestJaxrsBase {
test.setCompleted(withError);
}
}
+ }
+ public static final class PushNotificationKeyPre726 implements NotificationEvent {
+
+ public UUID tenantId;
+ public UUID accountId;
+ public String eventType;
+ public String objectType;
+ public UUID objectId;
+ public int attemptNumber;
+ public String url;
+
+ @JsonCreator
+ public PushNotificationKeyPre726(@JsonProperty("tenantId") final UUID tenantId,
+ @JsonProperty("accountId") final UUID accountId,
+ @JsonProperty("eventType") final String eventType,
+ @JsonProperty("objectType") final String objectType,
+ @JsonProperty("objectId") final UUID objectId,
+ @JsonProperty("attemptNumber") final int attemptNumber,
+ @JsonProperty("url") final String url) {
+ this.tenantId = tenantId;
+ this.accountId = accountId;
+ this.eventType = eventType;
+ this.objectType = objectType;
+ this.objectId = objectId;
+ this.attemptNumber = attemptNumber;
+ this.url = url;
+ }
}
}