killbill-uncached
Changes
analytics/pom.xml 20(+20 -0)
analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java 133(+133 -0)
analytics/src/test/java/com/ning/billing/analytics/MockBusinessSubscriptionTransitionDao.java 52(+52 -0)
pom.xml 5(+5 -0)
Details
analytics/pom.xml 20(+20 -0)
diff --git a/analytics/pom.xml b/analytics/pom.xml
index 3755178..0ead039 100644
--- a/analytics/pom.xml
+++ b/analytics/pom.xml
@@ -74,6 +74,26 @@
</dependency>
<dependency>
<groupId>com.ning.billing</groupId>
+ <artifactId>killbill-account</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-catalog</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-entitlement</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-util</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
<artifactId>killbill-util</artifactId>
<type>test-jar</type>
<scope>test</scope>
diff --git a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
index fbbe2b8..415a002 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -18,149 +18,51 @@ package com.ning.billing.analytics;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
-import com.ning.billing.account.api.IAccount;
-import com.ning.billing.account.api.IAccountUserApi;
-import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
-import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
-import com.ning.billing.entitlement.api.user.ISubscriptionBundle;
import com.ning.billing.entitlement.api.user.ISubscriptionTransition;
-import org.joda.time.DateTime;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.List;
-
public class AnalyticsListener
{
- private static final Logger log = LoggerFactory.getLogger(AnalyticsListener.class);
-
- private final BusinessSubscriptionTransitionDao dao;
- private final IEntitlementUserApi entitlementApi;
- private final IAccountUserApi accountApi;
+ private final BusinessSubscriptionTransitionRecorder bstRecorder;
+ private final BusinessAccountRecorder bacRecorder;
@Inject
- public AnalyticsListener(final BusinessSubscriptionTransitionDao dao, final IEntitlementUserApi entitlementApi, final IAccountUserApi accountApi)
+ public AnalyticsListener(final BusinessSubscriptionTransitionRecorder bstRecorder, final BusinessAccountRecorder bacRecorder)
{
- this.dao = dao;
- this.entitlementApi = entitlementApi;
- this.accountApi = accountApi;
+ this.bstRecorder = bstRecorder;
+ this.bacRecorder = bacRecorder;
}
- /*
- * Disable until we fix IRS to allow for two instances (One for bilr proxy, or for killbill)
- * @Subscribe
- */
- public void handleNotificationChange(ISubscriptionTransition event) {
- switch (event.getTransitionType()) {
- case CREATE:
- subscriptionCreated(event);
- break;
- case CANCEL:
- subscriptionCancelled(event);
- break;
- case CHANGE:
- subscriptionChanged(event);
- break;
- case PAUSE:
- subscriptionPaused(event);
- break;
- case RESUME:
- subscriptionResumed(event);
- break;
- case UNCANCEL:
- break;
- case PHASE:
- subscriptionPhaseChanged(event);
- break;
- default:
- throw new RuntimeException("Unexpected event type " + event.getRequestedTransitionTime());
- }
- }
-
- public void subscriptionCreated(final ISubscriptionTransition created)
+ @Subscribe
+ public void handleSubscriptionTransitionChange(final ISubscriptionTransition event)
{
- final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCreated(created.getNextPlan());
- recordTransition(event, created);
- }
-
- public void subscriptionCancelled(final ISubscriptionTransition cancelled)
- {
- final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(cancelled.getNextPlan());
- recordTransition(event, cancelled);
- }
-
- public void subscriptionChanged(final ISubscriptionTransition changed)
- {
- final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionChanged(changed.getNextPlan());
- recordTransition(event, changed);
- }
-
- public void subscriptionPaused(final ISubscriptionTransition paused)
- {
- final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPaused(paused.getNextPlan());
- recordTransition(event, paused);
- }
-
- public void subscriptionResumed(final ISubscriptionTransition resumed)
- {
- final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionResumed(resumed.getNextPlan());
- recordTransition(event, resumed);
- }
-
- public void subscriptionPhaseChanged(final ISubscriptionTransition phaseChanged)
- {
- final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPhaseChanged(phaseChanged.getNextPlan(), phaseChanged.getNextState());
- recordTransition(event, phaseChanged);
- }
-
- private void recordTransition(final BusinessSubscriptionEvent event, final ISubscriptionTransition transition)
- {
- Currency currency = null;
- String transitionKey = null;
-
- // Retrieve key and currency via the bundle
- final ISubscriptionBundle bundle = entitlementApi.getBundleFromId(transition.getBundleId());
- if (bundle != null) {
- transitionKey = bundle.getKey();
-
- final IAccount account = accountApi.getAccountFromId(bundle.getAccountId());
- if (account != null) {
- currency = account.getCurrency();
- }
- }
-
- // The ISubscriptionTransition interface gives us all the prev/next information we need but the start date
- // of the previous plan. We need to retrieve it from our own transitions table
- DateTime previousEffectiveTransitionTime = null;
- final List<BusinessSubscriptionTransition> transitions = dao.getTransitions(transitionKey);
- if (transitions != null) {
- final BusinessSubscriptionTransition lastTransition = transitions.get(transitions.size() - 1);
- if (lastTransition != null && lastTransition.getNextSubscription() != null) {
- previousEffectiveTransitionTime = lastTransition.getNextSubscription().getStartDate();
- }
+ switch (event.getTransitionType()) {
+ case CREATE:
+ bstRecorder.subscriptionCreated(event);
+ break;
+ case CANCEL:
+ bstRecorder.subscriptionCancelled(event);
+ break;
+ case CHANGE:
+ bstRecorder.subscriptionChanged(event);
+ break;
+ case PAUSE:
+ bstRecorder.subscriptionPaused(event);
+ break;
+ case RESUME:
+ bstRecorder.subscriptionResumed(event);
+ break;
+ case UNCANCEL:
+ break;
+ case PHASE:
+ bstRecorder.subscriptionPhaseChanged(event);
+ break;
+ default:
+ throw new RuntimeException("Unexpected event type " + event.getTransitionType());
}
-
- // TODO Support currency changes
- final BusinessSubscription prevSubscription = new BusinessSubscription(transition.getPreviousPlan(), transition.getPreviousPhase(), currency, previousEffectiveTransitionTime, transition.getPreviousState(), transition.getSubscriptionId(), transition.getBundleId());
- final BusinessSubscription nextSubscription = new BusinessSubscription(transition.getNextPlan(), transition.getNextPhase(), currency, transition.getEffectiveTransitionTime(), transition.getNextState(), transition.getSubscriptionId(), transition.getBundleId());
-
- recordTransition(transitionKey, transition.getRequestedTransitionTime(), event, prevSubscription, nextSubscription);
}
- // Public for now for internal reasons
- public void recordTransition(final String key, final DateTime requestedDateTime, final BusinessSubscriptionEvent event, final BusinessSubscription prevSubscription, final BusinessSubscription nextSubscription)
+ @Subscribe
+ public void handleAccountChange(final Object event)
{
- final BusinessSubscriptionTransition transition = new BusinessSubscriptionTransition(
- key,
- requestedDateTime,
- event,
- prevSubscription,
- nextSubscription
- );
-
- log.info(transition.getEvent() + " " + transition);
- dao.createTransition(transition);
}
}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/api/AnalyticsService.java b/analytics/src/main/java/com/ning/billing/analytics/api/AnalyticsService.java
new file mode 100644
index 0000000..216ff9b
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/api/AnalyticsService.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.analytics.api;
+
+import com.google.inject.Inject;
+import com.ning.billing.analytics.AnalyticsListener;
+import com.ning.billing.lifecycle.LyfecycleHandlerType;
+import com.ning.billing.util.eventbus.IEventBus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AnalyticsService implements IAnalyticsService
+{
+ private static final Logger log = LoggerFactory.getLogger(AnalyticsService.class);
+
+ private static final String ANALYTICS_SERVICE = "analytics-service";
+
+ private final AnalyticsListener listener;
+ private final IEventBus eventBus;
+
+ @Inject
+ public AnalyticsService(final AnalyticsListener listener, final IEventBus eventBus)
+ {
+ this.listener = listener;
+ this.eventBus = eventBus;
+ }
+
+ @Override
+ public String getName()
+ {
+ return ANALYTICS_SERVICE;
+ }
+
+ @LyfecycleHandlerType(LyfecycleHandlerType.LyfecycleLevel.REGISTER_EVENTS)
+ public void registerForNotifications()
+ {
+ try {
+ eventBus.register(listener);
+ }
+ catch (IEventBus.EventBusException e) {
+ log.error("Unable to register to the EventBus!", e);
+ }
+ }
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
new file mode 100644
index 0000000..7c5a6da
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.analytics;
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.IAccount;
+import com.ning.billing.account.api.IAccountUserApi;
+import com.ning.billing.analytics.dao.BusinessAccountDao;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BusinessAccountRecorder
+{
+ private static final Logger log = LoggerFactory.getLogger(BusinessAccountRecorder.class);
+
+ private final BusinessAccountDao dao;
+ private final IAccountUserApi accountApi;
+
+ @Inject
+ public BusinessAccountRecorder(final BusinessAccountDao dao, final IAccountUserApi accountApi)
+ {
+ this.dao = dao;
+ this.accountApi = accountApi;
+ }
+
+ public void subscriptionCreated(final IAccount created)
+ {
+ }
+
+ public void subscriptionUpdated(final IAccount updated)
+ {
+ }
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscription.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscription.java
index dc33209..69c89cd 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscription.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscription.java
@@ -125,16 +125,16 @@ public class BusinessSubscription
mrr = getMrrFromISubscription(currentPhase.getDuration(), price);
}
else {
- price = null;
- mrr = null;
+ price = BigDecimal.ZERO;
+ mrr = BigDecimal.ZERO;
}
}
else {
slug = null;
phase = null;
billingPeriod = null;
- price = null;
- mrr = null;
+ price = BigDecimal.ZERO;
+ mrr = BigDecimal.ZERO;
}
if (currency != null) {
@@ -228,7 +228,7 @@ public class BusinessSubscription
static BigDecimal getMrrFromISubscription(final IDuration duration, final BigDecimal price)
{
if (duration == null || duration.getUnit() == null || duration.getNumber() == 0) {
- return null;
+ return BigDecimal.ZERO;
}
if (duration.getUnit().equals(TimeUnit.UNLIMITED)) {
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
new file mode 100644
index 0000000..c3ecc70
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.analytics;
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.IAccount;
+import com.ning.billing.account.api.IAccountUserApi;
+import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
+import com.ning.billing.entitlement.api.user.ISubscriptionBundle;
+import com.ning.billing.entitlement.api.user.ISubscriptionTransition;
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+public class BusinessSubscriptionTransitionRecorder
+{
+ private static final Logger log = LoggerFactory.getLogger(BusinessSubscriptionTransitionRecorder.class);
+
+ private final BusinessSubscriptionTransitionDao dao;
+ private final IEntitlementUserApi entitlementApi;
+ private final IAccountUserApi accountApi;
+
+ @Inject
+ public BusinessSubscriptionTransitionRecorder(final BusinessSubscriptionTransitionDao dao, final IEntitlementUserApi entitlementApi, final IAccountUserApi accountApi)
+ {
+ this.dao = dao;
+ this.entitlementApi = entitlementApi;
+ this.accountApi = accountApi;
+ }
+
+ public void subscriptionCreated(final ISubscriptionTransition created)
+ {
+ final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCreated(created.getNextPlan());
+ recordTransition(event, created);
+ }
+
+ public void subscriptionCancelled(final ISubscriptionTransition cancelled)
+ {
+ final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(cancelled.getNextPlan());
+ recordTransition(event, cancelled);
+ }
+
+ public void subscriptionChanged(final ISubscriptionTransition changed)
+ {
+ final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionChanged(changed.getNextPlan());
+ recordTransition(event, changed);
+ }
+
+ public void subscriptionPaused(final ISubscriptionTransition paused)
+ {
+ final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPaused(paused.getNextPlan());
+ recordTransition(event, paused);
+ }
+
+ public void subscriptionResumed(final ISubscriptionTransition resumed)
+ {
+ final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionResumed(resumed.getNextPlan());
+ recordTransition(event, resumed);
+ }
+
+ public void subscriptionPhaseChanged(final ISubscriptionTransition phaseChanged)
+ {
+ final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPhaseChanged(phaseChanged.getNextPlan(), phaseChanged.getNextState());
+ recordTransition(event, phaseChanged);
+ }
+
+ public void recordTransition(final BusinessSubscriptionEvent event, final ISubscriptionTransition transition)
+ {
+ Currency currency = null;
+ String transitionKey = null;
+
+ // Retrieve key and currency via the bundle
+ final ISubscriptionBundle bundle = entitlementApi.getBundleFromId(transition.getBundleId());
+ if (bundle != null) {
+ transitionKey = bundle.getKey();
+
+ final IAccount account = accountApi.getAccountFromId(bundle.getAccountId());
+ if (account != null) {
+ currency = account.getCurrency();
+ }
+ }
+
+ // The ISubscriptionTransition interface gives us all the prev/next information we need but the start date
+ // of the previous plan. We need to retrieve it from our own transitions table
+ DateTime previousEffectiveTransitionTime = null;
+ final List<BusinessSubscriptionTransition> transitions = dao.getTransitions(transitionKey);
+ if (transitions != null && transitions.size() > 0) {
+ final BusinessSubscriptionTransition lastTransition = transitions.get(transitions.size() - 1);
+ if (lastTransition != null && lastTransition.getNextSubscription() != null) {
+ previousEffectiveTransitionTime = lastTransition.getNextSubscription().getStartDate();
+ }
+ }
+
+ // TODO Support currency changes
+ final BusinessSubscription prevSubscription = new BusinessSubscription(transition.getPreviousPlan(), transition.getPreviousPhase(), currency, previousEffectiveTransitionTime, transition.getPreviousState(), transition.getSubscriptionId(), transition.getBundleId());
+ final BusinessSubscription nextSubscription = new BusinessSubscription(transition.getNextPlan(), transition.getNextPhase(), currency, transition.getEffectiveTransitionTime(), transition.getNextState(), transition.getSubscriptionId(), transition.getBundleId());
+
+ record(transitionKey, transition.getRequestedTransitionTime(), event, prevSubscription, nextSubscription);
+ }
+
+ // Public for internal reasons
+ public void record(final String key, final DateTime requestedDateTime, final BusinessSubscriptionEvent event, final BusinessSubscription prevSubscription, final BusinessSubscription nextSubscription)
+ {
+ final BusinessSubscriptionTransition transition = new BusinessSubscriptionTransition(
+ key,
+ requestedDateTime,
+ event,
+ prevSubscription,
+ nextSubscription
+ );
+
+ log.info(transition.getEvent() + " " + transition);
+ dao.createTransition(transition);
+ }
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/setup/AnalyticsModule.java b/analytics/src/main/java/com/ning/billing/analytics/setup/AnalyticsModule.java
index 7c74808..59fc392 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/setup/AnalyticsModule.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/setup/AnalyticsModule.java
@@ -18,6 +18,11 @@ package com.ning.billing.analytics.setup;
import com.google.inject.AbstractModule;
+import com.ning.billing.analytics.AnalyticsListener;
+import com.ning.billing.analytics.BusinessAccountRecorder;
+import com.ning.billing.analytics.BusinessSubscriptionTransitionRecorder;
+import com.ning.billing.analytics.api.AnalyticsService;
+import com.ning.billing.analytics.api.IAnalyticsService;
import com.ning.billing.analytics.dao.BusinessAccountDao;
import com.ning.billing.analytics.dao.BusinessAccountDaoProvider;
import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
@@ -30,5 +35,11 @@ public class AnalyticsModule extends AbstractModule
{
bind(BusinessSubscriptionTransitionDao.class).toProvider(BusinessSubscriptionTransitionDaoProvider.class).asEagerSingleton();
bind(BusinessAccountDao.class).toProvider(BusinessAccountDaoProvider.class).asEagerSingleton();
+
+ bind(BusinessSubscriptionTransitionRecorder.class).asEagerSingleton();
+ bind(BusinessAccountRecorder.class).asEagerSingleton();
+ bind(AnalyticsListener.class).asEagerSingleton();
+
+ bind(IAnalyticsService.class).to(AnalyticsService.class).asEagerSingleton();
}
}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/utils/Rounder.java b/analytics/src/main/java/com/ning/billing/analytics/utils/Rounder.java
index 0f0adef..ab5728d 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/utils/Rounder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/utils/Rounder.java
@@ -22,6 +22,11 @@ public class Rounder
{
public static final int SCALE = 4;
+ // Static only
+ private Rounder()
+ {
+ }
+
public static double round(final BigDecimal decimal)
{
if (decimal == null) {
diff --git a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
new file mode 100644
index 0000000..03e102d
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.analytics;
+
+import com.ning.billing.account.glue.AccountModule;
+import com.ning.billing.analytics.setup.AnalyticsModule;
+import com.ning.billing.catalog.glue.CatalogModule;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.glue.EntitlementModule;
+import com.ning.billing.util.glue.EventBusModule;
+import org.skife.jdbi.v2.DBI;
+import org.skife.jdbi.v2.IDBI;
+
+public class AnalyticsTestModule extends AnalyticsModule
+{
+ @Override
+ protected void configure()
+ {
+ super.configure();
+
+ // Need to configure a few more things for the EventBus
+ install(new AccountModule());
+ install(new CatalogModule());
+ install(new EventBusModule());
+ install(new EntitlementModule());
+
+ // Install the Dao layer
+ final MysqlTestingHelper helper = new MysqlTestingHelper();
+ bind(MysqlTestingHelper.class).toInstance(helper);
+ final DBI dbi = helper.getDBI();
+ bind(IDBI.class).toInstance(dbi);
+ bind(DBI.class).toInstance(dbi);
+ }
+}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
new file mode 100644
index 0000000..aae04e7
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.analytics.api;
+
+import com.google.inject.Inject;
+import com.ning.billing.analytics.AnalyticsTestModule;
+import com.ning.billing.analytics.BusinessSubscription;
+import com.ning.billing.analytics.BusinessSubscriptionEvent;
+import com.ning.billing.analytics.BusinessSubscriptionTransition;
+import com.ning.billing.analytics.MockAccount;
+import com.ning.billing.analytics.MockDuration;
+import com.ning.billing.analytics.MockPhase;
+import com.ning.billing.analytics.MockPlan;
+import com.ning.billing.analytics.MockProduct;
+import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
+import com.ning.billing.catalog.api.IPlan;
+import com.ning.billing.catalog.api.IPlanPhase;
+import com.ning.billing.catalog.api.IProduct;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
+import com.ning.billing.entitlement.api.user.ISubscription;
+import com.ning.billing.entitlement.api.user.ISubscriptionBundle;
+import com.ning.billing.entitlement.api.user.ISubscriptionTransition;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.entitlement.events.IEvent;
+import com.ning.billing.entitlement.events.user.ApiEventType;
+import com.ning.billing.util.eventbus.IEventBus;
+import org.apache.commons.io.IOUtils;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.UUID;
+
+@Guice(modules = AnalyticsTestModule.class)
+public class TestAnalyticsService
+{
+ private static final String KEY = "1234";
+
+ @Inject
+ private IEntitlementUserApi entitlementApi;
+
+ @Inject
+ private AnalyticsService service;
+
+ @Inject
+ private IEventBus bus;
+
+ @Inject
+ private BusinessSubscriptionTransitionDao dao;
+
+ @Inject
+ private MysqlTestingHelper helper;
+
+ private ISubscriptionTransition transition;
+ private BusinessSubscriptionTransition expectedTransition;
+
+ @BeforeClass(alwaysRun = true)
+ public void startMysql() throws IOException, ClassNotFoundException, SQLException, EntitlementUserApiException
+ {
+ final String analyticsDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/analytics/ddl.sql"));
+ // For bundles
+ final String accountDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
+ final String entitlementDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
+
+ helper.startMysql();
+ helper.initDb(analyticsDdl);
+ helper.initDb(accountDdl);
+ helper.initDb(entitlementDdl);
+
+ // We need a bundle to retrieve the event key
+ final ISubscriptionBundle bundle = entitlementApi.createBundleForAccount(new MockAccount(KEY), KEY);
+
+ // Verify we correctly initialized the account subsystem
+ Assert.assertNotNull(bundle);
+ Assert.assertEquals(bundle.getKey(), KEY);
+
+ // Create a subscription transition
+ final IProduct product = new MockProduct("platinium", "subscription", ProductCategory.BASE);
+ final IPlan plan = new MockPlan("platinum-monthly", product);
+ final IPlanPhase phase = new MockPhase(PhaseType.EVERGREEN, plan, MockDuration.UNLIMITED(), 25.95);
+ final UUID subscriptionId = UUID.randomUUID();
+ final DateTime effectiveTransitionTime = new DateTime(DateTimeZone.UTC);
+ final DateTime requestedTransitionTime = new DateTime(DateTimeZone.UTC);
+ transition = new SubscriptionTransition(
+ subscriptionId,
+ bundle.getId(),
+ IEvent.EventType.API_USER,
+ ApiEventType.CREATE,
+ requestedTransitionTime,
+ effectiveTransitionTime,
+ null,
+ null,
+ null,
+ null,
+ ISubscription.SubscriptionState.ACTIVE,
+ plan,
+ phase,
+ "something"
+ );
+ expectedTransition = new BusinessSubscriptionTransition(
+ KEY,
+ requestedTransitionTime,
+ BusinessSubscriptionEvent.subscriptionCreated(plan),
+ null,
+ new BusinessSubscription(plan, phase, null, effectiveTransitionTime, ISubscription.SubscriptionState.ACTIVE, subscriptionId, bundle.getId())
+ );
+ }
+
+ @AfterClass(alwaysRun = true)
+ public void stopMysql()
+ {
+ helper.stopMysql();
+ }
+
+ @Test(groups = "slow")
+ public void testRegisterForNotifications() throws Exception
+ {
+ // Make sure the service has been instantiated
+ Assert.assertEquals(service.getName(), "analytics-service");
+
+ // Test the bus and make sure we can register our service
+ try {
+ bus.start();
+ service.registerForNotifications();
+ }
+ catch (Throwable t) {
+ Assert.fail("Unable to start the bus or service! " + t);
+ }
+
+ // Send an event to the bus and make sure our Dao got it
+ bus.post(transition);
+ Thread.sleep(1000);
+ Assert.assertEquals(dao.getTransitions(KEY).size(), 1);
+ Assert.assertEquals(dao.getTransitions(KEY).get(0), expectedTransition);
+
+ // Test the shutdown sequence
+ try {
+ bus.stop();
+ }
+ catch (Throwable t) {
+ Assert.fail("Unable to stop the bus!");
+ }
+ }
+}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java b/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java
index d575ee2..defdcef 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java
@@ -40,6 +40,7 @@ import org.skife.jdbi.v2.IDBI;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.io.IOException;
@@ -55,6 +56,9 @@ public class TestAnalyticsDao
private static final String ACCOUNT_KEY = "pierre-143343-vcc";
private final MysqlTestingHelper helper = new MysqlTestingHelper();
+ private final IProduct product = new MockProduct("platinium", "subscription", ProductCategory.BASE);
+ private final IPlan plan = new MockPlan("platinum-monthly", product);
+ private final IPlanPhase phase = new MockPhase(PhaseType.EVERGREEN, plan, MockDuration.UNLIMITED(), 25.95);
private BusinessSubscriptionTransitionDao businessSubscriptionTransitionDao;
private BusinessSubscriptionTransition transition;
@@ -75,10 +79,6 @@ public class TestAnalyticsDao
private void setupBusinessSubscriptionTransition()
{
- final IProduct product = new MockProduct("platinium", "subscription", ProductCategory.BASE);
- final IPlan plan = new MockPlan("platinum-monthly", product);
- final IPlanPhase phase = new MockPhase(PhaseType.EVERGREEN, plan, MockDuration.UNLIMITED(), 25.95);
-
final BusinessSubscription prevSubscription = new BusinessSubscription(plan, phase, Currency.USD, new DateTime(DateTimeZone.UTC), ISubscription.SubscriptionState.ACTIVE, UUID.randomUUID(), UUID.randomUUID());
final BusinessSubscription nextSubscription = new BusinessSubscription(plan, phase, Currency.USD, new DateTime(DateTimeZone.UTC), ISubscription.SubscriptionState.CANCELLED, UUID.randomUUID(), UUID.randomUUID());
final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(plan);
@@ -123,6 +123,131 @@ public class TestAnalyticsDao
helper.stopMysql();
}
+ @BeforeMethod
+ public void cleanup() throws Exception
+ {
+ helper.cleanupTable("bst");
+ }
+
+ @Test(groups = "slow")
+ public void testTransitionsWithNullPrevSubscription()
+ {
+ final BusinessSubscriptionTransition transitionWithNullPrev = new BusinessSubscriptionTransition(
+ transition.getKey(),
+ transition.getRequestedTimestamp(),
+ transition.getEvent(),
+ null,
+ transition.getNextSubscription()
+ );
+ businessSubscriptionTransitionDao.createTransition(transitionWithNullPrev);
+
+ final List<BusinessSubscriptionTransition> transitions = businessSubscriptionTransitionDao.getTransitions(EVENT_KEY);
+ Assert.assertEquals(transitions.size(), 1);
+ Assert.assertEquals(transitions.get(0), transitionWithNullPrev);
+ }
+
+ @Test(groups = "slow")
+ public void testTransitionsWithNullNextSubscription()
+ {
+ final BusinessSubscriptionTransition transitionWithNullNext = new BusinessSubscriptionTransition(
+ transition.getKey(),
+ transition.getRequestedTimestamp(),
+ transition.getEvent(),
+ transition.getPreviousSubscription(),
+ null
+ );
+ businessSubscriptionTransitionDao.createTransition(transitionWithNullNext);
+
+ final List<BusinessSubscriptionTransition> transitions = businessSubscriptionTransitionDao.getTransitions(EVENT_KEY);
+ Assert.assertEquals(transitions.size(), 1);
+ Assert.assertEquals(transitions.get(0), transitionWithNullNext);
+ }
+
+ @Test(groups = "slow")
+ public void testTransitionsWithNullFieldsInSubscription()
+ {
+ final BusinessSubscription subscriptionWithNullFields = new BusinessSubscription(plan, phase, Currency.USD, null, null, null, null);
+ final BusinessSubscriptionTransition transitionWithNullFields = new BusinessSubscriptionTransition(
+ transition.getKey(),
+ transition.getRequestedTimestamp(),
+ transition.getEvent(),
+ subscriptionWithNullFields,
+ subscriptionWithNullFields
+ );
+ businessSubscriptionTransitionDao.createTransition(transitionWithNullFields);
+
+ final List<BusinessSubscriptionTransition> transitions = businessSubscriptionTransitionDao.getTransitions(EVENT_KEY);
+ Assert.assertEquals(transitions.size(), 1);
+ Assert.assertEquals(transitions.get(0), transitionWithNullFields);
+ }
+
+ @Test(groups = "slow")
+ public void testTransitionsWithNullPlanAndPhase() throws Exception
+ {
+ final BusinessSubscription subscriptionWithNullPlanAndPhase = new BusinessSubscription(null, null, Currency.USD, null, null, null, null);
+ final BusinessSubscriptionTransition transitionWithNullPlanAndPhase = new BusinessSubscriptionTransition(
+ transition.getKey(),
+ transition.getRequestedTimestamp(),
+ transition.getEvent(),
+ subscriptionWithNullPlanAndPhase,
+ subscriptionWithNullPlanAndPhase
+ );
+ businessSubscriptionTransitionDao.createTransition(transitionWithNullPlanAndPhase);
+
+ final List<BusinessSubscriptionTransition> transitions = businessSubscriptionTransitionDao.getTransitions(EVENT_KEY);
+ Assert.assertEquals(transitions.size(), 1);
+ Assert.assertEquals(transitions.get(0).getKey(), transition.getKey());
+ Assert.assertEquals(transitions.get(0).getRequestedTimestamp(), transition.getRequestedTimestamp());
+ Assert.assertEquals(transitions.get(0).getEvent(), transition.getEvent());
+ // Null Plan and Phase doesn't make sense so we turn the subscription into a null
+ Assert.assertNull(transitions.get(0).getPreviousSubscription());
+ Assert.assertNull(transitions.get(0).getNextSubscription());
+ }
+
+ @Test(groups = "slow")
+ public void testTransitionsWithNullPlan() throws Exception
+ {
+ final BusinessSubscription subscriptionWithNullPlan = new BusinessSubscription(null, phase, Currency.USD, null, null, null, null);
+ final BusinessSubscriptionTransition transitionWithNullPlan = new BusinessSubscriptionTransition(
+ transition.getKey(),
+ transition.getRequestedTimestamp(),
+ transition.getEvent(),
+ subscriptionWithNullPlan,
+ subscriptionWithNullPlan
+ );
+ businessSubscriptionTransitionDao.createTransition(transitionWithNullPlan);
+
+ final List<BusinessSubscriptionTransition> transitions = businessSubscriptionTransitionDao.getTransitions(EVENT_KEY);
+ Assert.assertEquals(transitions.size(), 1);
+ // Null Plan but Phase - we don't turn the subscription into a null
+ Assert.assertEquals(transitions.get(0), transitionWithNullPlan);
+ }
+
+ @Test(groups = "slow")
+ public void testTransitionsWithNullPhase() throws Exception
+ {
+ final BusinessSubscription subscriptionWithNullPhase = new BusinessSubscription(plan, null, Currency.USD, null, null, null, null);
+ final BusinessSubscriptionTransition transitionWithNullPhase = new BusinessSubscriptionTransition(
+ transition.getKey(),
+ transition.getRequestedTimestamp(),
+ transition.getEvent(),
+ subscriptionWithNullPhase,
+ subscriptionWithNullPhase
+ );
+ businessSubscriptionTransitionDao.createTransition(transitionWithNullPhase);
+
+ final List<BusinessSubscriptionTransition> transitions = businessSubscriptionTransitionDao.getTransitions(EVENT_KEY);
+ Assert.assertEquals(transitions.size(), 1);
+ Assert.assertEquals(transitions.get(0).getKey(), transition.getKey());
+ Assert.assertEquals(transitions.get(0).getRequestedTimestamp(), transition.getRequestedTimestamp());
+ Assert.assertEquals(transitions.get(0).getEvent(), transition.getEvent());
+
+ // Null Phase but Plan - we don't turn the subscription into a null, however price and mrr are both set to 0 (not null)
+ final BusinessSubscription blankSubscription = new BusinessSubscription(plan, new MockPhase(null, null, null, 0.0), Currency.USD, null, null, null, null);
+ Assert.assertEquals(transitions.get(0).getPreviousSubscription(), blankSubscription);
+ Assert.assertEquals(transitions.get(0).getNextSubscription(), blankSubscription);
+ }
+
@Test(groups = "slow")
public void testCreateAndRetrieveTransitions()
{
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java b/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
new file mode 100644
index 0000000..c2ab5fd
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.analytics;
+
+import com.ning.billing.account.api.IAccount;
+import com.ning.billing.catalog.api.Currency;
+
+import java.util.UUID;
+
+public class MockAccount implements IAccount
+{
+ private final String key;
+
+ public MockAccount(final String key)
+ {
+ this.key = key;
+ }
+
+ @Override
+ public String getName()
+ {
+ return "accountName";
+ }
+
+ @Override
+ public String getEmail()
+ {
+ return "accountName@yahoo.com";
+ }
+
+ @Override
+ public String getPhone()
+ {
+ return "4152876341";
+ }
+
+ @Override
+ public String getKey()
+ {
+ return key;
+ }
+
+ @Override
+ public int getBillCycleDay()
+ {
+ return 1;
+ }
+
+ @Override
+ public Currency getCurrency()
+ {
+ return Currency.USD;
+ }
+
+ @Override
+ public UUID getId()
+ {
+ return UUID.randomUUID();
+ }
+
+ @Override
+ public void load()
+ {
+ }
+
+ @Override
+ public void save()
+ {
+ }
+
+ @Override
+ public String getFieldValue(final String fieldName)
+ {
+ return null;
+ }
+
+ @Override
+ public void setFieldValue(final String fieldName, final String fieldValue)
+ {
+ }
+}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockBusinessSubscriptionTransitionDao.java b/analytics/src/test/java/com/ning/billing/analytics/MockBusinessSubscriptionTransitionDao.java
new file mode 100644
index 0000000..d7e36b4
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockBusinessSubscriptionTransitionDao.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.analytics;
+
+import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionBinder;
+import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
+import org.skife.jdbi.v2.sqlobject.Bind;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MockBusinessSubscriptionTransitionDao implements BusinessSubscriptionTransitionDao
+{
+ private final Map<String, List<BusinessSubscriptionTransition>> content = new HashMap<String, List<BusinessSubscriptionTransition>>();
+
+ @Override
+ public List<BusinessSubscriptionTransition> getTransitions(@Bind("event_key") final String key)
+ {
+ return content.get(key);
+ }
+
+ @Override
+ public int createTransition(@BusinessSubscriptionTransitionBinder final BusinessSubscriptionTransition transition)
+ {
+ if (content.get(transition.getKey()) == null) {
+ content.put(transition.getKey(), new ArrayList<BusinessSubscriptionTransition>());
+ }
+ content.get(transition.getKey()).add(transition);
+ return 1;
+ }
+
+ @Override
+ public void test()
+ {
+ }
+}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java b/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
new file mode 100644
index 0000000..64fe29f
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.analytics;
+
+import com.ning.billing.account.api.IAccount;
+import com.ning.billing.account.api.IAccountData;
+import com.ning.billing.account.api.IAccountUserApi;
+
+import java.util.List;
+import java.util.UUID;
+
+public class MockIAccountUserApi implements IAccountUserApi
+{
+ @Override
+ public IAccount createAccount(final IAccountData data)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public IAccount getAccountByKey(final String key)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public IAccount getAccountFromId(final UUID uid)
+ {
+ return null;
+ }
+
+ @Override
+ public List<IAccount> getAccounts()
+ {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockIEntitlementUserApi.java b/analytics/src/test/java/com/ning/billing/analytics/MockIEntitlementUserApi.java
new file mode 100644
index 0000000..4ed2e8c
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockIEntitlementUserApi.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.analytics;
+
+import com.ning.billing.account.api.IAccount;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
+import com.ning.billing.entitlement.api.user.ISubscription;
+import com.ning.billing.entitlement.api.user.ISubscriptionBundle;
+import org.joda.time.DateTime;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+public class MockIEntitlementUserApi implements IEntitlementUserApi
+{
+ private final Map<UUID, String> subscriptionBundles = new HashMap<UUID, String>();
+
+ public MockIEntitlementUserApi(final UUID bundleUUID, final String key)
+ {
+ subscriptionBundles.put(bundleUUID, key);
+ }
+
+ @Override
+ public ISubscriptionBundle getBundleFromId(final UUID id)
+ {
+ final String key = subscriptionBundles.get(id);
+ if (key == null) {
+ return null;
+ }
+
+ return new ISubscriptionBundle()
+ {
+ @Override
+ public UUID getAccountId()
+ {
+ return UUID.randomUUID();
+ }
+
+ @Override
+ public UUID getId()
+ {
+ return id;
+ }
+
+ @Override
+ public DateTime getStartDate()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getKey()
+ {
+ return key;
+ }
+
+ @Override
+ public void setPrivate(final String name, final String value)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getPrivate(final String name)
+ {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ @Override
+ public ISubscription getSubscriptionFromId(final UUID id)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<ISubscriptionBundle> getBundlesForAccount(final UUID accountId)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<ISubscription> getSubscriptionsForBundle(final UUID bundleId)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ISubscriptionBundle createBundleForAccount(final IAccount account, final String bundleKey) throws EntitlementUserApiException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ISubscription createSubscription(final UUID bundleId, final String productName, final BillingPeriod term, final String planSet, final DateTime requestedDate) throws EntitlementUserApiException
+ {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockPhase.java b/analytics/src/test/java/com/ning/billing/analytics/MockPhase.java
index 4912957..2303e1d 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockPhase.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockPhase.java
@@ -16,9 +16,6 @@
package com.ning.billing.analytics;
-import java.math.BigDecimal;
-import java.util.Date;
-
import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.catalog.api.IDuration;
@@ -28,6 +25,9 @@ import com.ning.billing.catalog.api.IPlanPhase;
import com.ning.billing.catalog.api.IPrice;
import com.ning.billing.catalog.api.PhaseType;
+import java.math.BigDecimal;
+import java.util.Date;
+
public class MockPhase implements IPlanPhase
{
private final PhaseType cohort;
@@ -60,10 +60,11 @@ public class MockPhase implements IPlanPhase
return BigDecimal.valueOf(price);
}
- @Override
- public Date getEffectiveDateForExistingSubscriptons() {
- return new Date();
- }
+ @Override
+ public Date getEffectiveDateForExistingSubscriptons()
+ {
+ return new Date();
+ }
};
}
@@ -83,11 +84,12 @@ public class MockPhase implements IPlanPhase
{
return BigDecimal.valueOf(price);
}
-
- @Override
- public Date getEffectiveDateForExistingSubscriptons() {
- return new Date();
- }
+
+ @Override
+ public Date getEffectiveDateForExistingSubscriptons()
+ {
+ return new Date();
+ }
};
}
@@ -100,7 +102,12 @@ public class MockPhase implements IPlanPhase
@Override
public String getName()
{
- return plan.getName() + "-" + cohort;
+ if (plan == null) {
+ return null;
+ }
+ else {
+ return plan.getName() + "-" + cohort;
+ }
}
@Override
diff --git a/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java b/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
new file mode 100644
index 0000000..9f13e2e
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.analytics;
+
+import com.ning.billing.catalog.api.IPlan;
+import com.ning.billing.catalog.api.IPlanPhase;
+import com.ning.billing.catalog.api.IProduct;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.user.ISubscription;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.entitlement.events.IEvent;
+import com.ning.billing.entitlement.events.user.ApiEventType;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.util.UUID;
+
+public class TestAnalyticsListener
+{
+ private static final String KEY = "1234";
+
+ private final MockBusinessSubscriptionTransitionDao dao = new MockBusinessSubscriptionTransitionDao();
+ private final UUID subscriptionId = UUID.randomUUID();
+ private final UUID bundleUUID = UUID.randomUUID();
+ private final IProduct product = new MockProduct("platinium", "subscription", ProductCategory.BASE);
+ private final IPlan plan = new MockPlan("platinum-monthly", product);
+ private final IPlanPhase phase = new MockPhase(PhaseType.EVERGREEN, plan, MockDuration.UNLIMITED(), 25.95);
+ private final String priceList = "something";
+
+ private AnalyticsListener listener;
+
+ @BeforeMethod(alwaysRun = true)
+ public void setUp() throws Exception
+ {
+ final BusinessSubscriptionTransitionRecorder recorder = new BusinessSubscriptionTransitionRecorder(dao, new MockIEntitlementUserApi(bundleUUID, KEY), new MockIAccountUserApi());
+ listener = new AnalyticsListener(recorder, null);
+ }
+
+ @Test(groups = "fast")
+ public void testSubscriptionLifecycle() throws Exception
+ {
+ // Create a subscription
+ final DateTime effectiveTransitionTime = new DateTime(DateTimeZone.UTC);
+ final DateTime requestedTransitionTime = new DateTime(DateTimeZone.UTC);
+ final SubscriptionTransition firstTransition = createFirstSubscriptionTransition(requestedTransitionTime, effectiveTransitionTime);
+ final BusinessSubscriptionTransition firstBST = createExpectedFirstBST(requestedTransitionTime, effectiveTransitionTime);
+ listener.handleSubscriptionTransitionChange(firstTransition);
+ Assert.assertEquals(dao.getTransitions(KEY).size(), 1);
+ Assert.assertEquals(dao.getTransitions(KEY).get(0), firstBST);
+
+ // Pause it
+ final DateTime effectivePauseTransitionTime = new DateTime(DateTimeZone.UTC);
+ final DateTime requestedPauseTransitionTime = new DateTime(DateTimeZone.UTC);
+ final SubscriptionTransition pausedSubscriptionTransition = createPauseSubscriptionTransition(effectivePauseTransitionTime, requestedPauseTransitionTime, firstTransition.getNextState());
+ final BusinessSubscriptionTransition pausedBST = createExpectedPausedBST(requestedPauseTransitionTime, effectivePauseTransitionTime, firstBST.getNextSubscription());
+ listener.handleSubscriptionTransitionChange(pausedSubscriptionTransition);
+ Assert.assertEquals(dao.getTransitions(KEY).size(), 2);
+ Assert.assertEquals(dao.getTransitions(KEY).get(1), pausedBST);
+
+ // Un-Pause it
+ final DateTime effectiveResumeTransitionTime = new DateTime(DateTimeZone.UTC);
+ final DateTime requestedResumeTransitionTime = new DateTime(DateTimeZone.UTC);
+ final SubscriptionTransition resumedSubscriptionTransition = createResumeSubscriptionTransition(requestedResumeTransitionTime, effectiveResumeTransitionTime, pausedSubscriptionTransition.getNextState());
+ final BusinessSubscriptionTransition resumedBST = createExpectedResumedBST(requestedResumeTransitionTime, effectiveResumeTransitionTime, pausedBST.getNextSubscription());
+ listener.handleSubscriptionTransitionChange(resumedSubscriptionTransition);
+ Assert.assertEquals(dao.getTransitions(KEY).size(), 3);
+ Assert.assertEquals(dao.getTransitions(KEY).get(2), resumedBST);
+
+ // Cancel it
+ final DateTime effectiveCancelTransitionTime = new DateTime(DateTimeZone.UTC);
+ final DateTime requestedCancelTransitionTime = new DateTime(DateTimeZone.UTC);
+ listener.handleSubscriptionTransitionChange(createCancelSubscriptionTransition(requestedCancelTransitionTime, effectiveCancelTransitionTime, resumedSubscriptionTransition.getNextState()));
+ final BusinessSubscriptionTransition cancelledBST = createExpectedCancelledBST(requestedCancelTransitionTime, effectiveCancelTransitionTime, resumedBST.getNextSubscription());
+ Assert.assertEquals(dao.getTransitions(KEY).size(), 4);
+ Assert.assertEquals(dao.getTransitions(KEY).get(3), cancelledBST);
+ }
+
+ private BusinessSubscriptionTransition createExpectedFirstBST(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime)
+ {
+ final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCreated(plan);
+ final ISubscription.SubscriptionState subscriptionState = ISubscription.SubscriptionState.ACTIVE;
+ final BusinessSubscription emptyBST = new BusinessSubscription(null, null, null, null, null, subscriptionId, bundleUUID);
+ return createExpectedBST(event, requestedTransitionTime, effectiveTransitionTime, emptyBST, subscriptionState);
+ }
+
+ private BusinessSubscriptionTransition createExpectedPausedBST(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final BusinessSubscription lastSubscription)
+ {
+ final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPaused(plan);
+ final ISubscription.SubscriptionState subscriptionState = ISubscription.SubscriptionState.PAUSED;
+ return createExpectedBST(event, requestedTransitionTime, effectiveTransitionTime, lastSubscription, subscriptionState);
+ }
+
+ private BusinessSubscriptionTransition createExpectedResumedBST(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final BusinessSubscription lastSubscription)
+ {
+ final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionResumed(plan);
+ final ISubscription.SubscriptionState nextState = ISubscription.SubscriptionState.ACTIVE;
+ return createExpectedBST(event, requestedTransitionTime, effectiveTransitionTime, lastSubscription, nextState);
+ }
+
+ private BusinessSubscriptionTransition createExpectedCancelledBST(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final BusinessSubscription lastSubscription)
+ {
+ final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(plan);
+ final ISubscription.SubscriptionState nextState = ISubscription.SubscriptionState.CANCELLED;
+ return createExpectedBST(event, requestedTransitionTime, effectiveTransitionTime, lastSubscription, nextState);
+ }
+
+ private BusinessSubscriptionTransition createExpectedBST(
+ final BusinessSubscriptionEvent eventType,
+ final DateTime requestedTransitionTime,
+ final DateTime effectiveTransitionTime,
+ final BusinessSubscription previousSubscription,
+ final ISubscription.SubscriptionState nextState
+ )
+ {
+ return new BusinessSubscriptionTransition(
+ KEY,
+ requestedTransitionTime,
+ eventType,
+ previousSubscription,
+ new BusinessSubscription(
+ plan,
+ phase,
+ null,
+ effectiveTransitionTime,
+ nextState,
+ subscriptionId,
+ bundleUUID
+ )
+ );
+ }
+
+ private SubscriptionTransition createFirstSubscriptionTransition(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime)
+ {
+ final ApiEventType eventType = ApiEventType.CREATE;
+ final ISubscription.SubscriptionState nextState = ISubscription.SubscriptionState.ACTIVE;
+ return new SubscriptionTransition(
+ subscriptionId,
+ bundleUUID,
+ IEvent.EventType.API_USER,
+ eventType,
+ requestedTransitionTime,
+ effectiveTransitionTime,
+ null,
+ null,
+ null,
+ null,
+ nextState,
+ plan,
+ phase,
+ priceList
+ );
+ }
+
+ private SubscriptionTransition createPauseSubscriptionTransition(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final ISubscription.SubscriptionState previousState)
+ {
+ final ApiEventType eventType = ApiEventType.PAUSE;
+ final ISubscription.SubscriptionState nextState = ISubscription.SubscriptionState.PAUSED;
+ return createSubscriptionTransition(eventType, requestedTransitionTime, effectiveTransitionTime, previousState, nextState);
+ }
+
+ private SubscriptionTransition createResumeSubscriptionTransition(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final ISubscription.SubscriptionState previousState)
+ {
+ final ApiEventType eventType = ApiEventType.RESUME;
+ final ISubscription.SubscriptionState nextState = ISubscription.SubscriptionState.ACTIVE;
+ return createSubscriptionTransition(eventType, requestedTransitionTime, effectiveTransitionTime, previousState, nextState);
+ }
+
+ private SubscriptionTransition createCancelSubscriptionTransition(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final ISubscription.SubscriptionState previousState)
+ {
+ final ApiEventType eventType = ApiEventType.CANCEL;
+ final ISubscription.SubscriptionState nextState = ISubscription.SubscriptionState.CANCELLED;
+ return createSubscriptionTransition(eventType, requestedTransitionTime, effectiveTransitionTime, previousState, nextState);
+ }
+
+ private SubscriptionTransition createSubscriptionTransition(
+ final ApiEventType eventType,
+ final DateTime requestedTransitionTime,
+ final DateTime effectiveTransitionTime,
+ final ISubscription.SubscriptionState previousState,
+ final ISubscription.SubscriptionState nextState
+ )
+ {
+ return new SubscriptionTransition(
+ subscriptionId,
+ bundleUUID,
+ IEvent.EventType.API_USER,
+ eventType,
+ requestedTransitionTime,
+ effectiveTransitionTime,
+ previousState,
+ plan,
+ phase,
+ priceList,
+ nextState,
+ plan,
+ phase,
+ priceList
+ );
+ }
+}
\ No newline at end of file
diff --git a/analytics/src/test/java/com/ning/billing/analytics/utils/TestRounder.java b/analytics/src/test/java/com/ning/billing/analytics/utils/TestRounder.java
new file mode 100644
index 0000000..b3414d7
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/utils/TestRounder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.analytics.utils;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.math.BigDecimal;
+
+public class TestRounder
+{
+ @Test(groups = "fast")
+ public void testRound() throws Exception
+ {
+ Assert.assertEquals(Rounder.round(null), 0.0);
+ Assert.assertEquals(Rounder.round(BigDecimal.ZERO), 0.0);
+ Assert.assertEquals(Rounder.round(BigDecimal.ONE), 1.0);
+ Assert.assertEquals(Rounder.round(BigDecimal.TEN), 10.0);
+ Assert.assertEquals(Rounder.round(BigDecimal.valueOf(1.33333)), 1.3333);
+ Assert.assertEquals(Rounder.round(BigDecimal.valueOf(4444.33333)), 4444.3333);
+ Assert.assertEquals(Rounder.round(BigDecimal.valueOf(10.11111)), 10.1111);
+ Assert.assertEquals(Rounder.round(BigDecimal.valueOf(10.11115)), 10.1112);
+ Assert.assertEquals(Rounder.round(BigDecimal.valueOf(10.11116)), 10.1112);
+ }
+}
diff --git a/api/src/main/java/com/ning/billing/analytics/api/IAnalyticsService.java b/api/src/main/java/com/ning/billing/analytics/api/IAnalyticsService.java
new file mode 100644
index 0000000..2a5d0c5
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/analytics/api/IAnalyticsService.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.analytics.api;
+
+import com.ning.billing.lifecycle.IService;
+
+public interface IAnalyticsService extends IService {
+}
diff --git a/catalog/src/main/java/com/ning/billing/catalog/Plan.java b/catalog/src/main/java/com/ning/billing/catalog/Plan.java
index 9a994f0..5b0a926 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/Plan.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/Plan.java
@@ -119,10 +119,12 @@ public class Plan extends ValidatingConfig<Catalog> implements IPlan {
super.initialize(catalog, sourceURI);
if(finalPhase != null) {
finalPhase.setPlan(this);
+ finalPhase.initialize(catalog, sourceURI);
}
if(initialPhases != null) {
for(PlanPhase p : initialPhases) {
p.setPlan(this);
+ p.initialize(catalog, sourceURI);
}
}
}
diff --git a/catalog/src/main/java/com/ning/billing/catalog/PlanPhase.java b/catalog/src/main/java/com/ning/billing/catalog/PlanPhase.java
index 821d1f5..61dab1e 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/PlanPhase.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/PlanPhase.java
@@ -138,15 +138,11 @@ public class PlanPhase extends ValidatingConfig<Catalog> implements IPlanPhase {
}
-
@Override
public void initialize(Catalog root, URI uri) {
- fixedPrice.initialize(root, uri);
- recurringPrice.initialize(root, uri);
+ if (fixedPrice != null) { fixedPrice.initialize(root, uri); }
+ if (recurringPrice != null) { recurringPrice.initialize(root, uri); }
}
-
-
-
protected PlanPhase setFixedPrice(InternationalPrice price) {
this.fixedPrice = price;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
index 5280209..05f6e9d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
@@ -276,6 +276,11 @@ public class Subscription extends PrivateFields implements ISubscription {
try {
IProduct destProduct = catalog.getProductFromName(productName);
+ // STEPH really catalog exception
+ if (destProduct == null) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_BAD_CATALOG,
+ productName, term.toString(), "");
+ }
IPlan currentPlan = getCurrentPlan();
PlanPhaseSpecifier fromPlanPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
pom.xml 5(+5 -0)
diff --git a/pom.xml b/pom.xml
index 8038652..4ed2b26 100644
--- a/pom.xml
+++ b/pom.xml
@@ -56,6 +56,11 @@
</dependency>
<dependency>
<groupId>com.ning.billing</groupId>
+ <artifactId>killbill-account</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
<artifactId>killbill-entitlement</artifactId>
<version>${project.version}</version>
</dependency>
diff --git a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
index d8aad63..e8a77f6 100644
--- a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
+++ b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
@@ -25,6 +25,7 @@ import org.skife.jdbi.v2.IDBI;
import org.skife.jdbi.v2.tweak.HandleCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.testng.Assert;
import java.io.File;
import java.io.IOException;
@@ -47,13 +48,22 @@ public class MysqlTestingHelper
private MysqldResource mysqldResource;
private int port = 0;
- public void startMysql() throws IOException
+ public MysqlTestingHelper()
{
// New socket on any free port
- final ServerSocket socket = new ServerSocket(0);
- port = socket.getLocalPort();
- socket.close();
+ final ServerSocket socket;
+ try {
+ socket = new ServerSocket(0);
+ port = socket.getLocalPort();
+ socket.close();
+ }
+ catch (IOException e) {
+ Assert.fail();
+ }
+ }
+ public void startMysql() throws IOException
+ {
dbDir = File.createTempFile("mysql", "");
dbDir.delete();
dbDir.mkdir();
@@ -76,7 +86,7 @@ public class MysqlTestingHelper
public void cleanupTable(final String table)
{
- if (mysqldResource == null || mysqldResource.isRunning()) {
+ if (mysqldResource == null || !mysqldResource.isRunning()) {
log.error("Asked to cleanup table " + table + " but MySQL is not running!");
return;
}
@@ -88,7 +98,7 @@ public class MysqlTestingHelper
@Override
public Void withHandle(final Handle handle) throws Exception
{
- handle.createQuery("delete * from " + table);
+ handle.execute("truncate " + table);
return null;
}
});
@@ -103,7 +113,7 @@ public class MysqlTestingHelper
}
}
- public IDBI getDBI()
+ public DBI getDBI()
{
final String dbiString = "jdbc:mysql://localhost:" + port + "/" + DB_NAME + "?createDatabaseIfNotExist=true";
return new DBI(dbiString, USERNAME, PASSWORD);