killbill-uncached

analytics: integration with Account events Listen to Account

12/19/2011 7:47:48 PM

Details

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 ba7a707..cce74e7 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -18,6 +18,8 @@ package com.ning.billing.analytics;
 
 import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
+import com.ning.billing.account.api.AccountChangeNotification;
+import com.ning.billing.account.api.AccountCreationNotification;
 import com.ning.billing.entitlement.api.user.SubscriptionTransition;
 
 public class AnalyticsListener
@@ -62,7 +64,18 @@ public class AnalyticsListener
     }
 
     @Subscribe
-    public void handleAccountChange(final Object event)
+    public void handleAccountCreation(final AccountCreationNotification event)
     {
+        bacRecorder.accountCreated(event.getData());
+    }
+
+    @Subscribe
+    public void handleAccountChange(final AccountChangeNotification event)
+    {
+        if (!event.hasChanges()) {
+            return;
+        }
+
+        bacRecorder.accountUpdated(event.getAccountId(), event.getChangedFields());
     }
 }
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
index 185289a..23fbcf4 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
@@ -18,11 +18,18 @@ package com.ning.billing.analytics;
 
 import com.google.inject.Inject;
 import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.account.api.ChangedField;
 import com.ning.billing.analytics.dao.BusinessAccountDao;
+import com.ning.billing.util.tag.Tag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
 public class BusinessAccountRecorder
 {
     private static final Logger log = LoggerFactory.getLogger(BusinessAccountRecorder.class);
@@ -37,11 +44,35 @@ public class BusinessAccountRecorder
         this.accountApi = accountApi;
     }
 
-    public void subscriptionCreated(final Account created)
+    public void accountCreated(final AccountData data)
     {
+        final Account account = accountApi.getAccountByKey(data.getExternalKey());
+
+        final List<String> tags = new ArrayList<String>();
+        for (final Tag tag : account.getTagList()) {
+            tags.add(tag.getName());
+        }
+
+        // TODO Need payment and invoice api to fill most fields
+        final BusinessAccount bac = new BusinessAccount(
+            account.getExternalKey(),
+            null, // TODO
+            tags,
+            null, // TODO
+            null, // TODO
+            null, // TODO
+            null, // TODO
+            null, // TODO
+            null // TODO
+        );
+
+        log.info("ACCOUNT CREATION " + bac);
+        dao.createAccount(bac);
     }
 
-    public void subscriptionUpdated(final Account updated)
+    public void accountUpdated(final UUID accountId, final List<ChangedField> changedFields)
     {
+        // None of the fields updated interest us so far - see DefaultAccountChangeNotification
+        // TODO We'll need notifications for tags changes eventually
     }
 }
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountBinder.java b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountBinder.java
index 6a3e218..6478536 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountBinder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountBinder.java
@@ -30,6 +30,7 @@ import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.sql.Types;
 
 @BindingAnnotation(BusinessAccountBinder.BacBinderFactory.class)
 @Retention(RetentionPolicy.RUNTIME)
@@ -59,7 +60,12 @@ public @interface BusinessAccountBinder
                     q.bind("account_key", account.getKey());
                     q.bind("balance", account.getRoundedBalance());
                     q.bind("tags", joiner.join(account.getTags()));
-                    q.bind("last_invoice_date", account.getLastInvoiceDate().getMillis());
+                    if (account.getLastInvoiceDate() != null) {
+                        q.bind("last_invoice_date", account.getLastInvoiceDate().getMillis());
+                    }
+                    else {
+                        q.bindNull("last_invoice_date", Types.BIGINT);
+                    }
                     q.bind("total_invoice_balance", account.getRoundedTotalInvoiceBalance());
                     q.bind("last_payment_status", account.getLastPaymentStatus());
                     q.bind("payment_method", account.getPaymentMethod());
diff --git a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
index 03e102d..3c3de7e 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
@@ -22,6 +22,7 @@ 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 com.ning.billing.util.glue.TagStoreModule;
 import org.skife.jdbi.v2.DBI;
 import org.skife.jdbi.v2.IDBI;
 
@@ -37,6 +38,7 @@ public class AnalyticsTestModule extends AnalyticsModule
         install(new CatalogModule());
         install(new EventBusModule());
         install(new EntitlementModule());
+        install(new TagStoreModule());
 
         // Install the Dao layer
         final MysqlTestingHelper helper = new MysqlTestingHelper();
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
index 17b6a29..03a27d0 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
@@ -18,15 +18,38 @@ package com.ning.billing.analytics.api;
 
 import com.google.inject.Inject;
 import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountCreationNotification;
 import com.ning.billing.account.api.AccountUserApi;
-import com.ning.billing.analytics.*;
+import com.ning.billing.account.api.user.DefaultAccountCreationEvent;
+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.BusinessAccountDao;
 import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
-import com.ning.billing.catalog.api.*;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.Product;
+import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.dbi.MysqlTestingHelper;
-import com.ning.billing.entitlement.api.user.*;
+import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.util.eventbus.EventBus;
+import com.ning.billing.util.tag.DefaultTagDescription;
+import com.ning.billing.util.tag.dao.TagDescriptionDao;
 import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
@@ -45,6 +68,8 @@ public class TestAnalyticsService
 {
     private static final String KEY = "1234";
     private static final String ACCOUNT_KEY = "pierre-1234";
+    private static final DefaultTagDescription TAG_ONE = new DefaultTagDescription("batch20", "something", false, false, "pierre", new DateTime(DateTimeZone.UTC));
+    private static final DefaultTagDescription TAG_TWO = new DefaultTagDescription("awesome", "something", false, false, "pierre", new DateTime(DateTimeZone.UTC));
 
     @Inject
     private AccountUserApi accountApi;
@@ -53,13 +78,19 @@ public class TestAnalyticsService
     private EntitlementUserApi entitlementApi;
 
     @Inject
+    private TagDescriptionDao tagDao;
+
+    @Inject
     private AnalyticsService service;
 
     @Inject
     private EventBus bus;
 
     @Inject
-    private BusinessSubscriptionTransitionDao dao;
+    private BusinessSubscriptionTransitionDao subscriptionDao;
+
+    @Inject
+    private BusinessAccountDao accountDao;
 
     @Inject
     private MysqlTestingHelper helper;
@@ -67,13 +98,33 @@ public class TestAnalyticsService
     private SubscriptionTransition transition;
     private BusinessSubscriptionTransition expectedTransition;
 
+    private AccountCreationNotification accountCreationNotification;
+
     @BeforeClass(alwaysRun = true)
     public void startMysql() throws IOException, ClassNotFoundException, SQLException, EntitlementUserApiException
     {
+        // Killbill generic setup
+        setupBusAndMySQL();
+
+        tagDao.save(TAG_ONE);
+        tagDao.save(TAG_TWO);
+
+        final MockAccount account = new MockAccount(UUID.randomUUID(), ACCOUNT_KEY, Currency.USD);
+        final Account storedAccount = accountApi.createAccount(account);
+        storedAccount.addTag(TAG_ONE, "pierre", new DateTime(DateTimeZone.UTC));
+        storedAccount.addTag(TAG_TWO, "pierre", new DateTime(DateTimeZone.UTC));
+        accountApi.saveAccount(storedAccount);
+
+        // Create events for the bus and expected results
+        createSubscriptionTransitionEvent(storedAccount);
+        createAccountCreationEvent(storedAccount);
+    }
+
+    private void setupBusAndMySQL() throws IOException
+    {
         bus.start();
 
         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"));
 
@@ -81,17 +132,17 @@ public class TestAnalyticsService
         helper.initDb(analyticsDdl);
         helper.initDb(accountDdl);
         helper.initDb(entitlementDdl);
+    }
 
-        // We need a bundle to retrieve the event key
-        final MockAccount account = new MockAccount(UUID.randomUUID(), ACCOUNT_KEY, Currency.USD);
-        final Account storedAccount = accountApi.createAccount(account);
-        final SubscriptionBundle bundle = entitlementApi.createBundleForAccount(storedAccount.getId(), KEY);
+    private void createSubscriptionTransitionEvent(final Account account) throws EntitlementUserApiException
+    {
+        final SubscriptionBundle bundle = entitlementApi.createBundleForAccount(account.getId(), KEY);
 
         // Verify we correctly initialized the account subsystem
         Assert.assertNotNull(bundle);
         Assert.assertEquals(bundle.getKey(), KEY);
 
-        // Create a subscription transition
+        // Create a subscription transition event
         final Product product = new MockProduct("platinum", "subscription", ProductCategory.BASE);
         final Plan plan = new MockPlan("platinum-monthly", product);
         final PlanPhase phase = new MockPhase(PhaseType.EVERGREEN, plan, MockDuration.UNLIMITED(), 25.95);
@@ -99,6 +150,7 @@ public class TestAnalyticsService
         final DateTime effectiveTransitionTime = new DateTime(DateTimeZone.UTC);
         final DateTime requestedTransitionTime = new DateTime(DateTimeZone.UTC);
         final String priceList = "something";
+
         transition = new SubscriptionTransitionData(
             UUID.randomUUID(),
             subscriptionId,
@@ -126,6 +178,11 @@ public class TestAnalyticsService
         );
     }
 
+    private void createAccountCreationEvent(final Account account)
+    {
+        accountCreationNotification = new DefaultAccountCreationEvent(account);
+    }
+
     @AfterClass(alwaysRun = true)
     public void stopMysql()
     {
@@ -146,11 +203,18 @@ public class TestAnalyticsService
             Assert.fail("Unable to start the bus or service! " + t);
         }
 
-        // Send an event to the bus and make sure our Dao got it
+        // Send events and wait for the async part...
         bus.post(transition);
+        bus.post(accountCreationNotification);
         Thread.sleep(1000);
-        Assert.assertEquals(dao.getTransitions(KEY).size(), 1);
-        Assert.assertEquals(dao.getTransitions(KEY).get(0), expectedTransition);
+
+        Assert.assertEquals(subscriptionDao.getTransitions(KEY).size(), 1);
+        Assert.assertEquals(subscriptionDao.getTransitions(KEY).get(0), expectedTransition);
+
+        Assert.assertEquals(accountDao.getAccount(ACCOUNT_KEY).getKey(), ACCOUNT_KEY);
+        Assert.assertEquals(accountDao.getAccount(ACCOUNT_KEY).getTags().size(), 2);
+        Assert.assertTrue(accountDao.getAccount(ACCOUNT_KEY).getTags().indexOf(TAG_ONE.getName()) != -1);
+        Assert.assertTrue(accountDao.getAccount(ACCOUNT_KEY).getTags().indexOf(TAG_TWO.getName()) != -1);
 
         // Test the shutdown sequence
         try {
diff --git a/util/src/main/java/com/ning/billing/util/glue/TagDescriptionDaoProvider.java b/util/src/main/java/com/ning/billing/util/glue/TagDescriptionDaoProvider.java
new file mode 100644
index 0000000..31fb306
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/glue/TagDescriptionDaoProvider.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.util.glue;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.ning.billing.util.tag.dao.TagDescriptionDao;
+import org.skife.jdbi.v2.IDBI;
+
+public class TagDescriptionDaoProvider implements Provider<TagDescriptionDao>
+{
+    private final IDBI dbi;
+
+    @Inject
+    public TagDescriptionDaoProvider(final IDBI dbi)
+    {
+        this.dbi = dbi;
+    }
+
+    @Override
+    public TagDescriptionDao get()
+    {
+        return dbi.onDemand(TagDescriptionDao.class);
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/glue/TagStoreDaoProvider.java b/util/src/main/java/com/ning/billing/util/glue/TagStoreDaoProvider.java
new file mode 100644
index 0000000..2c612e6
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/glue/TagStoreDaoProvider.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.util.glue;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.ning.billing.util.tag.dao.TagStoreDao;
+import org.skife.jdbi.v2.IDBI;
+
+public class TagStoreDaoProvider implements Provider<TagStoreDao>
+{
+    private final IDBI dbi;
+
+    @Inject
+    public TagStoreDaoProvider(final IDBI dbi)
+    {
+        this.dbi = dbi;
+    }
+
+    @Override
+    public TagStoreDao get()
+    {
+        return dbi.onDemand(TagStoreDao.class);
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/glue/TagStoreModule.java b/util/src/main/java/com/ning/billing/util/glue/TagStoreModule.java
new file mode 100644
index 0000000..ae14782
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/glue/TagStoreModule.java
@@ -0,0 +1,31 @@
+/*
+ * 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.util.glue;
+
+import com.google.inject.AbstractModule;
+import com.ning.billing.util.tag.dao.TagDescriptionDao;
+import com.ning.billing.util.tag.dao.TagStoreDao;
+
+public class TagStoreModule extends AbstractModule
+{
+    @Override
+    protected void configure()
+    {
+        bind(TagDescriptionDao.class).toProvider(TagDescriptionDaoProvider.class).asEagerSingleton();
+        bind(TagStoreDao.class).toProvider(TagStoreDaoProvider.class).asEagerSingleton();
+    }
+}