killbill-uncached

Changes

Details

diff --git a/analytics/src/main/java/com/ning/billing/analytics/api/DefaultTimeSeriesData.java b/analytics/src/main/java/com/ning/billing/analytics/api/DefaultTimeSeriesData.java
new file mode 100644
index 0000000..4c1eebb
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/api/DefaultTimeSeriesData.java
@@ -0,0 +1,88 @@
+/*
+ * 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;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.joda.time.LocalDate;
+
+import com.ning.billing.analytics.dao.TimeSeriesTuple;
+
+public class DefaultTimeSeriesData implements TimeSeriesData {
+
+    private final List<LocalDate> dates;
+    private final List<Double> values;
+
+    public DefaultTimeSeriesData(final List<TimeSeriesTuple> dataOverTime) {
+        // We assume dataOverTime is sorted by time
+        dates = new LinkedList<LocalDate>();
+        values = new LinkedList<Double>();
+        for (final TimeSeriesTuple data : dataOverTime) {
+            dates.add(data.getLocalDate());
+            values.add(data.getValue());
+        }
+    }
+
+    @Override
+    public List<LocalDate> getDates() {
+        return dates;
+    }
+
+    @Override
+    public List<Double> getValues() {
+        return values;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("DefaultTimeSeriesData");
+        sb.append("{dates=").append(dates);
+        sb.append(", values=").append(values);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final DefaultTimeSeriesData that = (DefaultTimeSeriesData) o;
+
+        if (dates != null ? !dates.equals(that.dates) : that.dates != null) {
+            return false;
+        }
+        if (values != null ? !values.equals(that.values) : that.values != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = dates != null ? dates.hashCode() : 0;
+        result = 31 * result + (values != null ? values.hashCode() : 0);
+        return result;
+    }
+}
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
index 4d90894..cbd652d 100644
--- 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
@@ -16,10 +16,12 @@
 
 package com.ning.billing.analytics.api.user;
 
-import javax.inject.Inject;
 import java.util.List;
 import java.util.UUID;
 
+import javax.inject.Inject;
+
+import com.ning.billing.analytics.api.TimeSeriesData;
 import com.ning.billing.analytics.dao.AnalyticsDao;
 import com.ning.billing.analytics.model.BusinessAccount;
 import com.ning.billing.analytics.model.BusinessAccountTag;
@@ -29,8 +31,8 @@ import com.ning.billing.analytics.model.BusinessInvoicePayment;
 import com.ning.billing.analytics.model.BusinessOverdueStatus;
 import com.ning.billing.analytics.model.BusinessSubscriptionTransition;
 
-// Note: not exposed in api yet
-public class DefaultAnalyticsUserApi {
+public class DefaultAnalyticsUserApi implements AnalyticsUserApi {
+
     private final AnalyticsDao analyticsDao;
 
     @Inject
@@ -38,6 +40,13 @@ public class DefaultAnalyticsUserApi {
         this.analyticsDao = analyticsDao;
     }
 
+    @Override
+    public TimeSeriesData getAccountsCreatedOverTime() {
+        return analyticsDao.getAccountsCreatedOverTime();
+    }
+
+    // Note: the following is not exposed in api yet, as the models need to be extracted first
+
     public BusinessAccount getAccountByKey(final String accountKey) {
         return analyticsDao.getAccountByKey(accountKey);
     }
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 0e349fc..d4ae76e 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
@@ -18,6 +18,7 @@ package com.ning.billing.analytics.dao;
 
 import java.util.List;
 
+import com.ning.billing.analytics.api.TimeSeriesData;
 import com.ning.billing.analytics.model.BusinessAccount;
 import com.ning.billing.analytics.model.BusinessAccountTag;
 import com.ning.billing.analytics.model.BusinessInvoice;
@@ -27,6 +28,9 @@ import com.ning.billing.analytics.model.BusinessOverdueStatus;
 import com.ning.billing.analytics.model.BusinessSubscriptionTransition;
 
 public interface AnalyticsDao {
+
+    TimeSeriesData getAccountsCreatedOverTime();
+
     BusinessAccount getAccountByKey(final String accountKey);
 
     List<BusinessSubscriptionTransition> getTransitionsByKey(final String externalKey);
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountSqlDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountSqlDao.java
index 8ceb0ad..d2c6d2e 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountSqlDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountSqlDao.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.analytics.dao;
 
+import java.util.List;
+
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
@@ -27,8 +29,12 @@ import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTempla
 import com.ning.billing.analytics.model.BusinessAccount;
 
 @ExternalizedSqlViaStringTemplate3()
-@RegisterMapper(BusinessAccountMapper.class)
+@RegisterMapper({BusinessAccountMapper.class, TimeSeriesTupleMapper.class})
 public interface BusinessAccountSqlDao extends Transactional<BusinessAccountSqlDao>, Transmogrifier {
+
+    @SqlQuery
+    List<TimeSeriesTuple> getAccountsCreatedOverTime();
+
     @SqlQuery
     BusinessAccount getAccount(@Bind("account_id") final String accountId);
 
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 d4855fd..0918673 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
@@ -16,9 +16,12 @@
 
 package com.ning.billing.analytics.dao;
 
-import javax.inject.Inject;
 import java.util.List;
 
+import javax.inject.Inject;
+
+import com.ning.billing.analytics.api.DefaultTimeSeriesData;
+import com.ning.billing.analytics.api.TimeSeriesData;
 import com.ning.billing.analytics.model.BusinessAccount;
 import com.ning.billing.analytics.model.BusinessAccountTag;
 import com.ning.billing.analytics.model.BusinessInvoice;
@@ -28,6 +31,7 @@ import com.ning.billing.analytics.model.BusinessOverdueStatus;
 import com.ning.billing.analytics.model.BusinessSubscriptionTransition;
 
 public class DefaultAnalyticsDao implements AnalyticsDao {
+
     private final BusinessAccountSqlDao accountSqlDao;
     private final BusinessSubscriptionTransitionSqlDao subscriptionTransitionSqlDao;
     private final BusinessInvoiceSqlDao invoiceSqlDao;
@@ -54,6 +58,11 @@ public class DefaultAnalyticsDao implements AnalyticsDao {
     }
 
     @Override
+    public TimeSeriesData getAccountsCreatedOverTime() {
+        return new DefaultTimeSeriesData(accountSqlDao.getAccountsCreatedOverTime());
+    }
+
+    @Override
     public BusinessAccount getAccountByKey(final String accountKey) {
         return accountSqlDao.getAccountByKey(accountKey);
     }
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/TimeSeriesTuple.java b/analytics/src/main/java/com/ning/billing/analytics/dao/TimeSeriesTuple.java
new file mode 100644
index 0000000..ec82930
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/TimeSeriesTuple.java
@@ -0,0 +1,76 @@
+/*
+ * 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.dao;
+
+import org.joda.time.LocalDate;
+
+public class TimeSeriesTuple {
+
+    private final LocalDate localDate;
+    private final Double value;
+
+    public TimeSeriesTuple(final LocalDate localDate, final Double value) {
+        this.localDate = localDate;
+        this.value = value;
+    }
+
+    public LocalDate getLocalDate() {
+        return localDate;
+    }
+
+    public Double getValue() {
+        return value;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("TimeSeriesTuple");
+        sb.append("{localDate=").append(localDate);
+        sb.append(", value=").append(value);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final TimeSeriesTuple that = (TimeSeriesTuple) o;
+
+        if (localDate != null ? !localDate.equals(that.localDate) : that.localDate != null) {
+            return false;
+        }
+        if (value != null ? !value.equals(that.value) : that.value != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = localDate != null ? localDate.hashCode() : 0;
+        result = 31 * result + (value != null ? value.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/TimeSeriesTupleMapper.java b/analytics/src/main/java/com/ning/billing/analytics/dao/TimeSeriesTupleMapper.java
new file mode 100644
index 0000000..2843e8c
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/TimeSeriesTupleMapper.java
@@ -0,0 +1,32 @@
+/*
+ * 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.dao;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.joda.time.LocalDate;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+public class TimeSeriesTupleMapper implements ResultSetMapper<TimeSeriesTuple> {
+
+    @Override
+    public TimeSeriesTuple map(final int index, final ResultSet r, final StatementContext ctx) throws SQLException {
+        return new TimeSeriesTuple(new LocalDate(r.getDate(1)), r.getDouble(2));
+    }
+}
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 82433ec..1f4e3c9 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
@@ -16,14 +16,13 @@
 
 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.BusinessTagRecorder;
 import com.ning.billing.analytics.api.AnalyticsService;
 import com.ning.billing.analytics.api.DefaultAnalyticsService;
+import com.ning.billing.analytics.api.user.AnalyticsUserApi;
 import com.ning.billing.analytics.api.user.DefaultAnalyticsUserApi;
 import com.ning.billing.analytics.dao.AnalyticsDao;
 import com.ning.billing.analytics.dao.BusinessAccountSqlDao;
@@ -42,7 +41,10 @@ import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionSqlDao;
 import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionTagSqlDao;
 import com.ning.billing.analytics.dao.DefaultAnalyticsDao;
 
+import com.google.inject.AbstractModule;
+
 public class AnalyticsModule extends AbstractModule {
+
     @Override
     protected void configure() {
         bind(BusinessAccountSqlDao.class).toProvider(new BusinessSqlProvider<BusinessAccountSqlDao>(BusinessAccountSqlDao.class));
@@ -68,5 +70,6 @@ public class AnalyticsModule extends AbstractModule {
         bind(AnalyticsService.class).to(DefaultAnalyticsService.class).asEagerSingleton();
 
         bind(DefaultAnalyticsUserApi.class).asEagerSingleton();
+        bind(AnalyticsUserApi.class).to(DefaultAnalyticsUserApi.class).asEagerSingleton();
     }
 }
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountSqlDao.sql.stg
index 0fa4317..c6b9195 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountSqlDao.sql.stg
@@ -1,5 +1,16 @@
 group BusinessAccount;
 
+getAccountsCreatedOverTime() ::= <<
+  select
+    date(from_unixtime(created_date / 1000)) day
+    -- TODO: use account_record_id, once populated
+  , count(record_id) count
+  from bac
+  group by 1
+  order by 1
+  ;
+>>
+
 getAccount(account_id) ::= <<
   select
     account_id
diff --git a/analytics/src/test/java/com/ning/billing/analytics/api/user/TestDefaultAnalyticsUserApi.java b/analytics/src/test/java/com/ning/billing/analytics/api/user/TestDefaultAnalyticsUserApi.java
new file mode 100644
index 0000000..1b7ef04
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/api/user/TestDefaultAnalyticsUserApi.java
@@ -0,0 +1,84 @@
+/*
+ * 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 java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.LocalDate;
+import org.skife.jdbi.v2.IDBI;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.ning.billing.analytics.AnalyticsTestSuiteWithEmbeddedDB;
+import com.ning.billing.analytics.api.TimeSeriesData;
+import com.ning.billing.analytics.dao.AnalyticsDao;
+import com.ning.billing.analytics.dao.BusinessAccountSqlDao;
+import com.ning.billing.analytics.dao.BusinessAccountTagSqlDao;
+import com.ning.billing.analytics.dao.BusinessInvoiceItemSqlDao;
+import com.ning.billing.analytics.dao.BusinessInvoicePaymentSqlDao;
+import com.ning.billing.analytics.dao.BusinessInvoiceSqlDao;
+import com.ning.billing.analytics.dao.BusinessOverdueStatusSqlDao;
+import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionSqlDao;
+import com.ning.billing.analytics.dao.DefaultAnalyticsDao;
+import com.ning.billing.analytics.model.BusinessAccount;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
+
+public class TestDefaultAnalyticsUserApi extends AnalyticsTestSuiteWithEmbeddedDB {
+
+    private final Clock clock = new ClockMock();
+
+    private AnalyticsUserApi analyticsUserApi;
+    private BusinessAccountSqlDao accountSqlDao;
+    private BusinessSubscriptionTransitionSqlDao subscriptionTransitionSqlDao;
+    private BusinessInvoiceSqlDao invoiceSqlDao;
+    private BusinessInvoiceItemSqlDao invoiceItemSqlDao;
+    private BusinessAccountTagSqlDao accountTagSqlDao;
+    private BusinessOverdueStatusSqlDao overdueStatusSqlDao;
+    private BusinessInvoicePaymentSqlDao invoicePaymentSqlDao;
+
+    @BeforeMethod(groups = "slow")
+    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);
+        accountTagSqlDao = dbi.onDemand(BusinessAccountTagSqlDao.class);
+        overdueStatusSqlDao = dbi.onDemand(BusinessOverdueStatusSqlDao.class);
+        invoicePaymentSqlDao = dbi.onDemand(BusinessInvoicePaymentSqlDao.class);
+
+        final AnalyticsDao analyticsDao = new DefaultAnalyticsDao(accountSqlDao, subscriptionTransitionSqlDao, invoiceSqlDao,
+                                                                  invoiceItemSqlDao, accountTagSqlDao, overdueStatusSqlDao, invoicePaymentSqlDao);
+        analyticsUserApi = new DefaultAnalyticsUserApi(analyticsDao);
+    }
+
+    @Test(groups = "slow")
+    public void testAccountsCreatedOverTime() throws Exception {
+        final BusinessAccount account = new BusinessAccount(UUID.randomUUID(), UUID.randomUUID().toString(), UUID.randomUUID().toString(), BigDecimal.ONE, clock.getUTCToday(),
+                                                            BigDecimal.TEN, "ERROR_NOT_ENOUGH_FUNDS", "CreditCard", "Visa", "FRANCE", "USD");
+        accountSqlDao.createAccount(account);
+
+        final TimeSeriesData data = analyticsUserApi.getAccountsCreatedOverTime();
+        Assert.assertEquals(data.getDates().size(), 1);
+        Assert.assertEquals(data.getDates().get(0), new LocalDate());
+        Assert.assertEquals(data.getValues().size(), 1);
+        Assert.assertEquals(data.getValues().get(0), (double) 1);
+    }
+}
diff --git a/api/src/main/java/com/ning/billing/analytics/api/TimeSeriesData.java b/api/src/main/java/com/ning/billing/analytics/api/TimeSeriesData.java
new file mode 100644
index 0000000..35c8f02
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/analytics/api/TimeSeriesData.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+import java.util.List;
+
+import org.joda.time.LocalDate;
+
+public interface TimeSeriesData {
+
+    /**
+     * @return sorted list of dates, in UTC
+     */
+    public List<LocalDate> getDates();
+
+    /**
+     * @return sorted list of values associated with the dates
+     */
+    public List<Double> getValues();
+}
diff --git a/api/src/main/java/com/ning/billing/analytics/api/user/AnalyticsUserApi.java b/api/src/main/java/com/ning/billing/analytics/api/user/AnalyticsUserApi.java
new file mode 100644
index 0000000..26a2480
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/analytics/api/user/AnalyticsUserApi.java
@@ -0,0 +1,27 @@
+/*
+ * 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 com.ning.billing.analytics.api.TimeSeriesData;
+
+public interface AnalyticsUserApi {
+
+    /**
+     * @return the number of accounts created per day
+     */
+    public TimeSeriesData getAccountsCreatedOverTime();
+}
diff --git a/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java b/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
index 361f9bf..2f9555f 100644
--- a/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
+++ b/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
@@ -16,26 +16,34 @@
 
 package com.ning.billing.util.tag;
 
+import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.dao.ObjectType;
+
+import com.google.common.collect.ImmutableList;
+
 public enum ControlTagType {
 
-    AUTO_PAY_OFF(new UUID(0, 1), "Suspends payments until removed.", true, false),
-    AUTO_INVOICING_OFF(new UUID(0, 2), "Suspends invoicing until removed.", false, true),
-    OVERDUE_ENFORCEMENT_OFF(new UUID(0, 3), "Suspends overdue enforcement behaviour until removed.", false, false),
-    WRITTEN_OFF(new UUID(0, 4), "Indicates that an invoice is written off. No billing or payment effect.", false, false),
-    MANUAL_PAY(new UUID(0, 5), "Indicates that Killbill doesn't process payments for that account (external payments only)", true, false);
+    AUTO_PAY_OFF(new UUID(0, 1), "Suspends payments until removed.", true, false, ImmutableList.<ObjectType>of(ObjectType.ACCOUNT)),
+    AUTO_INVOICING_OFF(new UUID(0, 2), "Suspends invoicing until removed.", false, true, ImmutableList.<ObjectType>of(ObjectType.ACCOUNT)),
+    OVERDUE_ENFORCEMENT_OFF(new UUID(0, 3), "Suspends overdue enforcement behaviour until removed.", false, false, ImmutableList.<ObjectType>of(ObjectType.ACCOUNT)),
+    WRITTEN_OFF(new UUID(0, 4), "Indicates that an invoice is written off. No billing or payment effect.", false, false, ImmutableList.<ObjectType>of(ObjectType.INVOICE)),
+    MANUAL_PAY(new UUID(0, 5), "Indicates that Killbill doesn't process payments for that account (external payments only)", true, false, ImmutableList.<ObjectType>of(ObjectType.ACCOUNT));
 
     private final UUID id;
     private final String description;
     private final boolean autoPaymentOff;
     private final boolean autoInvoicingOff;
+    private final List<ObjectType> applicableObjectTypes;
 
-    ControlTagType(final UUID id, final String description, final boolean autoPaymentOff, final boolean autoInvoicingOff) {
+    ControlTagType(final UUID id, final String description, final boolean autoPaymentOff, final boolean autoInvoicingOff,
+                   final List<ObjectType> applicableObjectTypes) {
         this.id = id;
         this.description = description;
         this.autoPaymentOff = autoPaymentOff;
         this.autoInvoicingOff = autoInvoicingOff;
+        this.applicableObjectTypes = applicableObjectTypes;
     }
 
     public UUID getId() {
@@ -53,4 +61,8 @@ public enum ControlTagType {
     public boolean getAutoInvoicingOff() {
         return this.autoInvoicingOff;
     }
+
+    public List<ObjectType> getApplicableObjectTypes() {
+        return applicableObjectTypes;
+    }
 }
diff --git a/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java b/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java
index b00c46b..6114060 100644
--- a/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java
+++ b/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java
@@ -16,16 +16,23 @@
 
 package com.ning.billing.util.tag;
 
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import java.util.List;
+
+import com.ning.billing.util.dao.ObjectType;
 import com.ning.billing.util.entity.Entity;
 
-// TODO: needs to surface created date, created by, isControlTag
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+// TODO: needs to surface created date, created by
 
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
 public interface TagDefinition extends Entity {
+
     String getName();
 
     String getDescription();
 
     Boolean isControlTag();
+
+    List<ObjectType> getApplicableObjectTypes();
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/TagDefinitionJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/TagDefinitionJson.java
index 86ce645..630c5ae 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/TagDefinitionJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/TagDefinitionJson.java
@@ -16,36 +16,63 @@
 
 package com.ning.billing.jaxrs.json;
 
+import java.util.List;
+
 import javax.annotation.Nullable;
 
+import com.ning.billing.util.dao.ObjectType;
 import com.ning.billing.util.tag.TagDefinition;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 
 public class TagDefinitionJson {
 
     private final String id;
+    private final Boolean isControlTag;
     private final String name;
     private final String description;
+    private final List<String> applicableObjectTypes;
 
     @JsonCreator
     public TagDefinitionJson(@JsonProperty("id") final String id,
+                             @JsonProperty("isControlTag") final Boolean isControlTag,
                              @JsonProperty("name") final String name,
-                             @JsonProperty("description") @Nullable final String description) {
+                             @JsonProperty("description") @Nullable final String description,
+                             @JsonProperty("applicableObjectTypes") @Nullable final List<String> applicableObjectTypes) {
         this.id = id;
+        this.isControlTag = isControlTag;
         this.name = name;
         this.description = description;
+        this.applicableObjectTypes = applicableObjectTypes;
     }
 
     public TagDefinitionJson(final TagDefinition tagDefinition) {
-        this(tagDefinition.getId().toString(), tagDefinition.getName(), tagDefinition.getDescription());
+        this(tagDefinition.getId().toString(), tagDefinition.isControlTag(), tagDefinition.getName(),
+             tagDefinition.getDescription(), ImmutableList.<String>copyOf(Collections2.transform(tagDefinition.getApplicableObjectTypes(), new Function<ObjectType, String>() {
+            @Override
+            public String apply(@Nullable final ObjectType input) {
+                if (input == null) {
+                    return "";
+                } else {
+                    return input.toString();
+                }
+            }
+        })));
     }
 
     public String getId() {
         return id;
     }
 
+    @JsonProperty("isControlTag")
+    public Boolean isControlTag() {
+        return isControlTag;
+    }
+
     public String getName() {
         return name;
     }
@@ -54,50 +81,68 @@ public class TagDefinitionJson {
         return description;
     }
 
+    public List<String> getApplicableObjectTypes() {
+        return applicableObjectTypes;
+    }
+
     @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result
-                 + ((description == null) ? 0 : description.hashCode());
-        result = prime * result + ((name == null) ? 0 : name.hashCode());
-        result = prime * result
-                 + ((id == null) ? 0 : id.hashCode());
-        return result;
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("TagDefinitionJson");
+        sb.append("{id='").append(id).append('\'');
+        sb.append(", isControlTag=").append(isControlTag);
+        sb.append(", name='").append(name).append('\'');
+        sb.append(", description='").append(description).append('\'');
+        sb.append(", applicableObjectTypes='").append(applicableObjectTypes).append('\'');
+        sb.append('}');
+        return sb.toString();
     }
 
-    public boolean equalsNoId(Object obj) {
-        if (this == obj)
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
             return true;
-        if (obj == null)
-            return false;
-        if (getClass() != obj.getClass())
+        }
+        if (o == null || getClass() != o.getClass()) {
             return false;
-        TagDefinitionJson other = (TagDefinitionJson) obj;
-        if (description == null) {
-            if (other.description != null)
-                return false;
-        } else if (!description.equals(other.description))
+        }
+
+        final TagDefinitionJson that = (TagDefinitionJson) o;
+
+        if (!equalsNoId(that)) {
             return false;
-        if (name == null) {
-            if (other.name != null)
-                return false;
-        } else if (!name.equals(other.name))
+        }
+        if (id != null ? !id.equals(that.id) : that.id != null) {
             return false;
+        }
+
         return true;
     }
 
-    @Override
-    public boolean equals(Object obj) {
-        if (!equalsNoId(obj)) {
+    public boolean equalsNoId(final TagDefinitionJson that) {
+        if (description != null ? !description.equals(that.description) : that.description != null) {
+            return false;
+        }
+        if (isControlTag != null ? !isControlTag.equals(that.isControlTag) : that.isControlTag != null) {
+            return false;
+        }
+        if (name != null ? !name.equals(that.name) : that.name != null) {
             return false;
         }
-        TagDefinitionJson other = (TagDefinitionJson) obj;
-        if (id == null) {
-            if (other.id != null)
-                return false;
-        } else if (!id.equals(other.id))
+        if (applicableObjectTypes != null ? !applicableObjectTypes.equals(that.applicableObjectTypes) : that.applicableObjectTypes != null) {
             return false;
+        }
+
         return true;
     }
+
+    @Override
+    public int hashCode() {
+        int result = id != null ? id.hashCode() : 0;
+        result = 31 * result + (isControlTag != null ? isControlTag.hashCode() : 0);
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        result = 31 * result + (description != null ? description.hashCode() : 0);
+        result = 31 * result + (applicableObjectTypes != null ? applicableObjectTypes.hashCode() : 0);
+        return result;
+    }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/TimeSeriesDataJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/TimeSeriesDataJson.java
new file mode 100644
index 0000000..cbfbfe5
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/TimeSeriesDataJson.java
@@ -0,0 +1,103 @@
+/*
+ * 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.jaxrs.json;
+
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.LocalDate;
+
+import com.ning.billing.analytics.api.TimeSeriesData;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+
+public class TimeSeriesDataJson {
+
+    private final List<String> dates;
+    private final List<Double> values;
+
+    @JsonCreator
+    public TimeSeriesDataJson(@JsonProperty("dates") final List<String> dates,
+                              @JsonProperty("values") final List<Double> values) {
+        this.dates = dates;
+        this.values = values;
+    }
+
+    public TimeSeriesDataJson(final TimeSeriesData data) {
+        this(ImmutableList.<String>copyOf(Collections2.transform(data.getDates(), new Function<LocalDate, String>() {
+            @Override
+            public String apply(@Nullable final LocalDate input) {
+                if (input == null) {
+                    return null;
+                } else {
+                    return input.toString();
+                }
+            }
+        })), data.getValues());
+    }
+
+    public List<String> getDates() {
+        return dates;
+    }
+
+    public List<Double> getValues() {
+        return values;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("TimeSeriesDataJson");
+        sb.append("{dates=").append(dates);
+        sb.append(", values=").append(values);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final TimeSeriesDataJson that = (TimeSeriesDataJson) o;
+
+        if (dates != null ? !dates.equals(that.dates) : that.dates != null) {
+            return false;
+        }
+        if (values != null ? !values.equals(that.values) : that.values != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = dates != null ? dates.hashCode() : 0;
+        result = 31 * result + (values != null ? values.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AnalyticsResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AnalyticsResource.java
new file mode 100644
index 0000000..fbc3623
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AnalyticsResource.java
@@ -0,0 +1,53 @@
+/*
+ * 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.jaxrs.resources;
+
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import com.ning.billing.analytics.api.TimeSeriesData;
+import com.ning.billing.analytics.api.user.AnalyticsUserApi;
+import com.ning.billing.jaxrs.json.TimeSeriesDataJson;
+
+import com.google.inject.Singleton;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+@Singleton
+@Path(JaxrsResource.ANALYTICS_PATH)
+public class AnalyticsResource {
+
+    private final AnalyticsUserApi analyticsUserApi;
+
+    @Inject
+    public AnalyticsResource(final AnalyticsUserApi analyticsUserApi) {
+        this.analyticsUserApi = analyticsUserApi;
+    }
+
+    @GET
+    @Path("/accountsCreatedOverTime")
+    @Produces(APPLICATION_JSON)
+    public Response getAccountsCreatedOverTime() {
+        final TimeSeriesData data = analyticsUserApi.getAccountsCreatedOverTime();
+        final TimeSeriesDataJson json = new TimeSeriesDataJson(data);
+        return Response.status(Status.OK).entity(json).build();
+    }
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
index eeb1034..6e83cc8 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
@@ -77,6 +77,9 @@ public interface JaxrsResource {
     public static final String ACCOUNTS = "accounts";
     public static final String ACCOUNTS_PATH = PREFIX + "/" + ACCOUNTS;
 
+    public static final String ANALYTICS = "analytics";
+    public static final String ANALYTICS_PATH = PREFIX + "/" + ANALYTICS;
+
     public static final String BUNDLES = "bundles";
     public static final String BUNDLES_PATH = PREFIX + "/" + BUNDLES;
 
diff --git a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestTagDefinitionJson.java b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestTagDefinitionJson.java
index 01850a0..6aa44ed 100644
--- a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestTagDefinitionJson.java
+++ b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestTagDefinitionJson.java
@@ -21,26 +21,30 @@ import java.util.UUID;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
 import com.ning.billing.jaxrs.JaxrsTestSuite;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableList;
+
 public class TestTagDefinitionJson extends JaxrsTestSuite {
+
     private static final ObjectMapper mapper = new ObjectMapper();
 
     @Test(groups = "fast")
     public void testJson() throws Exception {
         final String id = UUID.randomUUID().toString();
+        final Boolean isControlTag = true;
         final String name = UUID.randomUUID().toString();
         final String description = UUID.randomUUID().toString();
-        final TagDefinitionJson tagDefinitionJson = new TagDefinitionJson(id, name, description);
+        final ImmutableList<String> applicableObjectTypes = ImmutableList.<String>of(UUID.randomUUID().toString());
+        final TagDefinitionJson tagDefinitionJson = new TagDefinitionJson(id, isControlTag, name, description, applicableObjectTypes);
+        Assert.assertEquals(tagDefinitionJson.getId(), id);
+        Assert.assertEquals(tagDefinitionJson.isControlTag(), isControlTag);
         Assert.assertEquals(tagDefinitionJson.getName(), name);
         Assert.assertEquals(tagDefinitionJson.getDescription(), description);
+        Assert.assertEquals(tagDefinitionJson.getApplicableObjectTypes(), applicableObjectTypes);
 
         final String asJson = mapper.writeValueAsString(tagDefinitionJson);
-        Assert.assertEquals(asJson, "{\"id\":\"" + tagDefinitionJson.getId() + "\"," +
-                "\"name\":\"" + tagDefinitionJson.getName() + "\"," +
-                "\"description\":\"" + tagDefinitionJson.getDescription() + "\"}");
-
         final TagDefinitionJson fromJson = mapper.readValue(asJson, TagDefinitionJson.class);
         Assert.assertEquals(fromJson, tagDefinitionJson);
     }
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
index 226f4ea..cfe911d 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
@@ -44,6 +44,7 @@ import com.ning.billing.jaxrs.resources.JaxrsResource;
 import com.ning.http.client.Response;
 
 import com.fasterxml.jackson.core.type.TypeReference;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
 import static org.testng.Assert.assertEquals;
@@ -259,7 +260,8 @@ public class TestAccount extends TestJaxrsBase {
     public void testTags() throws Exception {
 
         // Use tag definition for AUTO_PAY_OFF
-        final TagDefinitionJson input = new TagDefinitionJson(new UUID(0, 1).toString(), "AUTO_PAY_OFF", "nothing more to say");
+        final TagDefinitionJson input = new TagDefinitionJson(new UUID(0, 1).toString(), false, "AUTO_PAY_OFF",
+                                                              "nothing more to say", ImmutableList.<String>of());
 
         final Map<String, String> queryParams = new HashMap<String, String>();
         queryParams.put(JaxrsResource.QUERY_TAGS, input.getId());
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestTag.java b/server/src/test/java/com/ning/billing/jaxrs/TestTag.java
index 2fbf239..fa84c93 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestTag.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestTag.java
@@ -27,6 +27,7 @@ import com.ning.billing.jaxrs.resources.JaxrsResource;
 import com.ning.http.client.Response;
 
 import com.fasterxml.jackson.core.type.TypeReference;
+import com.google.common.collect.ImmutableList;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
@@ -36,7 +37,7 @@ public class TestTag extends TestJaxrsBase {
 
     @Test(groups = "slow")
     public void testTagDefinitionOk() throws Exception {
-        final TagDefinitionJson input = new TagDefinitionJson(null, "blue", "relaxing color");
+        final TagDefinitionJson input = new TagDefinitionJson(null, false, "blue", "relaxing color", ImmutableList.<String>of());
         String baseJson = mapper.writeValueAsString(input);
         Response response = doPost(JaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
@@ -51,7 +52,8 @@ public class TestTag extends TestJaxrsBase {
         baseJson = response.getResponseBody();
         final TagDefinitionJson objFromJson = mapper.readValue(baseJson, TagDefinitionJson.class);
         assertNotNull(objFromJson);
-        assertTrue(objFromJson.equalsNoId(input));
+        assertEquals(objFromJson.getName(), input.getName());
+        assertEquals(objFromJson.getDescription(), input.getDescription());
     }
 
     @Test(groups = "slow")
@@ -63,22 +65,22 @@ public class TestTag extends TestJaxrsBase {
         List<TagDefinitionJson> objFromJson = mapper.readValue(baseJson, new TypeReference<List<TagDefinitionJson>>() {});
         final int sizeSystemTag = (objFromJson == null || objFromJson.size() == 0) ? 0 : objFromJson.size();
 
-        final TagDefinitionJson inputBlue = new TagDefinitionJson(null, "blue", "relaxing color");
+        final TagDefinitionJson inputBlue = new TagDefinitionJson(null, false, "blue", "relaxing color", ImmutableList.<String>of());
         baseJson = mapper.writeValueAsString(inputBlue);
         response = doPost(JaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
 
-        final TagDefinitionJson inputRed = new TagDefinitionJson(null, "red", "hot color");
+        final TagDefinitionJson inputRed = new TagDefinitionJson(null, false, "red", "hot color", ImmutableList.<String>of());
         baseJson = mapper.writeValueAsString(inputRed);
         response = doPost(JaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
 
-        final TagDefinitionJson inputYellow = new TagDefinitionJson(null, "yellow", "vibrant color");
+        final TagDefinitionJson inputYellow = new TagDefinitionJson(null, false, "yellow", "vibrant color", ImmutableList.<String>of());
         baseJson = mapper.writeValueAsString(inputYellow);
         response = doPost(JaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
 
-        final TagDefinitionJson inputGreen = new TagDefinitionJson(null, "green", "super relaxing color");
+        final TagDefinitionJson inputGreen = new TagDefinitionJson(null, false, "green", "super relaxing color", ImmutableList.<String>of());
         baseJson = mapper.writeValueAsString(inputGreen);
         response = doPost(JaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java
index 7cf580e..95b1991 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java
@@ -73,14 +73,14 @@ public class AuditedTagDao extends AuditedCollectionDaoBase<Tag, Tag> implements
         throws TagApiException {
 
         TagDefinition tagDefintion = null;
-        for (ControlTagType t : ControlTagType.values()) {
+        for (final ControlTagType t : ControlTagType.values()) {
             if (t.getId().equals(tagDefinitionId)) {
-                tagDefintion = new DefaultTagDefinition(t.getId(), t.toString(), t.getDescription(), true);
+                tagDefintion = new DefaultTagDefinition(t);
                 break;
             }
         }
         if (tagDefintion == null) {
-            TagDefinitionSqlDao transTagDefintionSqlDao = tagSqlDao.become(TagDefinitionSqlDao.class);
+            final TagDefinitionSqlDao transTagDefintionSqlDao = tagSqlDao.become(TagDefinitionSqlDao.class);
             tagDefintion = transTagDefintionSqlDao.getById(tagDefinitionId.toString());
         }
 
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
index fc0ddf2..30700a1 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
@@ -63,16 +63,16 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
 
         // Add control tag definitions
         for (final ControlTagType controlTag : ControlTagType.values()) {
-            definitionList.add(new DefaultTagDefinition(controlTag.getId(), controlTag.toString(), controlTag.getDescription(), true));
+            definitionList.add(new DefaultTagDefinition(controlTag));
         }
         return definitionList;
     }
 
     @Override
-    public TagDefinition getByName(String definitionName) {
+    public TagDefinition getByName(final String definitionName) {
         for (final ControlTagType controlTag : ControlTagType.values()) {
             if (controlTag.name().equals(definitionName)) {
-                return new DefaultTagDefinition(controlTag.getId(), controlTag.toString(), controlTag.getDescription(), true);
+                return new DefaultTagDefinition(controlTag);
             }
          }
         return tagDefinitionSqlDao.getByName(definitionName);
@@ -83,7 +83,7 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
     public TagDefinition getById(final UUID definitionId) {
         for (final ControlTagType controlTag : ControlTagType.values()) {
             if (controlTag.getId().equals(definitionId)) {
-                return new DefaultTagDefinition(controlTag.getId(), controlTag.toString(), controlTag.getDescription(), true);
+                return new DefaultTagDefinition(controlTag);
             }
         }
         return tagDefinitionSqlDao.getById(definitionId.toString());
@@ -91,12 +91,11 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
 
     @Override
     public List<TagDefinition> getByIds(final Collection<UUID> definitionIds) {
-
-        List<TagDefinition> result = new LinkedList<TagDefinition>();
+        final List<TagDefinition> result = new LinkedList<TagDefinition>();
         for (final UUID cur : definitionIds) {
             for (final ControlTagType controlTag : ControlTagType.values()) {
                 if (controlTag.getId().equals(cur)) {
-                    result.add(new DefaultTagDefinition(controlTag.getId(), controlTag.toString(), controlTag.getDescription(), true));
+                    result.add(new DefaultTagDefinition(controlTag));
                     break;
                 }
             }
diff --git a/util/src/main/java/com/ning/billing/util/tag/DefaultTagDefinition.java b/util/src/main/java/com/ning/billing/util/tag/DefaultTagDefinition.java
index 3e566a5..8b0d38c 100644
--- a/util/src/main/java/com/ning/billing/util/tag/DefaultTagDefinition.java
+++ b/util/src/main/java/com/ning/billing/util/tag/DefaultTagDefinition.java
@@ -16,30 +16,46 @@
 
 package com.ning.billing.util.tag;
 
+import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.dao.ObjectType;
+import com.ning.billing.util.entity.EntityBase;
+
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.ning.billing.util.entity.EntityBase;
+import com.google.common.collect.ImmutableList;
 
 public class DefaultTagDefinition extends EntityBase implements TagDefinition {
+
     private final String name;
     private final String description;
     private final Boolean controlTag;
+    private final List<ObjectType> applicableObjectTypes;
 
     public DefaultTagDefinition(final String name, final String description, final Boolean isControlTag) {
         this(UUID.randomUUID(), name, description, isControlTag);
     }
 
+    public DefaultTagDefinition(final UUID id, final String name, final String description, final Boolean isControlTag) {
+        this(id, name, description, isControlTag, ImmutableList.<ObjectType>copyOf(ObjectType.values()));
+    }
+
+    public DefaultTagDefinition(final ControlTagType controlTag) {
+        this(controlTag.getId(), controlTag.toString(), controlTag.getDescription(), true, controlTag.getApplicableObjectTypes());
+    }
+
     @JsonCreator
     public DefaultTagDefinition(@JsonProperty("id") final UUID id,
                                 @JsonProperty("name") final String name,
                                 @JsonProperty("description") final String description,
-                                @JsonProperty("controlTag") final Boolean controlTag) {
+                                @JsonProperty("controlTag") final Boolean controlTag,
+                                @JsonProperty("applicableObjectTypes") final List<ObjectType> applicableObjectTypes) {
         super(id);
         this.name = name;
         this.description = description;
         this.controlTag = controlTag;
+        this.applicableObjectTypes = applicableObjectTypes;
     }
 
     @Override
@@ -58,18 +74,22 @@ public class DefaultTagDefinition extends EntityBase implements TagDefinition {
     }
 
     @Override
+    public List<ObjectType> getApplicableObjectTypes() {
+        return applicableObjectTypes;
+    }
+
+    @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
         sb.append("DefaultTagDefinition");
-        sb.append("{description='").append(description).append('\'');
-        sb.append(", name='").append(name).append('\'');
+        sb.append("{name='").append(name).append('\'');
+        sb.append(", description='").append(description).append('\'');
         sb.append(", controlTag=").append(controlTag);
+        sb.append(", applicableObjectTypes=").append(applicableObjectTypes);
         sb.append('}');
         return sb.toString();
     }
 
-
-
     @Override
     public boolean equals(final Object o) {
         if (this == o) {
@@ -78,29 +98,31 @@ public class DefaultTagDefinition extends EntityBase implements TagDefinition {
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
+
         final DefaultTagDefinition that = (DefaultTagDefinition) o;
 
-        if (id != null ? !id.equals(that.id) : that.id != null) {
+        if (applicableObjectTypes != null ? !applicableObjectTypes.equals(that.applicableObjectTypes) : that.applicableObjectTypes != null) {
             return false;
         }
-        if (description != null ? !description.equals(that.description) : that.description != null) {
+        if (controlTag != null ? !controlTag.equals(that.controlTag) : that.controlTag != null) {
             return false;
         }
-        if (controlTag != null ? !controlTag.equals(that.controlTag) : that.controlTag != null) {
+        if (description != null ? !description.equals(that.description) : that.description != null) {
             return false;
         }
         if (name != null ? !name.equals(that.name) : that.name != null) {
             return false;
         }
+
         return true;
     }
 
     @Override
     public int hashCode() {
         int result = name != null ? name.hashCode() : 0;
-        result = 31 * result + (id != null ? id.hashCode() : 0);
         result = 31 * result + (description != null ? description.hashCode() : 0);
         result = 31 * result + (controlTag != null ? controlTag.hashCode() : 0);
+        result = 31 * result + (applicableObjectTypes != null ? applicableObjectTypes.hashCode() : 0);
         return result;
     }
 }