killbill-aplcache
Changes
analytics/src/main/java/com/ning/billing/analytics/api/sanity/DefaultAnalyticsSanityApi.java 81(+81 -0)
Details
diff --git a/analytics/src/main/java/com/ning/billing/analytics/api/sanity/DefaultAnalyticsSanityApi.java b/analytics/src/main/java/com/ning/billing/analytics/api/sanity/DefaultAnalyticsSanityApi.java
new file mode 100644
index 0000000..11c82ff
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/api/sanity/DefaultAnalyticsSanityApi.java
@@ -0,0 +1,81 @@
+/*
+ * 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.sanity;
+
+import java.util.Collection;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import com.ning.billing.analytics.dao.AnalyticsSanityDao;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.callcontext.TenantContext;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+public class DefaultAnalyticsSanityApi implements AnalyticsSanityApi {
+
+ private final AnalyticsSanityDao dao;
+ private final InternalCallContextFactory internalCallContextFactory;
+
+ @Inject
+ public DefaultAnalyticsSanityApi(final AnalyticsSanityDao dao,
+ final InternalCallContextFactory internalCallContextFactory) {
+ this.dao = dao;
+ this.internalCallContextFactory = internalCallContextFactory;
+ }
+
+ @Override
+ public Collection<UUID> checkAnalyticsInSyncWithEntitlement(final TenantContext context) {
+ final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context);
+ return dao.checkBstMatchesSubscriptionEvents(internalTenantContext);
+ }
+
+ @Override
+ public Collection<UUID> checkAnalyticsInSyncWithInvoice(final TenantContext context) {
+ final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context);
+ return dao.checkBiiMatchesInvoiceItems(internalTenantContext);
+ }
+
+ @Override
+ public Collection<UUID> checkAnalyticsInSyncWithPayment(final TenantContext context) {
+ final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context);
+ final Collection<UUID> check1 = dao.checkBipMatchesInvoicePayments(internalTenantContext);
+ final Collection<UUID> check2 = dao.checkBinAmountPaidMatchesInvoicePayments(internalTenantContext);
+ final Collection<UUID> check3 = dao.checkBinAmountChargedMatchesInvoicePayments(internalTenantContext);
+
+ return ImmutableSet.<UUID>copyOf(Iterables.<UUID>concat(check1, check2, check3));
+ }
+
+ @Override
+ public Collection<UUID> checkAnalyticsInSyncWithTag(final TenantContext context) {
+ final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context);
+ return dao.checkBacTagsMatchesTags(internalTenantContext);
+ }
+
+ @Override
+ public Collection<UUID> checkAnalyticsConsistency(final TenantContext context) {
+ final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context);
+ final Collection<UUID> check1 = dao.checkBinBiiBalanceConsistency(internalTenantContext);
+ final Collection<UUID> check2 = dao.checkBinBiiAmountCreditedConsistency(internalTenantContext);
+ final Collection<UUID> check3 = dao.checkBacBinBiiConsistency(internalTenantContext);
+
+ return ImmutableSet.<UUID>copyOf(Iterables.<UUID>concat(check1, check2, check3));
+ }
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsSanityDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsSanityDao.java
new file mode 100644
index 0000000..9a689ab
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsSanityDao.java
@@ -0,0 +1,43 @@
+/*
+ * 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.util.Collection;
+import java.util.UUID;
+
+import com.ning.billing.util.callcontext.InternalTenantContext;
+
+public interface AnalyticsSanityDao {
+
+ public Collection<UUID> checkBstMatchesSubscriptionEvents(InternalTenantContext context);
+
+ public Collection<UUID> checkBiiMatchesInvoiceItems(InternalTenantContext context);
+
+ public Collection<UUID> checkBipMatchesInvoicePayments(InternalTenantContext context);
+
+ public Collection<UUID> checkBinAmountPaidMatchesInvoicePayments(InternalTenantContext context);
+
+ public Collection<UUID> checkBinAmountChargedMatchesInvoicePayments(InternalTenantContext context);
+
+ public Collection<UUID> checkBinBiiBalanceConsistency(InternalTenantContext context);
+
+ public Collection<UUID> checkBinBiiAmountCreditedConsistency(InternalTenantContext context);
+
+ public Collection<UUID> checkBacBinBiiConsistency(InternalTenantContext context);
+
+ public Collection<UUID> checkBacTagsMatchesTags(InternalTenantContext context);
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsSanitySqlDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsSanitySqlDao.java
new file mode 100644
index 0000000..fe9931f
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsSanitySqlDao.java
@@ -0,0 +1,62 @@
+/*
+ * 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.util.Collection;
+import java.util.UUID;
+
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+
+import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.callcontext.InternalTenantContextBinder;
+import com.ning.billing.util.dao.UuidMapper;
+
+@ExternalizedSqlViaStringTemplate3()
+@RegisterMapper(UuidMapper.class)
+public interface AnalyticsSanitySqlDao extends Transactional<AnalyticsSanitySqlDao>, Transmogrifier {
+
+ @SqlQuery
+ public Collection<UUID> checkBstMatchesSubscriptionEvents(@InternalTenantContextBinder final InternalTenantContext context);
+
+ @SqlQuery
+ public Collection<UUID> checkBiiMatchesInvoiceItems(@InternalTenantContextBinder final InternalTenantContext context);
+
+ @SqlQuery
+ public Collection<UUID> checkBipMatchesInvoicePayments(@InternalTenantContextBinder final InternalTenantContext context);
+
+ @SqlQuery
+ public Collection<UUID> checkBinAmountPaidMatchesInvoicePayments(@InternalTenantContextBinder final InternalTenantContext context);
+
+ @SqlQuery
+ public Collection<UUID> checkBinAmountChargedMatchesInvoicePayments(@InternalTenantContextBinder final InternalTenantContext context);
+
+ @SqlQuery
+ public Collection<UUID> checkBinBiiBalanceConsistency(@InternalTenantContextBinder final InternalTenantContext context);
+
+ @SqlQuery
+ public Collection<UUID> checkBinBiiAmountCreditedConsistency(@InternalTenantContextBinder final InternalTenantContext context);
+
+ @SqlQuery
+ public Collection<UUID> checkBacBinBiiConsistency(@InternalTenantContextBinder final InternalTenantContext context);
+
+ @SqlQuery
+ public Collection<UUID> checkBacTagsMatchesTags(@InternalTenantContextBinder final InternalTenantContext context);
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsSanityDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsSanityDao.java
new file mode 100644
index 0000000..8e97d89
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsSanityDao.java
@@ -0,0 +1,81 @@
+/*
+ * 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.util.Collection;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.skife.jdbi.v2.IDBI;
+
+import com.ning.billing.util.callcontext.InternalTenantContext;
+
+public class DefaultAnalyticsSanityDao implements AnalyticsSanityDao {
+
+ private final AnalyticsSanitySqlDao sqlDao;
+
+ @Inject
+ public DefaultAnalyticsSanityDao(final IDBI dbi) {
+ sqlDao = dbi.onDemand(AnalyticsSanitySqlDao.class);
+ }
+
+ @Override
+ public Collection<UUID> checkBstMatchesSubscriptionEvents(final InternalTenantContext context) {
+ return sqlDao.checkBstMatchesSubscriptionEvents(context);
+ }
+
+ @Override
+ public Collection<UUID> checkBiiMatchesInvoiceItems(final InternalTenantContext context) {
+ return sqlDao.checkBiiMatchesInvoiceItems(context);
+ }
+
+ @Override
+ public Collection<UUID> checkBipMatchesInvoicePayments(final InternalTenantContext context) {
+ return sqlDao.checkBipMatchesInvoicePayments(context);
+ }
+
+ @Override
+ public Collection<UUID> checkBinAmountPaidMatchesInvoicePayments(final InternalTenantContext context) {
+ return sqlDao.checkBinAmountPaidMatchesInvoicePayments(context);
+ }
+
+ @Override
+ public Collection<UUID> checkBinAmountChargedMatchesInvoicePayments(final InternalTenantContext context) {
+ return sqlDao.checkBinAmountChargedMatchesInvoicePayments(context);
+ }
+
+ @Override
+ public Collection<UUID> checkBinBiiBalanceConsistency(final InternalTenantContext context) {
+ return sqlDao.checkBinBiiBalanceConsistency(context);
+ }
+
+ @Override
+ public Collection<UUID> checkBinBiiAmountCreditedConsistency(final InternalTenantContext context) {
+ return sqlDao.checkBinBiiAmountCreditedConsistency(context);
+ }
+
+ @Override
+ public Collection<UUID> checkBacBinBiiConsistency(final InternalTenantContext context) {
+ return sqlDao.checkBacBinBiiConsistency(context);
+ }
+
+ @Override
+ public Collection<UUID> checkBacTagsMatchesTags(final InternalTenantContext context) {
+ return sqlDao.checkBacTagsMatchesTags(context);
+ }
+}
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 7c9e1fd..ec2732e 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
@@ -22,9 +22,12 @@ import com.ning.billing.analytics.BusinessSubscriptionTransitionDao;
import com.ning.billing.analytics.BusinessTagDao;
import com.ning.billing.analytics.api.AnalyticsService;
import com.ning.billing.analytics.api.DefaultAnalyticsService;
+import com.ning.billing.analytics.api.sanity.AnalyticsSanityApi;
+import com.ning.billing.analytics.api.sanity.DefaultAnalyticsSanityApi;
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.AnalyticsSanityDao;
import com.ning.billing.analytics.dao.BusinessAccountSqlDao;
import com.ning.billing.analytics.dao.BusinessAccountTagSqlDao;
import com.ning.billing.analytics.dao.BusinessInvoiceFieldSqlDao;
@@ -40,6 +43,7 @@ import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionFieldSqlDao;
import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionSqlDao;
import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionTagSqlDao;
import com.ning.billing.analytics.dao.DefaultAnalyticsDao;
+import com.ning.billing.analytics.dao.DefaultAnalyticsSanityDao;
import com.google.inject.AbstractModule;
@@ -67,9 +71,11 @@ public class AnalyticsModule extends AbstractModule {
bind(AnalyticsListener.class).asEagerSingleton();
bind(AnalyticsDao.class).to(DefaultAnalyticsDao.class).asEagerSingleton();
+ bind(AnalyticsSanityDao.class).to(DefaultAnalyticsSanityDao.class).asEagerSingleton();
bind(AnalyticsService.class).to(DefaultAnalyticsService.class).asEagerSingleton();
bind(DefaultAnalyticsUserApi.class).asEagerSingleton();
+ bind(AnalyticsSanityApi.class).to(DefaultAnalyticsSanityApi.class).asEagerSingleton();
bind(AnalyticsUserApi.class).to(DefaultAnalyticsUserApi.class).asEagerSingleton();
}
}
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/AnalyticsSanitySqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/AnalyticsSanitySqlDao.sql.stg
new file mode 100644
index 0000000..4040115
--- /dev/null
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/AnalyticsSanitySqlDao.sql.stg
@@ -0,0 +1,301 @@
+group AnalyticsSanitySqlDao;
+
+CHECK_TENANT(prefix) ::= "<prefix>tenant_record_id = :tenantRecordId"
+AND_CHECK_TENANT(prefix) ::= "AND <CHECK_TENANT(prefix)>"
+
+checkBstMatchesSubscriptionEvents() ::= <<
+select distinct
+ account_id
+from (
+ select
+ account_id
+ , sum(per_event_check) account_check_left
+ , count(*) account_check_right
+ from (
+ select
+ account_id
+ , account_key_check and app_id and date_type and slug per_event_check
+ from (
+ select
+ q.account_key
+ , q.account_id
+ , b_account_key = account_key account_key_check
+ , b_app_id = app_id app_id
+ , case
+ when b_event like 'CANCEL_%' then b_req_dt = req_dt
+ when b_event like 'SYSTEM_CANCEL_%' then b_req_dt = eff_dt
+ else b_req_dt = req_dt and b_eff_dt = eff_dt
+ end date_type
+ , coalesce(b_slug = slug, 1) slug
+ from (
+ select
+ bst.total_ordering record_id
+ , bst.account_key b_account_key
+ , bst.external_key b_app_id
+ , bst.event b_event
+ , bst.next_slug b_slug
+ , from_unixtime(bst.requested_timestamp / 1000) b_req_dt
+ , from_unixtime(bst.next_start_date / 1000) b_eff_dt
+ , a.external_key account_key
+ , a.id account_id
+ , b.external_key app_id
+ , s.id
+ , e.event_type
+ , e.user_type
+ , e.phase_name slug
+ , e.effective_date eff_dt
+ , e.requested_date req_dt
+ , from_unixtime(coalesce(bst.next_start_date, bst.requested_timestamp) / 1000) b_dt
+ from subscription_events e
+ join subscriptions s on e.subscription_id = s.id
+ join bundles b on s.bundle_id = b.id
+ join accounts a on b.account_id = a.id
+ join bst on bst.total_ordering = e.record_id
+ where
+ e.is_active = 1
+ and e.user_type != 'MIGRATE_BILLING'
+ <AND_CHECK_TENANT("e.")>
+ <AND_CHECK_TENANT("s.")>
+ <AND_CHECK_TENANT("b.")>
+ <AND_CHECK_TENANT("a.")>
+ <AND_CHECK_TENANT("bst.")>
+ order by e.record_id asc
+ ) q
+ ) p
+ ) r group by (account_id)
+) s
+where account_check_left != account_check_right
+;
+>>
+
+checkBiiMatchesInvoiceItems() ::= <<
+select distinct
+ account_id
+from (
+ select
+ id
+ , account_id
+ , start_date_check and end_date_check and amount_check and currency_check and linked_item_id_check and slug_check per_item_check
+ from (
+ select
+ ii.id
+ , ii.account_id
+ , ii.start_date = bii.start_date start_date_check
+ , coalesce(ii.end_date, bii.end_date) = bii.end_date end_date_check
+ , ii.amount = bii.amount amount_check
+ , ii.currency = bii.currency currency_check
+ , coalesce(ii.linked_item_id = bii.linked_item_id, 1) linked_item_id_check
+ , ii.phase_name = bii.slug slug_check
+ from invoice_items ii
+ join bii on ii.id = bii.item_id
+ where <CHECK_TENANT("ii.")>
+ <AND_CHECK_TENANT("bii.")>
+ ) p
+) q where !per_item_check
+;
+>>
+
+checkBipMatchesInvoicePayments() ::= <<
+select distinct
+ account_id
+from (
+ select
+ payment_id
+ , account_id
+ , amount_check and currency_check and payment_type_check and linked_invoice_payment_id_check and invoice_id_check total_check
+ from (
+ select
+ bip.payment_id
+ , a.id account_id
+ , bip.amount = ip.amount amount_check
+ , bip.currency = ip.currency currency_check
+ , bip.invoice_payment_type = ip.type payment_type_check
+ , bip.linked_invoice_payment_id = ip.linked_invoice_payment_id linked_invoice_payment_id_check
+ , bip.invoice_id = ip.invoice_id invoice_id_check
+ from bip
+ join invoice_payments ip on bip.payment_id = ip.id
+ join accounts a on a.record_id = ip.account_record_id
+ where <CHECK_TENANT("bip.")>
+ <AND_CHECK_TENANT("ip.")>
+ <AND_CHECK_TENANT("a.")>
+ ) p
+) q where !total_check
+;
+>>
+
+checkBinAmountPaidMatchesInvoicePayments() ::= <<
+select distinct
+ account_id
+from (
+ select
+ bin.invoice_id
+ , bin.account_id
+ , sum(ip.amount) = amount_paid amount_paid_check
+ from bin
+ join invoice_payments ip on bin.invoice_id = ip.invoice_id
+ where <CHECK_TENANT("bin.")>
+ <AND_CHECK_TENANT("ip.")>
+ group by ip.invoice_id, bin.account_id
+) p where !amount_paid_check
+;
+>>
+
+checkBinAmountChargedMatchesInvoicePayments() ::= <<
+select distinct
+ account_id
+from (
+ select
+ bin.invoice_id
+ , bin.account_id
+ , sum(ip.amount) = amount_charged amount_charged_check
+ from bin
+ join invoice_payments ip on bin.invoice_id = ip.invoice_id
+ where <CHECK_TENANT("bin.")>
+ <AND_CHECK_TENANT("ip.")>
+ group by ip.invoice_id, bin.account_id
+) p where !amount_charged_check
+;
+>>
+
+checkBinBiiBalanceConsistency() ::= <<
+select distinct
+ account_id
+from (
+ select
+ invoice_id
+ , account_id
+ , balance = amount_charged + total_adj_amount + total_cba - amount_paid balance_check
+ from (
+ select
+ bin.invoice_id
+ , bin.account_id
+ , bin.amount_paid
+ , bin.amount_charged
+ , bin.amount_credited
+ , bin.balance
+ , coalesce(total_adj_amount, 0) total_adj_amount
+ , coalesce(total_cba, 0) total_cba
+ from bin
+ left join (
+ select
+ bii.invoice_id
+ , sum(amount) total_adj_amount
+ from bii
+ join bin on bin.invoice_id = bii.invoice_id
+ where bii.item_type in ('CREDIT_ADJ', 'REFUND_ADJ', 'ITEM_ADJ')
+ <AND_CHECK_TENANT("bii.")>
+ <AND_CHECK_TENANT("bin.")>
+ group by (bii.invoice_id)
+ ) p on bin.invoice_id = p.invoice_id
+ left join (
+ select
+ q.invoice_id
+ , total_cba
+ from (
+ select
+ bii.invoice_id
+ , sum(amount) total_cba
+ from bii
+ join bin on bin.invoice_id = bii.invoice_id
+ where bii.item_type in ('CBA_ADJ')
+ <AND_CHECK_TENANT("bii.")>
+ <AND_CHECK_TENANT("bin.")>
+ group by (bii.invoice_id)
+ ) q
+ ) r on r.invoice_id = bin.invoice_id
+ where <CHECK_TENANT("bin.")>
+ ) s
+) t where !balance_check
+;
+>>
+
+checkBinBiiAmountCreditedConsistency() ::= <<
+select distinct
+ account_id
+from (
+ select
+ bii.invoice_id
+ , bin.account_id
+ , sum(amount) = bin.amount_credited credit_check
+ from bii
+ join bin on bin.invoice_id = bii.invoice_id
+ where bii.item_type in ('CREDIT_ADJ')
+ <AND_CHECK_TENANT("bii.")>
+ <AND_CHECK_TENANT("bin.")>
+ group by bii.invoice_id, bin.account_id
+) p where !credit_check
+;
+>>
+
+checkBacBinBiiConsistency() ::= <<
+select distinct
+ account_id
+from (
+ select
+ account_id
+ , total_invoice_balance_check and total_account_balance_check bac_check
+ from (
+ select
+ account_id
+ , total_invoice_balance_on_account = total_invoice_balance total_invoice_balance_check
+ , total_account_balance = total_invoice_balance_on_account - account_cba total_account_balance_check
+ from (
+ select
+ bac.account_id
+ , bac.total_invoice_balance total_invoice_balance_on_account
+ , sum(bin.balance) total_invoice_balance
+ , bac.balance total_account_balance
+ , coalesce(account_cba, 0) account_cba
+ from bac
+ -- some might not have cba items
+ left join (
+ select
+ bin.account_id
+ , sum(bii.amount) account_cba
+ from bac
+ join bin on bin.account_id = bac.account_id
+ join bii on bii.invoice_id = bin.invoice_id
+ where bii.item_type = 'CBA_ADJ'
+ <AND_CHECK_TENANT("bac.")>
+ <AND_CHECK_TENANT("bin.")>
+ <AND_CHECK_TENANT("bii.")>
+ group by (bin.account_id)
+ ) p on bac.account_id = p.account_id
+ left join bin on bin.account_id = bac.account_id
+ where <CHECK_TENANT("bac.")>
+ <AND_CHECK_TENANT("bin.")>
+ group by bac.account_id, account_cba
+ ) q
+ ) r
+) s where !bac_check
+;
+>>
+
+checkBacTagsMatchesTags() ::= <<
+select distinct
+ account_id
+from (
+ select
+ account_id
+ , b_tag_name = tag_name tag_name_check
+ from (
+ select
+ bt.account_id account_id
+ , bt.name b_tag_name
+ , case
+ when t.tag_definition_id = '00000000-0000-0000-0000-000000000001' then 'AUTO_PAY_OFF'
+ when t.tag_definition_id = '00000000-0000-0000-0000-000000000002' then 'AUTO_INVOICING_OFF'
+ when t.tag_definition_id = '00000000-0000-0000-0000-000000000003' then 'OVERDUE_ENFORCEMENT_OFF'
+ when t.tag_definition_id = '00000000-0000-0000-0000-000000000003' then 'WRITTEN_OFF'
+ else tdef.name
+ end tag_name
+ from bac_tags bt
+ join tags t on t.object_id = bt.account_id
+ left join tag_definitions tdef on t.tag_definition_id = tdef.id
+ where t.object_type = 'account'
+ <AND_CHECK_TENANT("bt.")>
+ <AND_CHECK_TENANT("t.")>
+ <AND_CHECK_TENANT("tdef.")>
+ ) p
+) q where ! tag_name_check;
+>>
diff --git a/api/src/main/java/com/ning/billing/analytics/api/sanity/AnalyticsSanityApi.java b/api/src/main/java/com/ning/billing/analytics/api/sanity/AnalyticsSanityApi.java
new file mode 100644
index 0000000..adf59c4
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/analytics/api/sanity/AnalyticsSanityApi.java
@@ -0,0 +1,50 @@
+/*
+ * 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.sanity;
+
+import java.util.Collection;
+import java.util.UUID;
+
+import com.ning.billing.util.callcontext.TenantContext;
+
+public interface AnalyticsSanityApi {
+
+ /**
+ * @return the list of account ids not in sync with entitlement
+ */
+ public Collection<UUID> checkAnalyticsInSyncWithEntitlement(TenantContext context);
+
+ /**
+ * @return the list of account ids not in sync with invoice
+ */
+ public Collection<UUID> checkAnalyticsInSyncWithInvoice(TenantContext context);
+
+ /**
+ * @return the list of account ids not in sync with payment
+ */
+ public Collection<UUID> checkAnalyticsInSyncWithPayment(TenantContext context);
+
+ /**
+ * @return the list of account ids not in sync with the tag service
+ */
+ public Collection<UUID> checkAnalyticsInSyncWithTag(TenantContext context);
+
+ /**
+ * @return the list of account ids not self-consistent in Analytics
+ */
+ public Collection<UUID> checkAnalyticsConsistency(TenantContext context);
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AnalyticsSanityJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AnalyticsSanityJson.java
new file mode 100644
index 0000000..f2e4136
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AnalyticsSanityJson.java
@@ -0,0 +1,159 @@
+/*
+ * 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.Collection;
+import java.util.List;
+import java.util.UUID;
+
+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 AnalyticsSanityJson extends JsonBase {
+
+ private final List<String> checkEntitlement;
+ private final List<String> checkInvoice;
+ private final List<String> checkPayment;
+ private final List<String> checkTag;
+ private final List<String> checkConsistency;
+
+ @JsonCreator
+ public AnalyticsSanityJson(@JsonProperty("checkEntitlement") final List<String> checkEntitlement,
+ @JsonProperty("checkInvoice") final List<String> checkInvoice,
+ @JsonProperty("checkPayment") final List<String> checkPayment,
+ @JsonProperty("checkTag") final List<String> checkTag,
+ @JsonProperty("checkConsistency") final List<String> checkConsistency) {
+ this.checkEntitlement = checkEntitlement;
+ this.checkInvoice = checkInvoice;
+ this.checkPayment = checkPayment;
+ this.checkTag = checkTag;
+ this.checkConsistency = checkConsistency;
+ }
+
+ public AnalyticsSanityJson(final Collection<UUID> checkEntitlement,
+ final Collection<UUID> checkInvoice,
+ final Collection<UUID> checkPayment,
+ final Collection<UUID> checkTag,
+ final Collection<UUID> checkConsistency) {
+ this.checkEntitlement = ImmutableList.<String>copyOf(Collections2.transform(checkEntitlement, new Function<UUID, String>() {
+ @Override
+ public String apply(final UUID input) {
+ return input.toString();
+ }
+ }));
+ this.checkInvoice = ImmutableList.<String>copyOf(Collections2.transform(checkInvoice, new Function<UUID, String>() {
+ @Override
+ public String apply(final UUID input) {
+ return input.toString();
+ }
+ }));
+ this.checkPayment = ImmutableList.<String>copyOf(Collections2.transform(checkPayment, new Function<UUID, String>() {
+ @Override
+ public String apply(final UUID input) {
+ return input.toString();
+ }
+ }));
+ this.checkTag = ImmutableList.<String>copyOf(Collections2.transform(checkTag, new Function<UUID, String>() {
+ @Override
+ public String apply(final UUID input) {
+ return input.toString();
+ }
+ }));
+ this.checkConsistency = ImmutableList.<String>copyOf(Collections2.transform(checkConsistency, new Function<UUID, String>() {
+ @Override
+ public String apply(final UUID input) {
+ return input.toString();
+ }
+ }));
+ }
+
+ public List<String> getCheckEntitlement() {
+ return checkEntitlement;
+ }
+
+ public List<String> getCheckInvoice() {
+ return checkInvoice;
+ }
+
+ public List<String> getCheckPayment() {
+ return checkPayment;
+ }
+
+ public List<String> getCheckTag() {
+ return checkTag;
+ }
+
+ public List<String> getCheckConsistency() {
+ return checkConsistency;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("AnalyticsSanityJson");
+ sb.append("{checkEntitlement=").append(checkEntitlement);
+ sb.append(", checkInvoice=").append(checkInvoice);
+ sb.append(", checkPayment=").append(checkPayment);
+ sb.append(", checkTag=").append(checkTag);
+ sb.append(", checkConsistency=").append(checkConsistency);
+ 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 AnalyticsSanityJson json = (AnalyticsSanityJson) o;
+
+ if (checkConsistency != null ? !checkConsistency.equals(json.checkConsistency) : json.checkConsistency != null) {
+ return false;
+ }
+ if (checkEntitlement != null ? !checkEntitlement.equals(json.checkEntitlement) : json.checkEntitlement != null) {
+ return false;
+ }
+ if (checkInvoice != null ? !checkInvoice.equals(json.checkInvoice) : json.checkInvoice != null) {
+ return false;
+ }
+ if (checkPayment != null ? !checkPayment.equals(json.checkPayment) : json.checkPayment != null) {
+ return false;
+ }
+ if (checkTag != null ? !checkTag.equals(json.checkTag) : json.checkTag != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = checkEntitlement != null ? checkEntitlement.hashCode() : 0;
+ result = 31 * result + (checkInvoice != null ? checkInvoice.hashCode() : 0);
+ result = 31 * result + (checkPayment != null ? checkPayment.hashCode() : 0);
+ result = 31 * result + (checkTag != null ? checkTag.hashCode() : 0);
+ result = 31 * result + (checkConsistency != null ? checkConsistency.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
index 5dfe918..a258a19 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AnalyticsResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AnalyticsResource.java
@@ -16,6 +16,7 @@
package com.ning.billing.jaxrs.resources;
+import java.util.Collection;
import java.util.UUID;
import javax.inject.Inject;
@@ -35,7 +36,9 @@ import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.account.api.AccountUserApi;
import com.ning.billing.analytics.api.BusinessSnapshot;
import com.ning.billing.analytics.api.TimeSeriesData;
+import com.ning.billing.analytics.api.sanity.AnalyticsSanityApi;
import com.ning.billing.analytics.api.user.AnalyticsUserApi;
+import com.ning.billing.jaxrs.json.AnalyticsSanityJson;
import com.ning.billing.jaxrs.json.BusinessSnapshotJson;
import com.ning.billing.jaxrs.json.TimeSeriesDataJson;
import com.ning.billing.jaxrs.util.Context;
@@ -56,10 +59,12 @@ public class AnalyticsResource extends JaxRsResourceBase {
private final AccountUserApi accountUserApi;
private final AnalyticsUserApi analyticsUserApi;
+ private final AnalyticsSanityApi analyticsSanityApi;
@Inject
public AnalyticsResource(final AccountUserApi accountUserApi,
final AnalyticsUserApi analyticsUserApi,
+ final AnalyticsSanityApi analyticsSanityApi,
final JaxrsUriBuilder uriBuilder,
final TagUserApi tagUserApi,
final CustomFieldUserApi customFieldUserApi,
@@ -68,6 +73,26 @@ public class AnalyticsResource extends JaxRsResourceBase {
super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, context);
this.accountUserApi = accountUserApi;
this.analyticsUserApi = analyticsUserApi;
+ this.analyticsSanityApi = analyticsSanityApi;
+ }
+
+ @GET
+ @Path("/sanity")
+ @Produces(APPLICATION_JSON)
+ public Response checkSanity(@javax.ws.rs.core.Context final HttpServletRequest request) {
+ final TenantContext tenantContext = context.createContext(request);
+ final Collection<UUID> checkEntitlement = analyticsSanityApi.checkAnalyticsInSyncWithEntitlement(tenantContext);
+ final Collection<UUID> checkInvoice = analyticsSanityApi.checkAnalyticsInSyncWithInvoice(tenantContext);
+ final Collection<UUID> checkPayment = analyticsSanityApi.checkAnalyticsInSyncWithPayment(tenantContext);
+ final Collection<UUID> checkTag = analyticsSanityApi.checkAnalyticsInSyncWithTag(tenantContext);
+ final Collection<UUID> checkConsistency = analyticsSanityApi.checkAnalyticsConsistency(tenantContext);
+
+ final AnalyticsSanityJson json = new AnalyticsSanityJson(checkEntitlement,
+ checkInvoice,
+ checkPayment,
+ checkTag,
+ checkConsistency);
+ return Response.status(Status.OK).entity(json).build();
}
@GET