killbill-memoizeit

analytics: fix NPE in BusinessInvoiceRecorder Some invoice

7/24/2012 7:36:51 PM

Details

diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceRecorder.java
index 554ee50..4dfd681 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceRecorder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceRecorder.java
@@ -47,7 +47,10 @@ import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.util.clock.Clock;
 
+import com.google.common.annotations.VisibleForTesting;
+
 public class BusinessInvoiceRecorder {
+
     private static final Logger log = LoggerFactory.getLogger(BusinessInvoiceRecorder.class);
 
     private final AccountUserApi accountApi;
@@ -160,42 +163,35 @@ public class BusinessInvoiceRecorder {
         accountSqlDao.saveAccount(account);
     }
 
-    private BusinessInvoiceItem createBusinessInvoiceItem(final InvoiceItem invoiceItem) {
-        final String externalKey;
-        try {
-            final SubscriptionBundle bundle = entitlementApi.getBundleFromId(invoiceItem.getBundleId());
-            externalKey = bundle.getKey();
-        } catch (EntitlementUserApiException e) {
-            log.warn("Ignoring invoice item {} for bundle {} (bundle does not exist)",
-                     invoiceItem.getId().toString(),
-                     invoiceItem.getBundleId().toString());
-            return null;
-        }
-
-        final Subscription subscription;
-        try {
-            subscription = entitlementApi.getSubscriptionFromId(invoiceItem.getSubscriptionId());
-        } catch (EntitlementUserApiException e) {
-            log.warn("Ignoring invoice item {} for subscription {} (subscription does not exist)",
-                     invoiceItem.getId().toString(),
-                     invoiceItem.getSubscriptionId().toString());
-            return null;
-        }
-
-        final Plan plan = subscription.getCurrentPlan();
-        if (plan == null) {
-            log.warn("Ignoring invoice item {} for subscription {} (null plan)",
-                     invoiceItem.getId().toString(),
-                     invoiceItem.getSubscriptionId().toString());
-            return null;
+    @VisibleForTesting
+    BusinessInvoiceItem createBusinessInvoiceItem(final InvoiceItem invoiceItem) {
+        String externalKey = null;
+        Plan plan = null;
+        PlanPhase planPhase = null;
+
+        // Subscription and bundle could be null for e.g. credits or adjustments
+        if (invoiceItem.getBundleId() != null) {
+            try {
+                final SubscriptionBundle bundle = entitlementApi.getBundleFromId(invoiceItem.getBundleId());
+                externalKey = bundle.getKey();
+            } catch (EntitlementUserApiException e) {
+                log.warn("Ignoring subscription fields for invoice item {} for bundle {} (bundle does not exist)",
+                         invoiceItem.getId().toString(),
+                         invoiceItem.getBundleId().toString());
+            }
         }
 
-        final PlanPhase planPhase = subscription.getCurrentPhase();
-        if (planPhase == null) {
-            log.warn("Ignoring invoice item {} for subscription {} (null phase)",
-                     invoiceItem.getId().toString(),
-                     invoiceItem.getSubscriptionId().toString());
-            return null;
+        if (invoiceItem.getSubscriptionId() != null) {
+            final Subscription subscription;
+            try {
+                subscription = entitlementApi.getSubscriptionFromId(invoiceItem.getSubscriptionId());
+                plan = subscription.getCurrentPlan();
+                planPhase = subscription.getCurrentPhase();
+            } catch (EntitlementUserApiException e) {
+                log.warn("Ignoring subscription fields for invoice item {} for subscription {} (subscription does not exist)",
+                         invoiceItem.getId().toString(),
+                         invoiceItem.getSubscriptionId().toString());
+            }
         }
 
         return new BusinessInvoiceItem(externalKey, invoiceItem, plan, planPhase);
diff --git a/analytics/src/main/java/com/ning/billing/analytics/model/BusinessInvoiceItem.java b/analytics/src/main/java/com/ning/billing/analytics/model/BusinessInvoiceItem.java
index 518227b..bdbd945 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/model/BusinessInvoiceItem.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/model/BusinessInvoiceItem.java
@@ -19,6 +19,8 @@ package com.ning.billing.analytics.model;
 import java.math.BigDecimal;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
+
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
@@ -30,6 +32,7 @@ import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.invoice.api.InvoiceItem;
 
 public class BusinessInvoiceItem {
+
     private final UUID itemId;
     private final DateTime createdDate;
     private final DateTime updatedDate;
@@ -47,11 +50,11 @@ public class BusinessInvoiceItem {
     private final BigDecimal amount;
     private final Currency currency;
 
-    public BusinessInvoiceItem(final BigDecimal amount, final String billingPeriod, final DateTime createdDate,
+    public BusinessInvoiceItem(final BigDecimal amount, @Nullable final String billingPeriod, final DateTime createdDate,
                                final Currency currency, final LocalDate endDate, final String externalKey,
-                               final UUID invoiceId, final UUID itemId, final String itemType, final String phase,
-                               final String productCategory, final String productName, final String productType,
-                               final String slug, final LocalDate startDate, final DateTime updatedDate) {
+                               final UUID invoiceId, final UUID itemId, final String itemType, @Nullable final String phase,
+                               @Nullable final String productCategory, @Nullable final String productName, @Nullable final String productType,
+                               @Nullable final String slug, final LocalDate startDate, final DateTime updatedDate) {
         this.amount = amount;
         this.billingPeriod = billingPeriod;
         this.createdDate = createdDate;
@@ -70,11 +73,12 @@ public class BusinessInvoiceItem {
         this.updatedDate = updatedDate;
     }
 
-    public BusinessInvoiceItem(final String externalKey, final InvoiceItem invoiceItem, final Plan plan, final PlanPhase planPhase) {
-        this(invoiceItem.getAmount(), planPhase.getBillingPeriod().toString(), new DateTime(DateTimeZone.UTC), invoiceItem.getCurrency(), invoiceItem.getEndDate(),
-             externalKey, invoiceItem.getInvoiceId(), invoiceItem.getId(), invoiceItem.getInvoiceItemType().toString(),
-             planPhase.getPhaseType().toString(), plan.getProduct().getCategory().toString(), plan.getProduct().getName(), plan.getProduct().getCatalogName(),
-             planPhase.getName(), invoiceItem.getStartDate(), new DateTime(DateTimeZone.UTC));
+    public BusinessInvoiceItem(@Nullable final String externalKey, final InvoiceItem invoiceItem, @Nullable final Plan plan, @Nullable final PlanPhase planPhase) {
+        this(invoiceItem.getAmount(), planPhase != null ? planPhase.getBillingPeriod().toString() : null, new DateTime(DateTimeZone.UTC), invoiceItem.getCurrency(),
+             invoiceItem.getEndDate(), externalKey, invoiceItem.getInvoiceId(), invoiceItem.getId(), invoiceItem.getInvoiceItemType().toString(),
+             planPhase != null ? planPhase.getPhaseType().toString() : null, plan != null ? plan.getProduct().getCategory().toString() : null,
+             plan != null ? plan.getProduct().getName() : null, plan != null ? plan.getProduct().getCatalogName() : null,
+             planPhase != null ? planPhase.getName() : null, invoiceItem.getStartDate(), new DateTime(DateTimeZone.UTC));
     }
 
     public DateTime getCreatedDate() {
diff --git a/analytics/src/test/java/com/ning/billing/analytics/TestBusinessInvoiceRecorder.java b/analytics/src/test/java/com/ning/billing/analytics/TestBusinessInvoiceRecorder.java
new file mode 100644
index 0000000..31e76e3
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/TestBusinessInvoiceRecorder.java
@@ -0,0 +1,77 @@
+/*
+ * 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;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.LocalDate;
+import org.mockito.Mockito;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.analytics.dao.BusinessInvoiceSqlDao;
+import com.ning.billing.analytics.model.BusinessInvoiceItem;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
+import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.util.clock.Clock;
+
+public class TestBusinessInvoiceRecorder {
+
+    private final AccountUserApi accountApi = Mockito.mock(AccountUserApi.class);
+    private final EntitlementUserApi entitlementApi = Mockito.mock(EntitlementUserApi.class);
+    private final InvoiceUserApi invoiceApi = Mockito.mock(InvoiceUserApi.class);
+    private final BusinessInvoiceSqlDao sqlDao = Mockito.mock(BusinessInvoiceSqlDao.class);
+    private final Clock clock = Mockito.mock(Clock.class);
+
+    @Test(groups = "fast")
+    public void testShouldBeAbleToHandleNullFieldsInInvoiceItem() throws Exception {
+        final BusinessInvoiceRecorder recorder = new BusinessInvoiceRecorder(accountApi, entitlementApi, invoiceApi, sqlDao, clock);
+
+        final InvoiceItem invoiceItem = Mockito.mock(InvoiceItem.class);
+        Mockito.when(invoiceItem.getAmount()).thenReturn(BigDecimal.TEN);
+        Mockito.when(invoiceItem.getCurrency()).thenReturn(Currency.AUD);
+        Mockito.when(invoiceItem.getEndDate()).thenReturn(new LocalDate(1200, 1, 12));
+        final UUID invoiceId = UUID.randomUUID();
+        Mockito.when(invoiceItem.getInvoiceId()).thenReturn(invoiceId);
+        final UUID id = UUID.randomUUID();
+        Mockito.when(invoiceItem.getId()).thenReturn(id);
+        Mockito.when(invoiceItem.getStartDate()).thenReturn(new LocalDate(1985, 9, 10));
+        Mockito.when(invoiceItem.getInvoiceItemType()).thenReturn(InvoiceItemType.CREDIT_ADJ);
+
+        final BusinessInvoiceItem bii = recorder.createBusinessInvoiceItem(invoiceItem);
+        Assert.assertNotNull(bii);
+        Assert.assertEquals(bii.getAmount(), invoiceItem.getAmount());
+        Assert.assertEquals(bii.getCurrency(), invoiceItem.getCurrency());
+        Assert.assertEquals(bii.getEndDate(), invoiceItem.getEndDate());
+        Assert.assertEquals(bii.getInvoiceId(), invoiceItem.getInvoiceId());
+        Assert.assertEquals(bii.getItemId(), invoiceItem.getId());
+        Assert.assertEquals(bii.getStartDate(), invoiceItem.getStartDate());
+        Assert.assertEquals(bii.getItemType(), invoiceItem.getInvoiceItemType().toString());
+        Assert.assertNull(bii.getBillingPeriod());
+        Assert.assertNull(bii.getPhase());
+        Assert.assertNull(bii.getProductCategory());
+        Assert.assertNull(bii.getProductName());
+        Assert.assertNull(bii.getProductType());
+        Assert.assertNull(bii.getSlug());
+        Assert.assertNull(bii.getExternalKey());
+    }
+}