killbill-memoizeit
Changes
beatrix/pom.xml 5(+5 -0)
Details
diff --git a/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java b/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
new file mode 100644
index 0000000..402f17d
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2010-2012 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.user;
+
+import javax.inject.Inject;
+import java.util.List;
+
+import com.ning.billing.analytics.dao.AnalyticsDao;
+import com.ning.billing.analytics.model.BusinessAccount;
+import com.ning.billing.analytics.model.BusinessSubscriptionTransition;
+
+// Note: not exposed in api yet
+public class DefaultAnalyticsUserApi {
+ private final AnalyticsDao analyticsDao;
+
+ @Inject
+ public DefaultAnalyticsUserApi(final AnalyticsDao analyticsDao) {
+ this.analyticsDao = analyticsDao;
+ }
+
+ public BusinessAccount getAccountByKey(final String accountKey) {
+ return analyticsDao.getAccountByKey(accountKey);
+ }
+
+ public List<BusinessSubscriptionTransition> getTransitionsForBundle(final String key) {
+ return analyticsDao.getTransitionsByKey(key);
+ }
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsDao.java
index 66a8a26..bd3cda9 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsDao.java
@@ -16,12 +16,17 @@
package com.ning.billing.analytics.dao;
+import java.util.List;
+
import com.ning.billing.analytics.model.BusinessAccount;
import com.ning.billing.analytics.model.BusinessInvoice;
import com.ning.billing.analytics.model.BusinessInvoiceItem;
+import com.ning.billing.analytics.model.BusinessSubscriptionTransition;
public interface AnalyticsDao {
BusinessAccount getAccountByKey(final String accountKey);
+ List<BusinessSubscriptionTransition> getTransitionsByKey(final String externalKey);
+
void createInvoice(final String accountKey, final BusinessInvoice invoice, final Iterable<BusinessInvoiceItem> invoiceItems);
}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsDao.java
index 87da400..1f44af9 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsDao.java
@@ -17,6 +17,7 @@
package com.ning.billing.analytics.dao;
import javax.inject.Inject;
+import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@@ -26,14 +27,19 @@ import org.skife.jdbi.v2.TransactionStatus;
import com.ning.billing.analytics.model.BusinessAccount;
import com.ning.billing.analytics.model.BusinessInvoice;
import com.ning.billing.analytics.model.BusinessInvoiceItem;
+import com.ning.billing.analytics.model.BusinessSubscriptionTransition;
public class DefaultAnalyticsDao implements AnalyticsDao {
private final BusinessAccountSqlDao accountSqlDao;
+ private final BusinessSubscriptionTransitionSqlDao subscriptionTransitionSqlDao;
private final BusinessInvoiceSqlDao invoiceSqlDao;
@Inject
- public DefaultAnalyticsDao(final BusinessAccountSqlDao accountSqlDao, final BusinessInvoiceSqlDao invoiceSqlDao) {
+ public DefaultAnalyticsDao(final BusinessAccountSqlDao accountSqlDao,
+ final BusinessSubscriptionTransitionSqlDao subscriptionTransitionSqlDao,
+ final BusinessInvoiceSqlDao invoiceSqlDao) {
this.accountSqlDao = accountSqlDao;
+ this.subscriptionTransitionSqlDao = subscriptionTransitionSqlDao;
this.invoiceSqlDao = invoiceSqlDao;
}
@@ -43,6 +49,11 @@ public class DefaultAnalyticsDao implements AnalyticsDao {
}
@Override
+ public List<BusinessSubscriptionTransition> getTransitionsByKey(final String externalKey) {
+ return subscriptionTransitionSqlDao.getTransitions(externalKey);
+ }
+
+ @Override
public void createInvoice(final String accountKey, final BusinessInvoice invoice, final Iterable<BusinessInvoiceItem> invoiceItems) {
invoiceSqlDao.inTransaction(new Transaction<Void, BusinessInvoiceSqlDao>() {
@Override
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 3d3c2e4..82433ec 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
@@ -24,6 +24,7 @@ import com.ning.billing.analytics.BusinessSubscriptionTransitionRecorder;
import com.ning.billing.analytics.BusinessTagRecorder;
import com.ning.billing.analytics.api.AnalyticsService;
import com.ning.billing.analytics.api.DefaultAnalyticsService;
+import com.ning.billing.analytics.api.user.DefaultAnalyticsUserApi;
import com.ning.billing.analytics.dao.AnalyticsDao;
import com.ning.billing.analytics.dao.BusinessAccountSqlDao;
import com.ning.billing.analytics.dao.BusinessAccountTagSqlDao;
@@ -65,5 +66,7 @@ public class AnalyticsModule extends AbstractModule {
bind(AnalyticsDao.class).to(DefaultAnalyticsDao.class).asEagerSingleton();
bind(AnalyticsService.class).to(DefaultAnalyticsService.class).asEagerSingleton();
+
+ bind(DefaultAnalyticsUserApi.class).asEagerSingleton();
}
}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/dao/TestDefaultAnalyticsDao.java b/analytics/src/test/java/com/ning/billing/analytics/dao/TestDefaultAnalyticsDao.java
index 20b3517..a7f1479 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/dao/TestDefaultAnalyticsDao.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/dao/TestDefaultAnalyticsDao.java
@@ -36,6 +36,7 @@ import com.ning.billing.catalog.api.Currency;
public class TestDefaultAnalyticsDao extends TestWithEmbeddedDB {
private BusinessAccountSqlDao accountSqlDao;
+ private BusinessSubscriptionTransitionSqlDao subscriptionTransitionSqlDao;
private BusinessInvoiceSqlDao invoiceSqlDao;
private BusinessInvoiceItemSqlDao invoiceItemSqlDao;
private AnalyticsDao analyticsDao;
@@ -44,9 +45,10 @@ public class TestDefaultAnalyticsDao extends TestWithEmbeddedDB {
public void setUp() throws Exception {
final IDBI dbi = helper.getDBI();
accountSqlDao = dbi.onDemand(BusinessAccountSqlDao.class);
+ subscriptionTransitionSqlDao = dbi.onDemand(BusinessSubscriptionTransitionSqlDao.class);
invoiceSqlDao = dbi.onDemand(BusinessInvoiceSqlDao.class);
invoiceItemSqlDao = dbi.onDemand(BusinessInvoiceItemSqlDao.class);
- analyticsDao = new DefaultAnalyticsDao(accountSqlDao, invoiceSqlDao);
+ analyticsDao = new DefaultAnalyticsDao(accountSqlDao, subscriptionTransitionSqlDao, invoiceSqlDao);
}
@Test(groups = "slow")
beatrix/pom.xml 5(+5 -0)
diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 4f66b35..805cd58 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -108,6 +108,11 @@
</dependency>
<dependency>
<groupId>com.ning.billing</groupId>
+ <artifactId>killbill-analytics</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
<artifactId>killbill-overdue</artifactId>
<scope>test</scope>
</dependency>
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/BeatrixModule.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/BeatrixModule.java
index 0e0ea55..c26fac5 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/BeatrixModule.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/BeatrixModule.java
@@ -29,6 +29,7 @@ import com.google.inject.Inject;
import com.google.inject.Injector;
import com.ning.billing.account.api.AccountService;
import com.ning.billing.account.glue.AccountModule;
+import com.ning.billing.analytics.setup.AnalyticsModule;
import com.ning.billing.beatrix.integration.overdue.IntegrationTestOverdueModule;
import com.ning.billing.beatrix.lifecycle.DefaultLifecycle;
import com.ning.billing.beatrix.lifecycle.Lifecycle;
@@ -96,6 +97,7 @@ public class BeatrixModule extends AbstractModule {
install(new TagStoreModule());
install(new CustomFieldModule());
install(new AccountModule());
+ install(new AnalyticsModule());
install(new CatalogModule());
install(new DefaultEntitlementModule());
install(new DefaultInvoiceModule());
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
new file mode 100644
index 0000000..4edd6d6
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2010-2012 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.beatrix.integration;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.AccountData;
+import com.ning.billing.account.api.MutableAccountData;
+import com.ning.billing.analytics.model.BusinessAccount;
+import com.ning.billing.analytics.model.BusinessSubscriptionEvent;
+import com.ning.billing.analytics.model.BusinessSubscriptionTransition;
+import com.ning.billing.analytics.utils.Rounder;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+
+@Guice(modules = BeatrixModule.class)
+public class TestAnalytics extends TestIntegrationBase {
+ @BeforeMethod(groups = "slow")
+ public void setUpAnalyticsHandler() throws Exception {
+ busService.getBus().register(analyticsListener);
+ }
+
+ @AfterMethod(groups = "slow")
+ public void tearDownAnalyticsHandler() throws Exception {
+ busService.getBus().unregister(analyticsListener);
+ }
+
+ @Test(groups = "slow")
+ public void testAnalyticsEvents() throws Exception {
+ // Create an account
+ final Account account = verifyAccountCreation();
+
+ // Update some fields
+ verifyAccountUpdate(account);
+
+ // Create a bundle
+ final SubscriptionBundle bundle = verifyFirstBundle(account);
+
+ // Add a subscription
+ verifyFirstSubscription(account, bundle);
+ }
+
+ private Account verifyAccountCreation() throws Exception {
+ final AccountData accountData = getAccountData(1);
+
+ // Verify BAC is empty
+ Assert.assertNull(analyticsUserApi.getAccountByKey(accountData.getExternalKey()));
+
+ // Create an account
+ final Account account = createAccountWithPaymentMethod(accountData);
+ Assert.assertNotNull(account);
+
+ waitALittle();
+
+ // Verify Analytics got the account creation event
+ final BusinessAccount businessAccount = analyticsUserApi.getAccountByKey(account.getExternalKey());
+ Assert.assertNotNull(businessAccount);
+ // No balance yet
+ Assert.assertEquals(businessAccount.getBalance().doubleValue(), Rounder.round(BigDecimal.ZERO));
+ Assert.assertEquals(businessAccount.getKey(), account.getExternalKey());
+ // No invoice yet
+ Assert.assertNull(businessAccount.getLastInvoiceDate());
+ // No payment yet
+ Assert.assertNull(businessAccount.getLastPaymentStatus());
+ Assert.assertEquals(businessAccount.getName(), account.getName());
+ // No invoice balance yet
+ Assert.assertEquals(businessAccount.getTotalInvoiceBalance().doubleValue(), Rounder.round(BigDecimal.ZERO));
+ // TODO - payment fields
+ //Assert.assertNotNull(businessAccount.getBillingAddressCountry());
+ //Assert.assertNotNull(businessAccount.getCreditCardType());
+ //Assert.assertNotNull(businessAccount.getPaymentMethod());
+
+ return account;
+ }
+
+ private void verifyAccountUpdate(final Account account) throws InterruptedException {
+ final MutableAccountData mutableAccountData = account.toMutableAccountData();
+
+ mutableAccountData.setName(UUID.randomUUID().toString().substring(0, 20));
+
+ try {
+ accountUserApi.updateAccount(account.getId(), mutableAccountData, context);
+ } catch (AccountApiException e) {
+ Assert.fail("Unable to update account", e);
+ }
+
+ waitALittle();
+
+ // Verify Analytics got the account update event
+ final BusinessAccount businessAccount = analyticsUserApi.getAccountByKey(mutableAccountData.getExternalKey());
+ Assert.assertNotNull(businessAccount);
+ // No balance yet
+ Assert.assertEquals(businessAccount.getBalance().doubleValue(), Rounder.round(BigDecimal.ZERO));
+ Assert.assertEquals(businessAccount.getKey(), mutableAccountData.getExternalKey());
+ // No invoice yet
+ Assert.assertNull(businessAccount.getLastInvoiceDate());
+ // No payment yet
+ Assert.assertNull(businessAccount.getLastPaymentStatus());
+ Assert.assertEquals(businessAccount.getName(), mutableAccountData.getName());
+ // No invoice balance yet
+ Assert.assertEquals(businessAccount.getTotalInvoiceBalance().doubleValue(), Rounder.round(BigDecimal.ZERO));
+ // TODO - payment fields
+ //Assert.assertNotNull(businessAccount.getBillingAddressCountry());
+ //Assert.assertNotNull(businessAccount.getCreditCardType());
+ //Assert.assertNotNull(businessAccount.getPaymentMethod());
+ }
+
+ private SubscriptionBundle verifyFirstBundle(final Account account) throws EntitlementUserApiException, InterruptedException {
+ // Add a bundle
+ final SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), UUID.randomUUID().toString(), context);
+ Assert.assertNotNull(bundle);
+
+ waitALittle();
+
+ // Verify BST is still empty since no subscription has been added yet
+ Assert.assertEquals(analyticsUserApi.getTransitionsForBundle(bundle.getKey()).size(), 0);
+
+ return bundle;
+ }
+
+ private Subscription verifyFirstSubscription(final Account account, final SubscriptionBundle bundle) throws EntitlementUserApiException, InterruptedException, CatalogApiException {
+ // Add a subscription
+ final String productName = "Shotgun";
+ final BillingPeriod term = BillingPeriod.MONTHLY;
+ final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+ final PlanPhaseSpecifier phaseSpecifier = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null);
+ final Subscription subscription = entitlementUserApi.createSubscription(bundle.getId(), phaseSpecifier, null, context);
+
+ waitALittle();
+
+ // BST should have one transition
+ final List<BusinessSubscriptionTransition> transitions = analyticsUserApi.getTransitionsForBundle(bundle.getKey());
+ Assert.assertEquals(transitions.size(), 1);
+ final BusinessSubscriptionTransition transition = transitions.get(0);
+ Assert.assertEquals(transition.getExternalKey(), bundle.getKey());
+ Assert.assertEquals(transition.getAccountKey(), account.getExternalKey());
+ Assert.assertEquals(transition.getEvent().getCategory(), phaseSpecifier.getProductCategory());
+ Assert.assertEquals(transition.getEvent().getEventType(), BusinessSubscriptionEvent.EventType.ADD);
+
+ // This is the first transition
+ Assert.assertNull(transition.getPreviousSubscription());
+
+ Assert.assertEquals(transition.getNextSubscription().getBillingPeriod(), subscription.getCurrentPhase().getBillingPeriod().toString());
+ Assert.assertEquals(transition.getNextSubscription().getBundleId(), subscription.getBundleId());
+ Assert.assertEquals(transition.getNextSubscription().getCurrency(), account.getCurrency().toString());
+ Assert.assertEquals(transition.getNextSubscription().getPhase(), subscription.getCurrentPhase().getPhaseType().toString());
+ // Trial: fixed price of zero
+ Assert.assertEquals(transition.getNextSubscription().getPrice().doubleValue(), subscription.getCurrentPhase().getFixedPrice().getPrice(account.getCurrency()).doubleValue());
+ Assert.assertEquals(transition.getNextSubscription().getPriceList(), subscription.getCurrentPriceList().getName());
+ Assert.assertEquals(transition.getNextSubscription().getProductCategory(), subscription.getCurrentPlan().getProduct().getCategory());
+ Assert.assertEquals(transition.getNextSubscription().getProductName(), subscription.getCurrentPlan().getProduct().getName());
+ Assert.assertEquals(transition.getNextSubscription().getProductType(), subscription.getCurrentPlan().getProduct().getCatalogName());
+ Assert.assertEquals(transition.getNextSubscription().getSlug(), subscription.getCurrentPhase().getName());
+ Assert.assertEquals(transition.getNextSubscription().getStartDate(), subscription.getStartDate());
+ Assert.assertEquals(transition.getNextSubscription().getState(), subscription.getState());
+ Assert.assertEquals(transition.getNextSubscription().getSubscriptionId(), subscription.getId());
+
+ return subscription;
+ }
+
+ private void waitALittle() throws InterruptedException {
+ // We especially need to wait for entitlement events
+ Thread.sleep(1000);
+ }
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
index 43ccf5d..48202da 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
@@ -37,6 +37,8 @@ import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountData;
import com.ning.billing.account.api.AccountService;
import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.analytics.AnalyticsListener;
+import com.ning.billing.analytics.api.user.DefaultAnalyticsUserApi;
import com.ning.billing.api.TestApiListener;
import com.ning.billing.api.TestListenerStatus;
import com.ning.billing.beatrix.lifecycle.Lifecycle;
@@ -124,6 +126,12 @@ public class TestIntegrationBase implements TestListenerStatus {
@Inject
protected AccountUserApi accountUserApi;
+ @Inject
+ protected DefaultAnalyticsUserApi analyticsUserApi;
+
+ @Inject
+ protected AnalyticsListener analyticsListener;
+
protected TestApiListener busHandler;
@@ -152,6 +160,7 @@ public class TestIntegrationBase implements TestListenerStatus {
protected void setupMySQL() throws IOException {
final String accountDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
+ final String analyticsDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/analytics/ddl.sql"));
final String entitlementDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
final String invoiceDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
final String paymentDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/payment/ddl.sql"));
@@ -161,6 +170,7 @@ public class TestIntegrationBase implements TestListenerStatus {
helper.startMysql();
helper.initDb(accountDdl);
+ helper.initDb(analyticsDdl);
helper.initDb(entitlementDdl);
helper.initDb(invoiceDdl);
helper.initDb(paymentDdl);